Compare commits
2 Commits
1912ef8974
...
0738fc27ba
| Author | SHA1 | Date | |
|---|---|---|---|
| 0738fc27ba | |||
| a0dce86425 |
199
AGENTS.md
Normal file
199
AGENTS.md
Normal file
@@ -0,0 +1,199 @@
|
||||
# Agent Guidelines for Jello
|
||||
|
||||
This document provides guidelines for AI coding agents working on the Jello codebase.
|
||||
|
||||
## Project Overview
|
||||
|
||||
Jello is a WIP video client for Jellyfin written in Rust, focusing on HDR video playback using:
|
||||
- **iced** - Primary GUI toolkit
|
||||
- **gstreamer** - Video + audio decoding library
|
||||
- **wgpu** - Rendering video from GStreamer in iced
|
||||
|
||||
## Build, Test, and Lint Commands
|
||||
|
||||
### Building
|
||||
```bash
|
||||
# Build in release mode
|
||||
cargo build --release
|
||||
cargo build -r
|
||||
|
||||
# Build specific workspace member
|
||||
cargo build -p api
|
||||
cargo build -p gst
|
||||
cargo build -p ui-iced
|
||||
|
||||
# Run the application
|
||||
cargo run --release -- -vv
|
||||
just jello # Uses justfile
|
||||
```
|
||||
|
||||
### Testing
|
||||
```bash
|
||||
# Run all tests in workspace
|
||||
cargo test --workspace
|
||||
|
||||
# Run tests for a specific package
|
||||
cargo test -p gst
|
||||
cargo test -p api
|
||||
cargo test -p iced-video
|
||||
|
||||
# Run a single test by name
|
||||
cargo test test_appsink
|
||||
cargo test -p gst test_appsink
|
||||
|
||||
# Run a specific test in a specific file
|
||||
cargo test -p gst --test <test_file_name> <test_function_name>
|
||||
|
||||
# Run tests with output
|
||||
cargo test -- --nocapture
|
||||
cargo test -- --show-output
|
||||
```
|
||||
|
||||
### Linting and Formatting
|
||||
```bash
|
||||
# Check code without building
|
||||
cargo check
|
||||
cargo check --workspace
|
||||
|
||||
# Run clippy (linter)
|
||||
cargo clippy
|
||||
cargo clippy --workspace
|
||||
cargo clippy --workspace -- -D warnings
|
||||
|
||||
# Format code
|
||||
cargo fmt
|
||||
cargo fmt --all
|
||||
|
||||
# Check formatting without modifying files
|
||||
cargo fmt --all -- --check
|
||||
```
|
||||
|
||||
### Other Tools
|
||||
```bash
|
||||
# Check for security vulnerabilities and license compliance
|
||||
cargo deny check
|
||||
|
||||
# Generate Jellyfin type definitions
|
||||
just typegen
|
||||
```
|
||||
|
||||
## Code Style Guidelines
|
||||
|
||||
### Rust Edition
|
||||
- Use **Rust 2024 edition** (as specified in Cargo.toml files)
|
||||
|
||||
### Imports
|
||||
- Use `use` statements at the top of files
|
||||
- Group imports: std library, external crates, then local modules
|
||||
- Use `crate::` for absolute paths within the crate
|
||||
- Common pattern: create a `priv_prelude` module for internal imports
|
||||
- Use `pub use` to re-export commonly used items
|
||||
- Use wildcard imports (`use crate::priv_prelude::*;`) within internal modules when a prelude exists
|
||||
|
||||
Example:
|
||||
```rust
|
||||
use std::sync::Arc;
|
||||
|
||||
use reqwest::{Method, header::InvalidHeaderValue};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::errors::*;
|
||||
```
|
||||
|
||||
### Naming Conventions
|
||||
- **Types/Structs/Enums**: PascalCase (e.g., `JellyfinClient`, `Error`, `AppSink`)
|
||||
- **Functions/Methods**: snake_case (e.g., `request_builder`, `stream_url`)
|
||||
- **Variables**: snake_case (e.g., `access_token`, `device_id`)
|
||||
- **Constants**: SCREAMING_SNAKE_CASE (e.g., `NEXT_ID`, `GST`)
|
||||
- **Modules**: snake_case (e.g., `priv_prelude`, `error_stack`)
|
||||
|
||||
### Error Handling
|
||||
- Use **`error-stack`** for error handling with context propagation
|
||||
- Use **`thiserror`** for defining error types
|
||||
- Standard error type pattern:
|
||||
```rust
|
||||
pub use error_stack::{Report, ResultExt};
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
#[error("An error occurred")]
|
||||
pub struct Error;
|
||||
|
||||
pub type Result<T, E = error_stack::Report<Error>> = core::result::Result<T, E>;
|
||||
```
|
||||
- Attach context to errors using `.change_context(Error)` and `.attach("description")`
|
||||
- Use `#[track_caller]` on functions that may panic or error for better error messages
|
||||
- Error handling example:
|
||||
```rust
|
||||
self.inner
|
||||
.set_state(gstreamer::State::Playing)
|
||||
.change_context(Error)
|
||||
.attach("Failed to set pipeline to Playing state")?;
|
||||
```
|
||||
|
||||
### Types
|
||||
- Prefer explicit types over type inference when it improves clarity
|
||||
- Use `impl Trait` for function parameters when appropriate (e.g., `impl AsRef<str>`)
|
||||
- Use `Option<T>` and `Result<T, E>` idiomatically
|
||||
- Use `Arc<T>` for shared ownership
|
||||
- Use newtype patterns for semantic clarity (e.g., `ApiKey` wrapping `secrecy::SecretBox<String>`)
|
||||
|
||||
### Formatting
|
||||
- Use 4 spaces for indentation
|
||||
- Line length: aim for 100 characters, but not strictly enforced
|
||||
- Use trailing commas in multi-line collections
|
||||
- Follow standard Rust formatting conventions (enforced by `cargo fmt`)
|
||||
|
||||
### Documentation
|
||||
- Add doc comments (`///`) for public APIs
|
||||
- Use inline comments (`//`) sparingly, prefer self-documenting code
|
||||
- Include examples in doc comments when helpful
|
||||
|
||||
### Async/Await
|
||||
- Use `tokio` as the async runtime
|
||||
- Mark async functions with `async` keyword
|
||||
- Use `.await` for async operations
|
||||
- Common pattern: `tokio::fs` for file operations
|
||||
|
||||
### Module Structure
|
||||
- Use `mod.rs` or inline modules as appropriate
|
||||
- Keep related functionality together
|
||||
- Use `pub(crate)` for internal APIs
|
||||
- Re-export commonly used items at crate root
|
||||
|
||||
### Macros
|
||||
- Custom macros used: `wrap_gst!`, `parent_child!`
|
||||
- Use macros for reducing boilerplate, only in the `gst` crate
|
||||
|
||||
### Testing
|
||||
- Place tests in the same file with `#[test]` or `#[cfg(test)]`
|
||||
- Use descriptive test function names (e.g., `test_appsink`, `unique_generates_different_ids`)
|
||||
- Initialize tracing in tests when needed for debugging
|
||||
|
||||
### Dependencies
|
||||
- Prefer well-maintained crates from crates.io
|
||||
- Use `workspace.dependencies` for shared dependencies across workspace members
|
||||
- Pin versions when stability is important
|
||||
|
||||
### Workspace Structure
|
||||
The project uses a Cargo workspace with multiple members:
|
||||
- `.` - Main jello binary
|
||||
- `api` - Jellyfin API client
|
||||
- `gst` - GStreamer wrapper
|
||||
- `ui-iced` - Iced UI implementation
|
||||
- `ui-gpui` - GPUI UI implementation (optional)
|
||||
- `store` - Secret/data/storage management
|
||||
- `jello-types` - Shared type definitions
|
||||
- `typegen` - Jellyfin type generator
|
||||
- `crates/iced-video` - Custom iced video widget
|
||||
- `examples/hdr-gstreamer-wgpu` - HDR example
|
||||
|
||||
### Project-Specific Patterns
|
||||
- Use `LazyLock` for global initialization (e.g., GStreamer init)
|
||||
- Use the builder pattern with method chaining (e.g., `request_builder()`)
|
||||
- Use `tap` crate's `.pipe()` for functional transformations
|
||||
- Prefer `BTreeMap`/`BTreeSet` over `HashMap`/`HashSet` when order matters
|
||||
- Prefer a functional programming style instead of an imperative one.
|
||||
- When building UIs keep the handler and view code in the same module (eg. settings view and settings handle in the same file)
|
||||
|
||||
## License
|
||||
All code in this project is MIT licensed.
|
||||
135
Cargo.lock
generated
135
Cargo.lock
generated
@@ -747,18 +747,6 @@ dependencies = [
|
||||
"core2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitvec"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c"
|
||||
dependencies = [
|
||||
"funty",
|
||||
"radium",
|
||||
"tap",
|
||||
"wyz",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "blade-graphics"
|
||||
version = "0.7.0"
|
||||
@@ -878,28 +866,6 @@ version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e79769241dcd44edf79a732545e8b5cec84c247ac060f5252cd51885d093a8fc"
|
||||
|
||||
[[package]]
|
||||
name = "bson"
|
||||
version = "3.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b3f109694c4f45353972af96bf97d8a057f82e2d6e496457f4d135b9867a518c"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"base64",
|
||||
"bitvec",
|
||||
"getrandom 0.3.4",
|
||||
"hex",
|
||||
"indexmap",
|
||||
"js-sys",
|
||||
"rand 0.9.2",
|
||||
"serde",
|
||||
"serde_bytes",
|
||||
"simdutf8",
|
||||
"thiserror 2.0.18",
|
||||
"time",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bstr"
|
||||
version = "1.12.1"
|
||||
@@ -1747,15 +1713,6 @@ version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26bf8fc351c5ed29b5c2f0cbbac1b209b74f60ecd62e675a998df72c49af5204"
|
||||
|
||||
[[package]]
|
||||
name = "deranged"
|
||||
version = "0.5.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587"
|
||||
dependencies = [
|
||||
"powerfmt",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_more"
|
||||
version = "0.99.20"
|
||||
@@ -2341,12 +2298,6 @@ dependencies = [
|
||||
"pkg-config",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "funty"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c"
|
||||
|
||||
[[package]]
|
||||
name = "futf"
|
||||
version = "0.1.5"
|
||||
@@ -4833,12 +4784,6 @@ dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-conv"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
|
||||
|
||||
[[package]]
|
||||
name = "num-derive"
|
||||
version = "0.4.2"
|
||||
@@ -5733,12 +5678,6 @@ dependencies = [
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "powerfmt"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.21"
|
||||
@@ -5960,12 +5899,6 @@ version = "5.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
|
||||
|
||||
[[package]]
|
||||
name = "radium"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09"
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.8.5"
|
||||
@@ -6142,16 +6075,6 @@ dependencies = [
|
||||
"font-types",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redb"
|
||||
version = "3.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ae323eb086579a3769daa2c753bb96deb95993c534711e0dbe881b5192906a06"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.2.16"
|
||||
@@ -6674,16 +6597,6 @@ dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_bytes"
|
||||
version = "0.11.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a5d440709e79d88e51ac01c4b72fc6cb7314017bb7da9eeff678aa94c10e3ea8"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_core"
|
||||
version = "1.0.228"
|
||||
@@ -6849,12 +6762,6 @@ dependencies = [
|
||||
"quote",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "simdutf8"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e"
|
||||
|
||||
[[package]]
|
||||
name = "simplecss"
|
||||
version = "0.2.2"
|
||||
@@ -7129,10 +7036,8 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||
name = "store"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bson",
|
||||
"futures",
|
||||
"parking_lot",
|
||||
"redb",
|
||||
"secrecy",
|
||||
"serde",
|
||||
"tokio",
|
||||
@@ -7547,37 +7452,6 @@ dependencies = [
|
||||
"zune-jpeg 0.4.21",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.3.45"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f9e442fc33d7fdb45aa9bfeb312c095964abdf596f7567261062b2a7107aaabd"
|
||||
dependencies = [
|
||||
"deranged",
|
||||
"itoa",
|
||||
"num-conv",
|
||||
"powerfmt",
|
||||
"serde_core",
|
||||
"time-core",
|
||||
"time-macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time-core"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b36ee98fd31ec7426d599183e8fe26932a8dc1fb76ddb6214d05493377d34ca"
|
||||
|
||||
[[package]]
|
||||
name = "time-macros"
|
||||
version = "0.2.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "71e552d1249bf61ac2a52db88179fd0673def1e1ad8243a00d9ec9ed71fee3dd"
|
||||
dependencies = [
|
||||
"num-conv",
|
||||
"time-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tiny-skia"
|
||||
version = "0.11.4"
|
||||
@@ -9511,15 +9385,6 @@ version = "0.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9"
|
||||
|
||||
[[package]]
|
||||
name = "wyz"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed"
|
||||
dependencies = [
|
||||
"tap",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "x11-dl"
|
||||
version = "2.21.0"
|
||||
|
||||
@@ -4,10 +4,8 @@ version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
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"] }
|
||||
|
||||
@@ -1,225 +0,0 @@
|
||||
// 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();
|
||||
// // };
|
||||
// // // 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,
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
@@ -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;
|
||||
|
||||
@@ -293,9 +294,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)
|
||||
.padding(50)
|
||||
.pipe(mouse_area)
|
||||
.on_press(Message::Settings(settings::SettingsMessage::Close));
|
||||
// let content = mouse_area(content).on_press(Message::Home);
|
||||
stack![content, settings].into()
|
||||
}
|
||||
Screen::Home | _ => content,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -310,20 +331,18 @@ 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),
|
||||
)
|
||||
Grid::with_children(state.cache.items_of(state.current).into_iter().map(card))
|
||||
.fluid(400)
|
||||
.spacing(50)
|
||||
.pipe(container)
|
||||
.padding(50)
|
||||
.align_x(Alignment::Center)
|
||||
// .align_y(Alignment::Center)
|
||||
.height(Length::Fill)
|
||||
.width(Length::Fill),
|
||||
)
|
||||
.height(Length::Fill)
|
||||
.into()
|
||||
.width(Length::Fill)
|
||||
.pipe(scrollable)
|
||||
.height(Length::Fill)
|
||||
.into()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -377,19 +396,18 @@ 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),
|
||||
)
|
||||
.padding(10)
|
||||
.width(Length::Fill)
|
||||
.height(Length::Shrink)
|
||||
.style(container::rounded_box)
|
||||
.into()
|
||||
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)
|
||||
.pipe(container)
|
||||
.padding(10)
|
||||
.width(Length::Fill)
|
||||
.height(Length::Shrink)
|
||||
.style(container::rounded_box)
|
||||
.into()
|
||||
}
|
||||
|
||||
fn footer(state: &State) -> Element<'_, Message> {
|
||||
|
||||
Reference in New Issue
Block a user