Compare commits
7 Commits
8aa698e65e
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| d509fb7813 | |||
| dcbb5a127b | |||
| e66c457b57 | |||
| 76fc14c73b | |||
| 5b4fbd5df6 | |||
| e7fd01c0af | |||
| a040478069 |
887
Cargo.lock
generated
887
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
35
flake.lock
generated
35
flake.lock
generated
@@ -3,11 +3,11 @@
|
||||
"advisory-db": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1766435619,
|
||||
"narHash": "sha256-3A5Z5K28YB45REOHMWtyQ24cEUXW76MOtbT6abPrARE=",
|
||||
"lastModified": 1768679419,
|
||||
"narHash": "sha256-l9rM4lXBeS2mIAJsJjVfl0UABx3S3zg5tul7bv+bn50=",
|
||||
"owner": "rustsec",
|
||||
"repo": "advisory-db",
|
||||
"rev": "a98dbc80b16730a64c612c6ab5d5fecb4ebb79ba",
|
||||
"rev": "c700e1cd023ca87343cbd9217d50d47023e9adc7",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -18,11 +18,11 @@
|
||||
},
|
||||
"crane": {
|
||||
"locked": {
|
||||
"lastModified": 1766194365,
|
||||
"narHash": "sha256-4AFsUZ0kl6MXSm4BaQgItD0VGlEKR3iq7gIaL7TjBvc=",
|
||||
"lastModified": 1768873933,
|
||||
"narHash": "sha256-CfyzdaeLNGkyAHp3kT5vjvXhA1pVVK7nyDziYxCPsNk=",
|
||||
"owner": "ipetkov",
|
||||
"repo": "crane",
|
||||
"rev": "7d8ec2c71771937ab99790b45e6d9b93d15d9379",
|
||||
"rev": "0bda7e7d005ccb5522a76d11ccfbf562b71953ca",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -34,10 +34,10 @@
|
||||
"crates-io-index": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1763363725,
|
||||
"narHash": "sha256-cxr5xIKZFP45yV1ZHFTB1sHo5YGiR3FA8D9vAfDizMo=",
|
||||
"lastModified": 1769614137,
|
||||
"narHash": "sha256-3Td8fiv6iFVxeS0hYq3xdd10ZvUkC9INMAiQx/mECas=",
|
||||
"ref": "refs/heads/master",
|
||||
"rev": "0382002e816a4cbd17d8d5b172f08b848aa22ff6",
|
||||
"rev": "c7e7d6394bc95555d6acd5c6783855f47d64c90d",
|
||||
"shallow": true,
|
||||
"type": "git",
|
||||
"url": "https://github.com/rust-lang/crates.io-index"
|
||||
@@ -50,7 +50,9 @@
|
||||
},
|
||||
"crates-nix": {
|
||||
"inputs": {
|
||||
"crates-io-index": "crates-io-index"
|
||||
"crates-io-index": [
|
||||
"crates-io-index"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1763364255,
|
||||
@@ -106,11 +108,11 @@
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1766309749,
|
||||
"narHash": "sha256-3xY8CZ4rSnQ0NqGhMKAy5vgC+2IVK0NoVEzDoOh4DA4=",
|
||||
"lastModified": 1768564909,
|
||||
"narHash": "sha256-Kell/SpJYVkHWMvnhqJz/8DqQg2b6PguxVWOuadbHCc=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "a6531044f6d0bef691ea18d4d4ce44d0daa6e816",
|
||||
"rev": "e4bae1bd10c9c57b2cf517953ab70060a828ee6f",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -124,6 +126,7 @@
|
||||
"inputs": {
|
||||
"advisory-db": "advisory-db",
|
||||
"crane": "crane",
|
||||
"crates-io-index": "crates-io-index",
|
||||
"crates-nix": "crates-nix",
|
||||
"flake-utils": "flake-utils",
|
||||
"nix-github-actions": "nix-github-actions",
|
||||
@@ -138,11 +141,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1766371695,
|
||||
"narHash": "sha256-W7CX9vy7H2Jj3E8NI4djHyF8iHSxKpb2c/7uNQ/vGFU=",
|
||||
"lastModified": 1768877311,
|
||||
"narHash": "sha256-abSDl0cNr0B+YCsIDpO1SjXD9JMxE4s8EFnhLEFVovI=",
|
||||
"owner": "oxalica",
|
||||
"repo": "rust-overlay",
|
||||
"rev": "d81285ba8199b00dc31847258cae3c655b605e8c",
|
||||
"rev": "59e4ab96304585fde3890025fd59bd2717985cc1",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
||||
39
flake.nix
39
flake.nix
@@ -9,7 +9,14 @@
|
||||
url = "github:nix-community/nix-github-actions";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
crates-nix.url = "github:uttarayan21/crates.nix";
|
||||
crates-io-index = {
|
||||
url = "git+https://github.com/rust-lang/crates.io-index?shallow=1";
|
||||
flake = false;
|
||||
};
|
||||
crates-nix = {
|
||||
url = "github:uttarayan21/crates.nix";
|
||||
inputs.crates-io-index.follows = "crates-io-index";
|
||||
};
|
||||
rust-overlay = {
|
||||
url = "github:oxalica/rust-overlay";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
@@ -179,28 +186,38 @@
|
||||
devShells = rec {
|
||||
rust-shell =
|
||||
pkgs.mkShell.override {
|
||||
stdenv =
|
||||
if pkgs.stdenv.isLinux
|
||||
then (pkgs.stdenvAdapters.useMoldLinker pkgs.clangStdenv)
|
||||
else pkgs.clangStdenv;
|
||||
} (commonArgs
|
||||
stdenv = pkgs.clangStdenv;
|
||||
# if pkgs.stdenv.isLinux
|
||||
# then (pkgs.stdenvAdapters.useMoldLinker pkgs.clangStdenv)
|
||||
# else pkgs.clangStdenv;
|
||||
}
|
||||
(commonArgs
|
||||
// {
|
||||
# GST_PLUGIN_PATH = "/run/current-system/sw/lib/gstreamer-1.0/";
|
||||
GIO_EXTRA_MODULES = "${pkgs.glib-networking}/lib/gio/modules";
|
||||
packages = with pkgs;
|
||||
[
|
||||
toolchainWithRustAnalyzer
|
||||
cargo-nextest
|
||||
bacon
|
||||
cargo-audit
|
||||
cargo-deny
|
||||
cargo-expand
|
||||
bacon
|
||||
cargo-make
|
||||
cargo-hack
|
||||
cargo-make
|
||||
cargo-nextest
|
||||
cargo-outdated
|
||||
lld
|
||||
lldb
|
||||
cargo-audit
|
||||
(crates.buildCrate "cargo-with" {doCheck = false;})
|
||||
(crates.buildCrate "dioxus-cli" {
|
||||
nativeBuildInputs = with pkgs; [pkg-config];
|
||||
buildInputs = [openssl];
|
||||
doCheck = false;
|
||||
})
|
||||
(crates.buildCrate "cargo-hot" {
|
||||
nativeBuildInputs = with pkgs; [pkg-config];
|
||||
buildInputs = [openssl];
|
||||
})
|
||||
]
|
||||
++ (lib.optionals pkgs.stdenv.isDarwin [
|
||||
apple-sdk_26
|
||||
@@ -211,7 +228,7 @@
|
||||
samply
|
||||
cargo-flamegraph
|
||||
perf
|
||||
mold
|
||||
# mold
|
||||
]);
|
||||
});
|
||||
default = rust-shell;
|
||||
|
||||
@@ -8,6 +8,7 @@ bson = { version = "3.1.0", features = ["serde"] }
|
||||
futures = "0.3.31"
|
||||
parking_lot = "0.12.5"
|
||||
redb = { version = "3.1.0", features = ["uuid"] }
|
||||
secrecy = "0.10.3"
|
||||
serde = "1.0.228"
|
||||
tokio = { version = "1.48.0", features = ["rt"] }
|
||||
uuid = "1.18.1"
|
||||
uuid = { version = "1.18.1", features = ["v4"] }
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
pub mod redb;
|
||||
pub mod sqlite;
|
||||
pub mod toml;
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
pub trait Store {
|
||||
fn image(&self, id: &str) -> Option<Vec<u8>>;
|
||||
fn save_image(&mut self, id: &str, data: &[u8]);
|
||||
use uuid::Uuid;
|
||||
|
||||
pub struct ApiKey {
|
||||
inner: secrecy::SecretBox<String>,
|
||||
}
|
||||
pub struct SecretStore {
|
||||
api_keys: BTreeMap<Uuid, ApiKey>,
|
||||
}
|
||||
|
||||
pub struct Settings {}
|
||||
|
||||
@@ -1,225 +1,225 @@
|
||||
use std::{
|
||||
borrow::Borrow,
|
||||
collections::VecDeque,
|
||||
marker::PhantomData,
|
||||
path::Path,
|
||||
sync::{Arc, RwLock, atomic::AtomicBool},
|
||||
};
|
||||
|
||||
use futures::task::AtomicWaker;
|
||||
use redb::{Error, Key, ReadableDatabase, TableDefinition, Value};
|
||||
use serde::{Serialize, de::DeserializeOwned};
|
||||
|
||||
const USERS: TableDefinition<uuid::Uuid, Vec<u8>> = TableDefinition::new("users");
|
||||
const SERVERS: TableDefinition<uuid::Uuid, Vec<u8>> = TableDefinition::new("servers");
|
||||
const SETTINGS: TableDefinition<uuid::Uuid, Vec<u8>> = TableDefinition::new("settings");
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct TableInner<T> {
|
||||
db: Arc<T>,
|
||||
}
|
||||
|
||||
impl<T> Clone for TableInner<T> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
db: Arc::clone(&self.db),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> TableInner<T> {
|
||||
fn new(db: Arc<T>) -> Self {
|
||||
Self { db }
|
||||
}
|
||||
}
|
||||
|
||||
impl TableInner<DatabaseHandle> {
|
||||
async fn get<'a, K: Key, V: Serialize + DeserializeOwned>(
|
||||
&self,
|
||||
table: TableDefinition<'static, K, Vec<u8>>,
|
||||
key: impl Borrow<K::SelfType<'a>>,
|
||||
) -> Result<Option<V>> {
|
||||
let db: &redb::Database = &self.db.as_ref().database;
|
||||
let db_reader = db.begin_read()?;
|
||||
let table = db_reader.open_table(table)?;
|
||||
table
|
||||
.get(key)?
|
||||
.map(|value| bson::deserialize_from_slice(&value.value()))
|
||||
.transpose()
|
||||
.map_err(|e| redb::Error::Io(std::io::Error::other(e)))
|
||||
}
|
||||
|
||||
async fn insert<
|
||||
'a,
|
||||
'b,
|
||||
K: Key + Send + Sync,
|
||||
V: Serialize + DeserializeOwned + Send + Sync + 'a,
|
||||
>(
|
||||
&'b self,
|
||||
table: TableDefinition<'static, K, Vec<u8>>,
|
||||
key: impl Borrow<K::SelfType<'a>> + Send + 'b,
|
||||
value: V,
|
||||
) -> Result<Option<V>> {
|
||||
let db: &redb::Database = &self.db.as_ref().database;
|
||||
// self.db
|
||||
// .writing
|
||||
// .store(true, std::sync::atomic::Ordering::SeqCst);
|
||||
|
||||
// let out = tokio::task::spawn_blocking(move || -> Result<Option<V>>
|
||||
|
||||
let out = tokio::task::spawn_blocking(|| -> Result<Option<V>> {
|
||||
let db_writer = db.begin_write()?;
|
||||
let out = {
|
||||
let mut table = db_writer.open_table(table)?;
|
||||
let serialized_value = bson::serialize_to_vec(&value)
|
||||
.map_err(|e| redb::Error::Io(std::io::Error::other(e)))?;
|
||||
let previous = table.insert(key, &serialized_value)?;
|
||||
let out = previous
|
||||
.map(|value| bson::deserialize_from_slice(&value.value()))
|
||||
.transpose()
|
||||
.map_err(|e| redb::Error::Io(std::io::Error::other(e)));
|
||||
out
|
||||
};
|
||||
db_writer.commit()?;
|
||||
out
|
||||
})
|
||||
.await
|
||||
.expect("Task panicked");
|
||||
|
||||
out
|
||||
}
|
||||
}
|
||||
|
||||
// impl<K: Key, V: Serialize + DeserializeOwned> Table<K, V> for TableInner {
|
||||
// async fn get(&self, key: K) -> Result<Option<Value>> {}
|
||||
// async fn insert(&self, key: K, value: V) -> Result<Option<Value>> {}
|
||||
// async fn modify(&self, key: K, v: FnOnce(V) -> V) -> Result<bool> {}
|
||||
// async fn remove(&self, key: K) -> Result<Option<Value>> {}
|
||||
// }
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Users<T>(TableInner<T>);
|
||||
|
||||
impl<T> Clone for Users<T> {
|
||||
fn clone(&self) -> Self {
|
||||
Self(self.0.clone())
|
||||
}
|
||||
}
|
||||
impl<T> Users<T> {
|
||||
const TABLE: TableDefinition<'static, uuid::Uuid, Vec<u8>> = USERS;
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Servers<T>(TableInner<T>);
|
||||
impl<T> Clone for Servers<T> {
|
||||
fn clone(&self) -> Self {
|
||||
Self(self.0.clone())
|
||||
}
|
||||
}
|
||||
impl<T> Servers<T> {
|
||||
const TABLE: TableDefinition<'static, uuid::Uuid, Vec<u8>> = SERVERS;
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Settings<T>(TableInner<T>);
|
||||
impl<T> Clone for Settings<T> {
|
||||
fn clone(&self) -> Self {
|
||||
Self(self.0.clone())
|
||||
}
|
||||
}
|
||||
impl<T> Settings<T> {
|
||||
const TABLE: TableDefinition<'static, uuid::Uuid, Vec<u8>> = SETTINGS;
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Database {
|
||||
users: Users<DatabaseHandle>,
|
||||
servers: Servers<DatabaseHandle>,
|
||||
settings: Settings<DatabaseHandle>,
|
||||
handle: Arc<DatabaseHandle>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DatabaseHandle {
|
||||
database: redb::Database,
|
||||
writing: AtomicBool,
|
||||
wakers: RwLock<VecDeque<AtomicWaker>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DatabaseWriterGuard<'a> {
|
||||
handle: &'a DatabaseHandle,
|
||||
dropper: Arc<AtomicBool>,
|
||||
}
|
||||
|
||||
// impl Drop for DatabaseWriterGuard<'_> {
|
||||
// fn drop(&mut self) {
|
||||
// self.handle
|
||||
// .writing
|
||||
// .store(false, std::sync::atomic::Ordering::SeqCst);
|
||||
// let is_panicking = std::thread::panicking();
|
||||
// let Ok(writer) = self.handle.wakers.write() else {
|
||||
// if is_panicking {
|
||||
// return;
|
||||
// } else {
|
||||
// panic!("Wakers lock poisoned");
|
||||
// }
|
||||
// }
|
||||
// if let Some(waker) = (self.handle.wakers.write()).pop() {
|
||||
// waker.wake();
|
||||
// use std::{
|
||||
// borrow::Borrow,
|
||||
// collections::VecDeque,
|
||||
// marker::PhantomData,
|
||||
// path::Path,
|
||||
// sync::{Arc, RwLock, atomic::AtomicBool},
|
||||
// };
|
||||
// // let mut wakers = self.handle.wakers.write().expect();
|
||||
// // if let Some(waker) = self.handle.wakers.write().expect("Wakers lock poisoned").pop_front() {
|
||||
// // waker.wake();
|
||||
// // }
|
||||
// // while let Some(waker) = wakers.pop_front() {
|
||||
// // waker.wake();
|
||||
// // }
|
||||
//
|
||||
// use futures::task::AtomicWaker;
|
||||
// use redb::{Error, Key, ReadableDatabase, TableDefinition, Value};
|
||||
// use serde::{Serialize, de::DeserializeOwned};
|
||||
//
|
||||
// const USERS: TableDefinition<uuid::Uuid, Vec<u8>> = TableDefinition::new("users");
|
||||
// const SERVERS: TableDefinition<uuid::Uuid, Vec<u8>> = TableDefinition::new("servers");
|
||||
// const SETTINGS: TableDefinition<uuid::Uuid, Vec<u8>> = TableDefinition::new("settings");
|
||||
//
|
||||
// #[derive(Debug)]
|
||||
// pub struct TableInner<T> {
|
||||
// db: Arc<T>,
|
||||
// }
|
||||
//
|
||||
// impl<T> Clone for TableInner<T> {
|
||||
// fn clone(&self) -> Self {
|
||||
// Self {
|
||||
// db: Arc::clone(&self.db),
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// impl<T> TableInner<T> {
|
||||
// fn new(db: Arc<T>) -> Self {
|
||||
// Self { db }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// impl TableInner<DatabaseHandle> {
|
||||
// async fn get<'a, K: Key, V: Serialize + DeserializeOwned>(
|
||||
// &self,
|
||||
// table: TableDefinition<'static, K, Vec<u8>>,
|
||||
// key: impl Borrow<K::SelfType<'a>>,
|
||||
// ) -> Result<Option<V>> {
|
||||
// let db: &redb::Database = &self.db.as_ref().database;
|
||||
// let db_reader = db.begin_read()?;
|
||||
// let table = db_reader.open_table(table)?;
|
||||
// table
|
||||
// .get(key)?
|
||||
// .map(|value| bson::deserialize_from_slice(&value.value()))
|
||||
// .transpose()
|
||||
// .map_err(|e| redb::Error::Io(std::io::Error::other(e)))
|
||||
// }
|
||||
//
|
||||
// async fn insert<
|
||||
// 'a,
|
||||
// 'b,
|
||||
// K: Key + Send + Sync,
|
||||
// V: Serialize + DeserializeOwned + Send + Sync + 'a,
|
||||
// >(
|
||||
// &'b self,
|
||||
// table: TableDefinition<'static, K, Vec<u8>>,
|
||||
// key: impl Borrow<K::SelfType<'a>> + Send + 'b,
|
||||
// value: V,
|
||||
// ) -> Result<Option<V>> {
|
||||
// let db: &redb::Database = &self.db.as_ref().database;
|
||||
// // self.db
|
||||
// // .writing
|
||||
// // .store(true, std::sync::atomic::Ordering::SeqCst);
|
||||
//
|
||||
// // let out = tokio::task::spawn_blocking(move || -> Result<Option<V>>
|
||||
//
|
||||
// let out = tokio::task::spawn_blocking(|| -> Result<Option<V>> {
|
||||
// let db_writer = db.begin_write()?;
|
||||
// let out = {
|
||||
// let mut table = db_writer.open_table(table)?;
|
||||
// let serialized_value = bson::serialize_to_vec(&value)
|
||||
// .map_err(|e| redb::Error::Io(std::io::Error::other(e)))?;
|
||||
// let previous = table.insert(key, &serialized_value)?;
|
||||
// let out = previous
|
||||
// .map(|value| bson::deserialize_from_slice(&value.value()))
|
||||
// .transpose()
|
||||
// .map_err(|e| redb::Error::Io(std::io::Error::other(e)));
|
||||
// out
|
||||
// };
|
||||
// db_writer.commit()?;
|
||||
// out
|
||||
// })
|
||||
// .await
|
||||
// .expect("Task panicked");
|
||||
//
|
||||
// out
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // impl<K: Key, V: Serialize + DeserializeOwned> Table<K, V> for TableInner {
|
||||
// // async fn get(&self, key: K) -> Result<Option<Value>> {}
|
||||
// // async fn insert(&self, key: K, value: V) -> Result<Option<Value>> {}
|
||||
// // async fn modify(&self, key: K, v: FnOnce(V) -> V) -> Result<bool> {}
|
||||
// // async fn remove(&self, key: K) -> Result<Option<Value>> {}
|
||||
// // }
|
||||
//
|
||||
// #[derive(Debug)]
|
||||
// pub struct Users<T>(TableInner<T>);
|
||||
//
|
||||
// impl<T> Clone for Users<T> {
|
||||
// fn clone(&self) -> Self {
|
||||
// Self(self.0.clone())
|
||||
// }
|
||||
// }
|
||||
// impl<T> Users<T> {
|
||||
// const TABLE: TableDefinition<'static, uuid::Uuid, Vec<u8>> = USERS;
|
||||
// }
|
||||
//
|
||||
// #[derive(Debug)]
|
||||
// pub struct Servers<T>(TableInner<T>);
|
||||
// impl<T> Clone for Servers<T> {
|
||||
// fn clone(&self) -> Self {
|
||||
// Self(self.0.clone())
|
||||
// }
|
||||
// }
|
||||
// impl<T> Servers<T> {
|
||||
// const TABLE: TableDefinition<'static, uuid::Uuid, Vec<u8>> = SERVERS;
|
||||
// }
|
||||
//
|
||||
// #[derive(Debug)]
|
||||
// pub struct Settings<T>(TableInner<T>);
|
||||
// impl<T> Clone for Settings<T> {
|
||||
// fn clone(&self) -> Self {
|
||||
// Self(self.0.clone())
|
||||
// }
|
||||
// }
|
||||
// impl<T> Settings<T> {
|
||||
// const TABLE: TableDefinition<'static, uuid::Uuid, Vec<u8>> = SETTINGS;
|
||||
// }
|
||||
//
|
||||
// #[derive(Debug, Clone)]
|
||||
// pub struct Database {
|
||||
// users: Users<DatabaseHandle>,
|
||||
// servers: Servers<DatabaseHandle>,
|
||||
// settings: Settings<DatabaseHandle>,
|
||||
// handle: Arc<DatabaseHandle>,
|
||||
// }
|
||||
//
|
||||
// #[derive(Debug)]
|
||||
// pub struct DatabaseHandle {
|
||||
// database: redb::Database,
|
||||
// writing: AtomicBool,
|
||||
// wakers: RwLock<VecDeque<AtomicWaker>>,
|
||||
// }
|
||||
//
|
||||
// #[derive(Debug)]
|
||||
// pub struct DatabaseWriterGuard<'a> {
|
||||
// handle: &'a DatabaseHandle,
|
||||
// dropper: Arc<AtomicBool>,
|
||||
// }
|
||||
//
|
||||
// // impl Drop for DatabaseWriterGuard<'_> {
|
||||
// // fn drop(&mut self) {
|
||||
// // self.handle
|
||||
// // .writing
|
||||
// // .store(false, std::sync::atomic::Ordering::SeqCst);
|
||||
// // let is_panicking = std::thread::panicking();
|
||||
// // let Ok(writer) = self.handle.wakers.write() else {
|
||||
// // if is_panicking {
|
||||
// // return;
|
||||
// // } else {
|
||||
// // panic!("Wakers lock poisoned");
|
||||
// // }
|
||||
// // }
|
||||
// // if let Some(waker) = (self.handle.wakers.write()).pop() {
|
||||
// // waker.wake();
|
||||
// // };
|
||||
// // // let mut wakers = self.handle.wakers.write().expect();
|
||||
// // // if let Some(waker) = self.handle.wakers.write().expect("Wakers lock poisoned").pop_front() {
|
||||
// // // waker.wake();
|
||||
// // // }
|
||||
// // // while let Some(waker) = wakers.pop_front() {
|
||||
// // // waker.wake();
|
||||
// // // }
|
||||
// // }
|
||||
// // }
|
||||
//
|
||||
// type Result<O, E = redb::Error> = core::result::Result<O, E>;
|
||||
//
|
||||
// pub trait Table<K: Key> {
|
||||
// fn insert<V: Serialize + DeserializeOwned>(
|
||||
// &self,
|
||||
// key: K,
|
||||
// value: V,
|
||||
// ) -> impl Future<Output = Result<Option<V>>> + Send;
|
||||
// fn modify<V: Serialize + DeserializeOwned, O: Serialize + DeserializeOwned>(
|
||||
// &self,
|
||||
// key: K,
|
||||
// v: impl FnOnce(V) -> O,
|
||||
// ) -> impl Future<Output = Result<bool>> + Send;
|
||||
// fn remove<V: Serialize + DeserializeOwned>(
|
||||
// &self,
|
||||
// key: K,
|
||||
// ) -> impl Future<Output = Result<Option<V>>> + Send;
|
||||
// fn get<V: Serialize + DeserializeOwned>(
|
||||
// &self,
|
||||
// key: K,
|
||||
// ) -> impl Future<Output = Result<Option<V>>> + Send;
|
||||
// }
|
||||
//
|
||||
// impl Database {
|
||||
// pub fn create(path: impl AsRef<Path>) -> Result<Self, Error> {
|
||||
// let writing = AtomicBool::new(false);
|
||||
// let wakers = RwLock::new(VecDeque::new());
|
||||
// let db = redb::Database::create(path)?;
|
||||
// let db = Arc::new(DatabaseHandle {
|
||||
// database: db,
|
||||
// writing,
|
||||
// wakers,
|
||||
// });
|
||||
// let table_inner = TableInner::new(Arc::clone(&db));
|
||||
// let users = Users(table_inner.clone());
|
||||
// let servers = Servers(table_inner.clone());
|
||||
// let settings = Settings(table_inner.clone());
|
||||
// Ok(Self {
|
||||
// servers,
|
||||
// users,
|
||||
// settings,
|
||||
// handle: db,
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
|
||||
type Result<O, E = redb::Error> = core::result::Result<O, E>;
|
||||
|
||||
pub trait Table<K: Key> {
|
||||
fn insert<V: Serialize + DeserializeOwned>(
|
||||
&self,
|
||||
key: K,
|
||||
value: V,
|
||||
) -> impl Future<Output = Result<Option<V>>> + Send;
|
||||
fn modify<V: Serialize + DeserializeOwned, O: Serialize + DeserializeOwned>(
|
||||
&self,
|
||||
key: K,
|
||||
v: impl FnOnce(V) -> O,
|
||||
) -> impl Future<Output = Result<bool>> + Send;
|
||||
fn remove<V: Serialize + DeserializeOwned>(
|
||||
&self,
|
||||
key: K,
|
||||
) -> impl Future<Output = Result<Option<V>>> + Send;
|
||||
fn get<V: Serialize + DeserializeOwned>(
|
||||
&self,
|
||||
key: K,
|
||||
) -> impl Future<Output = Result<Option<V>>> + Send;
|
||||
}
|
||||
|
||||
impl Database {
|
||||
pub fn create(path: impl AsRef<Path>) -> Result<Self, Error> {
|
||||
let writing = AtomicBool::new(false);
|
||||
let wakers = RwLock::new(VecDeque::new());
|
||||
let db = redb::Database::create(path)?;
|
||||
let db = Arc::new(DatabaseHandle {
|
||||
database: db,
|
||||
writing,
|
||||
wakers,
|
||||
});
|
||||
let table_inner = TableInner::new(Arc::clone(&db));
|
||||
let users = Users(table_inner.clone());
|
||||
let servers = Servers(table_inner.clone());
|
||||
let settings = Settings(table_inner.clone());
|
||||
Ok(Self {
|
||||
servers,
|
||||
users,
|
||||
settings,
|
||||
handle: db,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,9 +21,10 @@ iced = { workspace = true, features = [
|
||||
|
||||
|
||||
iced-video = { workspace = true }
|
||||
iced_aw = "0.13.0"
|
||||
iced_wgpu = "0.14.0"
|
||||
iced_winit = "0.14.0"
|
||||
reqwest = "0.12.24"
|
||||
reqwest = "0.13"
|
||||
tap = "1.0.1"
|
||||
toml = "0.9.8"
|
||||
tracing = "0.1.41"
|
||||
|
||||
@@ -4,6 +4,7 @@ mod video;
|
||||
mod shared_string;
|
||||
use iced_video::{Ready, Video, VideoHandle};
|
||||
use shared_string::SharedString;
|
||||
use tap::Pipe as _;
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
@@ -25,6 +26,8 @@ pub struct ItemCache {
|
||||
pub tree: BTreeMap<Option<uuid::Uuid>, BTreeSet<uuid::Uuid>>,
|
||||
}
|
||||
|
||||
const BACKGROUND_COLOR: iced::Color = iced::Color::from_rgba8(30, 30, 30, 0.7);
|
||||
|
||||
impl ItemCache {
|
||||
pub fn insert(&mut self, parent: impl Into<Option<uuid::Uuid>>, item: Item) {
|
||||
let parent = parent.into();
|
||||
@@ -155,8 +158,6 @@ impl State {
|
||||
query: None,
|
||||
screen: Screen::Home,
|
||||
settings: settings::SettingsState::default(),
|
||||
// username_input: String::new(),
|
||||
// password_input: String::new(),
|
||||
is_authenticated: false,
|
||||
video: None,
|
||||
}
|
||||
@@ -172,17 +173,8 @@ pub enum Message {
|
||||
OpenItem(Option<uuid::Uuid>),
|
||||
LoadedItem(Option<uuid::Uuid>, Vec<Item>),
|
||||
Error(String),
|
||||
SetToken(String),
|
||||
Back,
|
||||
Home,
|
||||
// Login {
|
||||
// username: String,
|
||||
// password: String,
|
||||
// config: api::JellyfinConfig,
|
||||
// },
|
||||
// LoginSuccess(String),
|
||||
// LoadedClient(api::JellyfinClient, bool),
|
||||
// Logout,
|
||||
Video(video::VideoMessage),
|
||||
}
|
||||
|
||||
@@ -249,15 +241,6 @@ fn update(state: &mut State, message: Message) -> Task<Message> {
|
||||
state.messages.push(err);
|
||||
Task::none()
|
||||
}
|
||||
Message::SetToken(token) => {
|
||||
tracing::info!("Authenticated with token: {}", token);
|
||||
state
|
||||
.jellyfin_client
|
||||
.as_mut()
|
||||
.map(|mut client| client.set_token(token));
|
||||
state.is_authenticated = true;
|
||||
Task::none()
|
||||
}
|
||||
Message::Back => {
|
||||
state.current = state.history.pop().unwrap_or(None);
|
||||
Task::none()
|
||||
@@ -268,7 +251,6 @@ fn update(state: &mut State, message: Message) -> Task<Message> {
|
||||
}
|
||||
Message::SearchQueryChanged(query) => {
|
||||
state.query = Some(query);
|
||||
// Handle search query change
|
||||
Task::none()
|
||||
}
|
||||
Message::Search => {
|
||||
@@ -293,9 +275,29 @@ fn update(state: &mut State, message: Message) -> Task<Message> {
|
||||
}
|
||||
|
||||
fn view(state: &State) -> Element<'_, Message> {
|
||||
let content = home(state);
|
||||
match state.screen {
|
||||
Screen::Settings => settings::settings(state),
|
||||
Screen::Home | _ => home(state),
|
||||
Screen::Settings => {
|
||||
let settings = settings::settings(state);
|
||||
let settings = container(settings)
|
||||
.width(Length::FillPortion(4))
|
||||
.height(Length::FillPortion(4))
|
||||
.style(container::rounded_box)
|
||||
.pipe(mouse_area)
|
||||
.on_press(Message::Refresh)
|
||||
.pipe(|c| iced::widget::column![space::vertical(), c, space::vertical()])
|
||||
.pipe(container)
|
||||
.width(Length::Fill)
|
||||
.width(Length::Fill)
|
||||
.align_y(Alignment::Center)
|
||||
.align_x(Alignment::Center)
|
||||
.style(|_| container::background(BACKGROUND_COLOR))
|
||||
.padding(50)
|
||||
.pipe(mouse_area)
|
||||
.on_press(Message::Settings(settings::SettingsMessage::Close));
|
||||
stack![content, settings].into()
|
||||
}
|
||||
Screen::Home | _ => content,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -310,18 +312,16 @@ fn body(state: &State) -> Element<'_, Message> {
|
||||
if let Some(ref video) = state.video {
|
||||
video::player(video)
|
||||
} else {
|
||||
scrollable(
|
||||
container(
|
||||
Grid::with_children(state.cache.items_of(state.current).into_iter().map(card))
|
||||
.fluid(400)
|
||||
.spacing(50),
|
||||
)
|
||||
.spacing(50)
|
||||
.pipe(container)
|
||||
.padding(50)
|
||||
.align_x(Alignment::Center)
|
||||
// .align_y(Alignment::Center)
|
||||
.height(Length::Fill)
|
||||
.width(Length::Fill),
|
||||
)
|
||||
.width(Length::Fill)
|
||||
.pipe(scrollable)
|
||||
.height(Length::Fill)
|
||||
.into()
|
||||
}
|
||||
@@ -329,19 +329,17 @@ fn body(state: &State) -> Element<'_, Message> {
|
||||
|
||||
fn header(state: &State) -> Element<'_, Message> {
|
||||
row([
|
||||
container(
|
||||
Button::new(
|
||||
Text::new(
|
||||
text(
|
||||
state
|
||||
.jellyfin_client
|
||||
.as_ref()
|
||||
.map(|c| c.config.server_url.as_str())
|
||||
.unwrap_or("No Server"),
|
||||
)
|
||||
.align_x(Alignment::Start),
|
||||
)
|
||||
.on_press(Message::Home),
|
||||
)
|
||||
.align_x(Alignment::Start)
|
||||
.pipe(button)
|
||||
.on_press(Message::Home)
|
||||
.pipe(container)
|
||||
.padding(10)
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill)
|
||||
@@ -350,7 +348,6 @@ fn header(state: &State) -> Element<'_, Message> {
|
||||
.style(container::rounded_box)
|
||||
.into(),
|
||||
search(state),
|
||||
container(
|
||||
row([
|
||||
button("Refresh").on_press(Message::Refresh).into(),
|
||||
button("Settings")
|
||||
@@ -360,8 +357,8 @@ fn header(state: &State) -> Element<'_, Message> {
|
||||
.on_press(Message::Video(video::VideoMessage::Test))
|
||||
.into(),
|
||||
])
|
||||
.spacing(10),
|
||||
)
|
||||
.spacing(10)
|
||||
.pipe(container)
|
||||
.padding(10)
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill)
|
||||
@@ -377,14 +374,13 @@ fn header(state: &State) -> Element<'_, Message> {
|
||||
}
|
||||
|
||||
fn search(state: &State) -> Element<'_, Message> {
|
||||
container(
|
||||
TextInput::new("Search...", state.query.as_deref().unwrap_or_default())
|
||||
.padding(10)
|
||||
.size(16)
|
||||
.width(Length::Fill)
|
||||
.on_input(Message::SearchQueryChanged)
|
||||
.on_submit(Message::Search),
|
||||
)
|
||||
.on_submit(Message::Search)
|
||||
.pipe(container)
|
||||
.padding(10)
|
||||
.width(Length::Fill)
|
||||
.height(Length::Shrink)
|
||||
|
||||
@@ -66,7 +66,7 @@ pub enum ServerMessage {
|
||||
Clear,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
#[derive(Debug, Clone, Default, PartialEq, Eq)]
|
||||
pub enum SettingsScreen {
|
||||
#[default]
|
||||
Main,
|
||||
@@ -195,9 +195,28 @@ impl ServerForm {
|
||||
}
|
||||
|
||||
mod screens {
|
||||
use iced_aw::Tabs;
|
||||
|
||||
use super::*;
|
||||
pub fn settings(state: &State) -> Element<'_, Message> {
|
||||
row([settings_list(state), settings_screen(state)]).into()
|
||||
Tabs::new(|f| Message::Settings(SettingsMessage::Select(f)))
|
||||
.push(
|
||||
SettingsScreen::Main,
|
||||
iced_aw::TabLabel::Text("General".into()),
|
||||
main(state),
|
||||
)
|
||||
.push(
|
||||
SettingsScreen::Servers,
|
||||
iced_aw::TabLabel::Text("Servers".into()),
|
||||
server(state),
|
||||
)
|
||||
.push(
|
||||
SettingsScreen::Users,
|
||||
iced_aw::TabLabel::Text("Users".into()),
|
||||
user(state),
|
||||
)
|
||||
.set_active_tab(&state.settings.screen)
|
||||
.into()
|
||||
}
|
||||
|
||||
pub fn settings_screen(state: &State) -> Element<'_, Message> {
|
||||
@@ -207,63 +226,64 @@ mod screens {
|
||||
SettingsScreen::Users => user(state),
|
||||
})
|
||||
.width(Length::FillPortion(10))
|
||||
.height(Length::Fill)
|
||||
.style(|theme| container::background(theme.extended_palette().background.base.color))
|
||||
.pipe(container)
|
||||
.padding(10)
|
||||
.style(|theme| container::background(theme.extended_palette().secondary.base.color))
|
||||
.width(Length::FillPortion(10))
|
||||
.into()
|
||||
}
|
||||
|
||||
pub fn settings_list(state: &State) -> Element<'_, Message> {
|
||||
scrollable(
|
||||
column(
|
||||
[
|
||||
button(center_text("Main")).on_press(Message::Settings(
|
||||
button(center_text("General")).on_press(Message::Settings(
|
||||
SettingsMessage::Select(SettingsScreen::Main),
|
||||
)),
|
||||
button(center_text("Servers")).on_press(Message::Settings(
|
||||
SettingsMessage::Select(SettingsScreen::Servers),
|
||||
)),
|
||||
button(center_text("Users")).on_press(Message::Settings(
|
||||
SettingsMessage::Select(SettingsScreen::Users),
|
||||
)),
|
||||
button(center_text("Users")).on_press(Message::Settings(SettingsMessage::Select(
|
||||
SettingsScreen::Users,
|
||||
))),
|
||||
]
|
||||
.map(|p| p.clip(true).width(Length::Fill).into()),
|
||||
)
|
||||
.width(Length::FillPortion(2))
|
||||
.spacing(10)
|
||||
.padding(10),
|
||||
)
|
||||
.padding(10)
|
||||
.pipe(scrollable)
|
||||
.into()
|
||||
}
|
||||
|
||||
pub fn main(state: &State) -> Element<'_, Message> {
|
||||
// placeholder for now
|
||||
container(
|
||||
Column::new()
|
||||
.push(text("Main Settings"))
|
||||
.push(toggler(true).label("Foobar"))
|
||||
.push(toggler(true).label("HDR"))
|
||||
.spacing(20)
|
||||
.padding(20),
|
||||
)
|
||||
.padding(20)
|
||||
.pipe(container)
|
||||
.into()
|
||||
}
|
||||
|
||||
pub fn server(state: &State) -> Element<'_, Message> {
|
||||
container(
|
||||
Column::new()
|
||||
.push(text("Server Settings"))
|
||||
.push(state.settings.server_form.view())
|
||||
// .push(toggler(false).label("Enable Server"))
|
||||
.spacing(20)
|
||||
.padding(20),
|
||||
)
|
||||
.padding(20)
|
||||
.pipe(container)
|
||||
.into()
|
||||
}
|
||||
|
||||
pub fn user(state: &State) -> Element<'_, Message> {
|
||||
container(
|
||||
Column::new()
|
||||
.push(text("User Settings"))
|
||||
.push(state.settings.login_form.view())
|
||||
// .push(userlist(&state))
|
||||
.spacing(20)
|
||||
.padding(20),
|
||||
)
|
||||
.padding(20)
|
||||
.pipe(container)
|
||||
.into()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user