diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index d649966d..dade483e 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -36,7 +36,7 @@ jobs: run: cargo fmt --all -- --check - name: clippy - run: cargo clippy --all-targets -- -D warnings + run: cargo clippy --workspace --all-targets -- -D warnings - name: test run: cargo test diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7291dc9c..5b423adf 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,7 +36,7 @@ jobs: run: cargo fmt --all -- --check - name: clippy - run: cargo clippy --all-targets -- -D warnings + run: cargo clippy --workspace --all-targets -- -D warnings - name: test run: cargo test diff --git a/CODE_STYLE.md b/CODE_STYLE.md new file mode 100644 index 00000000..12d18456 --- /dev/null +++ b/CODE_STYLE.md @@ -0,0 +1,283 @@ +# Code Style Guide + +This document defines the coding standards for this project. All contributors (human and agent) must follow these rules. When in doubt, prefer clarity and correctness over cleverness and speed. + +## Philosophy + +- Code reads like prose +- Do things properly — no shortcuts +- SOLID principles — always, no exceptions +- KISS — simplest solution that solves the problem **correctly and properly**. KISS means no unnecessary complexity, NOT skipping proper architecture. If the design calls for a trait, a newtype, or a pattern — that IS the simple solution. When in doubt, follow the architecture (CONTEXT.md, ADRs) over perceived simplicity. +- DRY — one source of truth for every piece of knowledge +- Use design patterns where they fit (Strategy, Observer, Builder, etc.) — name them in code so intent is clear +- Single responsibility everywhere: files, functions, types, modules +- Single point of entry, single control flow — no parallel data/control paths +- Open/Closed — extend via new types, don't modify existing ones +- Depend on abstractions (traits), not concretions +- Keep things as private as possible +- Zero magic numbers, strings, or unnamed constants +- Newtypes for self-documenting code +- Descriptive variable names — `review_rating`, not `rr`. `watched_on`, not `wd`. `poster_url`, not `pu`. No abbreviations unless universally understood (e.g., `id`, `url`, `i` for a trivial loop index). + +## Project Structure + +### Module Organization + +Deep module hierarchy, grouped by concern. No flat file dumps. Each file is its own small, enclosed thing. + +``` +crates/domain/src/ +├── models/ +│ ├── review.rs +│ ├── diary_entry.rs +│ └── movie.rs +├── value_objects/ +│ ├── movie_id.rs +│ ├── rating.rs +│ └── watched_on.rs +├── ports/ +│ ├── review_repository.rs +│ └── event_publisher.rs +└── ... +``` + +### File Size + +Small files with a single concern. If a file grows past ~100-150 lines, it's probably doing too much — split it. + +### Tests + +Never in the same file as production code. Use cases keep their tests in a sibling `tests/` module, one file per use case: + +``` +crates/application/src/diary/log_review.rs +crates/application/src/diary/tests/log_review.rs +``` + +## Naming + +### Conventions + +- Files and modules: `snake_case` +- Types: `PascalCase` with semantic suffixes for clarity and navigation +- Functions and methods: `snake_case`, verb-first, descriptive + +### Type Suffixes + +| Suffix | Usage | Example | +|---|---|---| +| (none) | Value objects, entities | `Review`, `Movie`, `DiaryEntry` | +| `Id` | Identity newtypes | `MovieId`, `ReviewId`, `UserId` | +| `Config` | Configuration data | `ServerConfig`, `StorageConfig` | +| `Service` | Domain services | `LocalSocialService` | +| `Error` | Error types | `DomainError`, `AuthError` | +| `UseCase` | Application use cases | `LogReviewUseCase`, `GetDiaryUseCase` | +| `Strategy` | Strategy trait/impls | `RatingAggregationStrategy` | + +Ports are named for the capability they provide (`ReviewRepository`, `EventPublisher`); their implementations are named for the technology (`SqliteReviewRepository`). + +### Newtypes + +Use newtypes everywhere a primitive carries domain meaning: + +```rust +// YES +struct MovieId(Uuid); +struct ReviewId(Uuid); +struct Rating(u8); +struct PosterUrl(Url); + +// NO +type MovieId = String; +fn log_review(movie: &str, rating: u8, watched_on: &str) -> ... +``` + +Newtypes are self-documenting and prevent accidental misuse (can't pass a `MovieId` where a `ReviewId` is expected). + +## Functions and Methods + +### Single Responsibility + +Each function does ONE thing. Compose small functions into larger behaviors. + +```rust +// YES +fn resolve_movie(...) -> Movie { ... } +fn build_review(movie: &Movie, draft: ReviewDraft) -> Review { ... } +fn record_diary_entry(review: &Review) -> DiaryEntry { ... } + +// NO +fn log_review(...) -> DiaryEntry { + // 80 lines doing movie resolution, review construction and diary recording +} +``` + +### Visibility + +Default to private. Only `pub` what the module's consumers actually need. Use `pub(crate)` for intra-crate sharing that shouldn't leak outside. + +### impl Blocks + +Split by concern: + +```rust +// Construction +impl Review { + pub fn new(movie: MovieId, rating: Rating, watched_on: WatchedOn) -> Self { ... } +} + +// Queries +impl Review { + pub fn is_rewatch(&self) -> bool { ... } + pub fn rating(&self) -> Rating { ... } +} + +// Mutations +impl Review { + pub fn revise_rating(&mut self, rating: Rating) { ... } +} +``` + +## Error Handling + +### Domain and Application: `thiserror` + +Typed, matchable error enums. Every error variant is meaningful. + +```rust +#[derive(Debug, thiserror::Error)] +pub enum DiaryError { + #[error("movie {0} not found")] + MovieNotFound(MovieId), + #[error("review rejected: {0}")] + ReviewRejected(String), +} +``` + +### Adapters and Bootstrap: `anyhow` + +Pragmatic error handling. Context strings for debugging. + +```rust +fn fetch_poster_blob(poster_url: &PosterUrl) -> anyhow::Result { + let data = http_client.get(poster_url) + .context("failed to fetch poster")?; + // ... +} +``` + +### Never `unwrap` or `expect` in production code + +Errors propagate with `?` and bubble up to a single handler. No scattered panic points. + +`unwrap` is allowed **only** in test code. + +## Configuration + +Secrets (API keys, JWT secrets) and deployment settings live in environment variables, read once at startup into a typed config struct. + +Zero magic numbers or strings. Everything named, everything explained by its name. + +```rust +// YES +const MAX_REVIEW_BODY_LENGTH: usize = 10_000; + +// NO +if body.len() > 10_000 { ... } +``` + +Constants are only for true invariants that never change. Tunable values belong in config. + +## Comments + +Comments only where a name, test or ADR cannot carry the reason. A comment is noise that drifts out of date while the code moves on, so prefer to put the reason somewhere that cannot rot silently: + +- **A name.** Rename the function, the variable, or the type until the reason is visible in the code. +- **A test.** A constraint worth a comment is worth a test that fails when someone breaks it. `media_is_resolved_before_the_cascade_runs` outlives any note explaining why the order matters. +- **An ADR.** Architectural reasoning belongs in `docs/adr/`, where it is versioned and discoverable. + +When none of those can carry it, a short comment stating the reason (never restating the code) is acceptable. + +## Dependencies + +- Use good crates when they exist — don't reinvent +- Prefer crates with minimal transitive dependencies +- Aim for small memory footprint and executable size +- Every dependency that has a `[workspace.dependencies]` entry is declared `workspace = true`; crates inherit `edition` from `[workspace.package]` +- Domain crate: zero external dependencies beyond `thiserror`, `serde` and small value-type crates +- Application crate: depends only on domain +- Adapters: free to pull in platform crates (`axum`, `sqlx`, `nats`, etc.) +- Bootstrap: wiring only + +## Trait Objects for Ports + +Ports are `dyn` behind `Arc`. Use cases hold their dependencies as trait objects: + +```rust +pub struct Deps { + pub reviews: Arc, + pub events: Arc, +} +``` + +Prefer a closed enum over `dyn` only where exhaustiveness is the point — when adding a variant must force every consumer to handle it. That is a decision about compiler-enforced coverage, not about dispatch cost. + +## Testing + +### Test Helpers Are Gated + +`domain::testing` (in-memory fakes and noops) compiles only with the `test-helpers` feature, which crates enable in their `[dev-dependencies]`. Production builds must never contain fakes. `application::test_helpers` is `#[cfg(test)]`. + +### Coverage Targets + +| Crate | Approach | Target | +|---|---|---| +| Domain | TDD (red-green-refactor) | 100% | +| Application | TDD | High coverage | +| Adapters | Integration tests where practical | Best effort | +| Bootstrap | No tests | — | + +### Fakes Must Not Be Kinder Than Production + +A fake that is more forgiving than the real adapter turns a test into a lie: it passes whichever way the code is written, which is worse than having no test. Two rules follow. + +**A fake must reproduce the constraints the database enforces.** SQLite removes dependent rows through `ON DELETE CASCADE`, so an in-memory store must cascade the same way, and enforce the same uniqueness constraints. Without that, an ordering requirement such as "resolve the poster before deleting the `DiaryEntry`" could not be tested at all. + +**A fake must be able to fail.** Every port whose failure is meant to degrade rather than propagate needs a fake that refuses (a poster fetcher that errors, an event publisher that rejects). A best-effort path with no failing test is a path that has never run. + +When a claim cannot be observed through the fake, move the test to where the real thing runs rather than asserting it against the fake's own behaviour. + +### Test Naming + +Tests read as specifications: + +```rust +#[tokio::test] +async fn logging_review_creates_diary_entry() { ... } + +#[tokio::test] +async fn rewatch_review_keeps_original_entry() { ... } +``` + +## Control Flow + +### Single Point of Entry + +One path through the code. No parallel flows that do similar things in different places. + +```rust +// YES — one function handles movie resolution +let movie = movie_resolver.resolve(movie_id, requester); + +// NO — movie resolution duplicated in two places +// diary_handler.rs: resolves movie +// import_handler.rs: also resolves movie with slightly different logic +``` + +### Single Call, Cascading Changes + +When something changes, it should cascade from one point. Don't require updating multiple files for a single logical change. + +### No God Objects + +If a struct has more than 5-6 fields, question whether it's doing too much. If a function takes more than 4-5 parameters, consider grouping them into a dedicated type. diff --git a/Cargo.toml b/Cargo.toml index 59e44179..4c4c940a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -40,6 +40,9 @@ members = [ ] resolver = "2" +[workspace.package] +edition = "2024" + [workspace.dependencies] tokio = { version = "1.0", features = ["macros", "net", "rt", "rt-multi-thread", "sync", "time"] } bytes = "1" diff --git a/Makefile b/Makefile index 435ea944..caef6984 100644 --- a/Makefile +++ b/Makefile @@ -74,10 +74,10 @@ fmt-check: cargo fmt --check clippy: - cargo clippy -- -D warnings + cargo clippy --workspace --all-targets -- -D warnings test: - cargo test + cargo test --workspace fix: cargo fmt diff --git a/crates/adapters/activitypub/Cargo.toml b/crates/adapters/activitypub/Cargo.toml index 4fad9e32..261eb766 100644 --- a/crates/adapters/activitypub/Cargo.toml +++ b/crates/adapters/activitypub/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "activitypub" version = "0.1.0" -edition = "2024" +edition.workspace = true [dependencies] k-ap = { version = "0.5.0", registry = "gitea" } diff --git a/crates/adapters/adapter-common/Cargo.toml b/crates/adapters/adapter-common/Cargo.toml index 0951f37c..fe6f793c 100644 --- a/crates/adapters/adapter-common/Cargo.toml +++ b/crates/adapters/adapter-common/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "adapter-common" version = "0.1.0" -edition = "2024" +edition.workspace = true [dependencies] domain = { workspace = true } diff --git a/crates/adapters/auth/Cargo.toml b/crates/adapters/auth/Cargo.toml index 437ffc67..89a9f7b3 100644 --- a/crates/adapters/auth/Cargo.toml +++ b/crates/adapters/auth/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "auth" version = "0.1.0" -edition = "2024" +edition.workspace = true [dependencies] async-trait = { workspace = true } @@ -9,7 +9,7 @@ domain = { workspace = true } anyhow = { workspace = true } chrono = { workspace = true } uuid = { workspace = true } -serde = { version = "1.0", features = ["derive"] } +serde = { workspace = true } jsonwebtoken = "9" argon2 = { version = "0.5", features = ["std"] } rand_core = { version = "0.6", features = ["getrandom"] } diff --git a/crates/adapters/event-payload/Cargo.toml b/crates/adapters/event-payload/Cargo.toml index f06e5364..4418cba6 100644 --- a/crates/adapters/event-payload/Cargo.toml +++ b/crates/adapters/event-payload/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "event-payload" version = "0.1.0" -edition = "2024" +edition.workspace = true [dependencies] domain = { workspace = true } diff --git a/crates/adapters/event-publisher/Cargo.toml b/crates/adapters/event-publisher/Cargo.toml index 33f40e12..bb925b3a 100644 --- a/crates/adapters/event-publisher/Cargo.toml +++ b/crates/adapters/event-publisher/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "event-publisher" version = "0.1.0" -edition = "2024" +edition.workspace = true [dependencies] domain = { workspace = true } diff --git a/crates/adapters/export/Cargo.toml b/crates/adapters/export/Cargo.toml index 2b10cf48..edcdcfc3 100644 --- a/crates/adapters/export/Cargo.toml +++ b/crates/adapters/export/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "export" version = "0.1.0" -edition = "2024" +edition.workspace = true [dependencies] domain = { workspace = true } diff --git a/crates/adapters/image-converter/Cargo.toml b/crates/adapters/image-converter/Cargo.toml index 28a1a2dc..3e39230c 100644 --- a/crates/adapters/image-converter/Cargo.toml +++ b/crates/adapters/image-converter/Cargo.toml @@ -15,5 +15,5 @@ webp = "0.3" [dev-dependencies] object-storage = { workspace = true } -object_store = "0.11" +object_store = { workspace = true } uuid = { workspace = true } diff --git a/crates/adapters/importer/Cargo.toml b/crates/adapters/importer/Cargo.toml index f4d0bc94..e80f6e75 100644 --- a/crates/adapters/importer/Cargo.toml +++ b/crates/adapters/importer/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "importer" version = "0.1.0" -edition = "2024" +edition.workspace = true [features] xlsx = ["dep:calamine"] diff --git a/crates/adapters/jellyfin/Cargo.toml b/crates/adapters/jellyfin/Cargo.toml index 78368268..cd50e5e4 100644 --- a/crates/adapters/jellyfin/Cargo.toml +++ b/crates/adapters/jellyfin/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "jellyfin" version = "0.1.0" -edition = "2024" +edition.workspace = true [dependencies] domain = { workspace = true } diff --git a/crates/adapters/metadata/Cargo.toml b/crates/adapters/metadata/Cargo.toml index 3ae18512..efbb2a6e 100644 --- a/crates/adapters/metadata/Cargo.toml +++ b/crates/adapters/metadata/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "metadata" version = "0.1.0" -edition = "2024" +edition.workspace = true [dependencies] anyhow = { workspace = true } diff --git a/crates/adapters/nats/Cargo.toml b/crates/adapters/nats/Cargo.toml index 0e5fd467..c2318dc2 100644 --- a/crates/adapters/nats/Cargo.toml +++ b/crates/adapters/nats/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "nats" version = "0.1.0" -edition = "2024" +edition.workspace = true [dependencies] async-nats = "0.48.0" diff --git a/crates/adapters/object-storage/Cargo.toml b/crates/adapters/object-storage/Cargo.toml index fa791461..fcf61cfc 100644 --- a/crates/adapters/object-storage/Cargo.toml +++ b/crates/adapters/object-storage/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "object-storage" version = "0.1.0" -edition = "2024" +edition.workspace = true [dependencies] domain = { workspace = true } diff --git a/crates/adapters/plex/Cargo.toml b/crates/adapters/plex/Cargo.toml index 1dbb842b..408dfd19 100644 --- a/crates/adapters/plex/Cargo.toml +++ b/crates/adapters/plex/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "plex" version = "0.1.0" -edition = "2024" +edition.workspace = true [dependencies] domain = { workspace = true } diff --git a/crates/adapters/poster-sync/Cargo.toml b/crates/adapters/poster-sync/Cargo.toml index 4ad9be58..39e41276 100644 --- a/crates/adapters/poster-sync/Cargo.toml +++ b/crates/adapters/poster-sync/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "poster-sync" version = "0.1.0" -edition = "2024" +edition.workspace = true [dependencies] domain = { workspace = true } diff --git a/crates/adapters/postgres-event-queue/Cargo.toml b/crates/adapters/postgres-event-queue/Cargo.toml index e9cd111d..83f23d11 100644 --- a/crates/adapters/postgres-event-queue/Cargo.toml +++ b/crates/adapters/postgres-event-queue/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "postgres-event-queue" version = "0.1.0" -edition = "2024" +edition.workspace = true [dependencies] sqlx = { version = "0.8.6", features = ["runtime-tokio-rustls", "postgres", "macros", "chrono"] } diff --git a/crates/adapters/postgres-federation/Cargo.toml b/crates/adapters/postgres-federation/Cargo.toml index d130e25a..a7bd6358 100644 --- a/crates/adapters/postgres-federation/Cargo.toml +++ b/crates/adapters/postgres-federation/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "postgres-federation" version = "0.1.0" -edition = "2024" +edition.workspace = true [dependencies] sqlx = { version = "0.8.6", features = [ diff --git a/crates/adapters/postgres-social/Cargo.toml b/crates/adapters/postgres-social/Cargo.toml index d90e9bf8..9354c526 100644 --- a/crates/adapters/postgres-social/Cargo.toml +++ b/crates/adapters/postgres-social/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "postgres-social" version = "0.1.0" -edition = "2024" +edition.workspace = true [dependencies] sqlx = { version = "0.8.6", features = [ diff --git a/crates/adapters/postgres/Cargo.toml b/crates/adapters/postgres/Cargo.toml index 38402ba4..30627837 100644 --- a/crates/adapters/postgres/Cargo.toml +++ b/crates/adapters/postgres/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "postgres" version = "0.1.0" -edition = "2024" +edition.workspace = true [dependencies] sqlx = { version = "0.8.6", features = [ diff --git a/crates/adapters/rss/Cargo.toml b/crates/adapters/rss/Cargo.toml index 2de1ac45..73207cc5 100644 --- a/crates/adapters/rss/Cargo.toml +++ b/crates/adapters/rss/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "rss" version = "0.1.0" -edition = "2024" +edition.workspace = true [dependencies] rss-feed = { package = "rss", version = "2", features = ["atom"] } diff --git a/crates/adapters/rss/src/tests/lib.rs b/crates/adapters/rss/src/tests/lib.rs index 0fe98bf4..3b282f5e 100644 --- a/crates/adapters/rss/src/tests/lib.rs +++ b/crates/adapters/rss/src/tests/lib.rs @@ -60,7 +60,9 @@ fn channel_links_are_absolute_with_atom_self() { fn item_links_to_absolute_movie_page() { let adapter = RssAdapter::new("http://example.com".into()); let e = entry(false); - let xml = adapter.render_feed(&[e.clone()], &channel("Feed")).unwrap(); + let xml = adapter + .render_feed(std::slice::from_ref(&e), &channel("Feed")) + .unwrap(); let expected = format!( "http://example.com/movies/{}", e.movie().id().value() diff --git a/crates/adapters/sqlite-event-queue/Cargo.toml b/crates/adapters/sqlite-event-queue/Cargo.toml index 30604ac8..df685810 100644 --- a/crates/adapters/sqlite-event-queue/Cargo.toml +++ b/crates/adapters/sqlite-event-queue/Cargo.toml @@ -1,10 +1,10 @@ [package] name = "sqlite-event-queue" version = "0.1.0" -edition = "2024" +edition.workspace = true [dependencies] -sqlx = { version = "0.8.6", features = ["runtime-tokio-rustls", "sqlite", "macros", "chrono"] } +sqlx = { workspace = true, features = ["macros", "chrono"] } domain = { workspace = true } event-payload = { workspace = true } anyhow = { workspace = true } diff --git a/crates/adapters/sqlite-federation/Cargo.toml b/crates/adapters/sqlite-federation/Cargo.toml index df18d5e8..573f19ad 100644 --- a/crates/adapters/sqlite-federation/Cargo.toml +++ b/crates/adapters/sqlite-federation/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "sqlite-federation" version = "0.1.0" -edition = "2024" +edition.workspace = true [dependencies] sqlx = { workspace = true } diff --git a/crates/adapters/sqlite-social/Cargo.toml b/crates/adapters/sqlite-social/Cargo.toml index d853a233..2fd4adf0 100644 --- a/crates/adapters/sqlite-social/Cargo.toml +++ b/crates/adapters/sqlite-social/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "sqlite-social" version = "0.1.0" -edition = "2024" +edition.workspace = true [dependencies] sqlx = { workspace = true } diff --git a/crates/adapters/sqlite/Cargo.toml b/crates/adapters/sqlite/Cargo.toml index 74079173..b53c5054 100644 --- a/crates/adapters/sqlite/Cargo.toml +++ b/crates/adapters/sqlite/Cargo.toml @@ -1,15 +1,10 @@ [package] name = "sqlite" version = "0.1.0" -edition = "2024" +edition.workspace = true [dependencies] -sqlx = { version = "0.8.6", features = [ - "runtime-tokio-rustls", - "sqlite", - "uuid", - "macros", -] } +sqlx = { workspace = true, features = ["macros"] } adapter-common = { workspace = true } domain = { workspace = true } diff --git a/crates/adapters/template-askama/Cargo.toml b/crates/adapters/template-askama/Cargo.toml index 9b037548..eea631b7 100644 --- a/crates/adapters/template-askama/Cargo.toml +++ b/crates/adapters/template-askama/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "template-askama" version = "0.1.0" -edition = "2024" +edition.workspace = true [dependencies] askama = { version = "0.16.0" } diff --git a/crates/adapters/tmdb-enrichment/Cargo.toml b/crates/adapters/tmdb-enrichment/Cargo.toml index b6d28d49..11fa35c3 100644 --- a/crates/adapters/tmdb-enrichment/Cargo.toml +++ b/crates/adapters/tmdb-enrichment/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "tmdb-enrichment" version = "0.1.0" -edition = "2024" +edition.workspace = true [dependencies] domain = { workspace = true } diff --git a/crates/api-types/Cargo.toml b/crates/api-types/Cargo.toml index 322565af..b1185136 100644 --- a/crates/api-types/Cargo.toml +++ b/crates/api-types/Cargo.toml @@ -1,13 +1,13 @@ [package] name = "api-types" version = "0.1.0" -edition = "2024" +edition.workspace = true [dependencies] serde = { workspace = true } uuid = { workspace = true } utoipa = { version = "5.5.0", features = ["axum_extras", "uuid"] } -domain = { path = "../domain" } +domain = { workspace = true } [dev-dependencies] serde_json = { workspace = true } diff --git a/crates/application/Cargo.toml b/crates/application/Cargo.toml index 50d8e29e..2ff9fe03 100644 --- a/crates/application/Cargo.toml +++ b/crates/application/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "application" version = "0.1.0" -edition = "2024" +edition.workspace = true [dependencies] async-trait = { workspace = true } diff --git a/crates/composition/Cargo.toml b/crates/composition/Cargo.toml index e4dab622..a9ad821d 100644 --- a/crates/composition/Cargo.toml +++ b/crates/composition/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "composition" version = "0.1.0" -edition = "2024" +edition.workspace = true [features] default = [] diff --git a/crates/domain/Cargo.toml b/crates/domain/Cargo.toml index 1e2cc03e..c16d2d0f 100644 --- a/crates/domain/Cargo.toml +++ b/crates/domain/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "domain" version = "0.1.0" -edition = "2024" +edition.workspace = true [dependencies] uuid = { workspace = true } diff --git a/crates/domain/src/lib.rs b/crates/domain/src/lib.rs index 42b7ebc1..30bb5eb8 100644 --- a/crates/domain/src/lib.rs +++ b/crates/domain/src/lib.rs @@ -5,5 +5,5 @@ pub mod ports; pub mod services; pub mod value_objects; -#[cfg(any(test, feature = "test-helpers"))] +#[cfg(feature = "test-helpers")] pub mod testing; diff --git a/crates/presentation/Cargo.toml b/crates/presentation/Cargo.toml index 56d14455..246c415c 100644 --- a/crates/presentation/Cargo.toml +++ b/crates/presentation/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "presentation" version = "0.1.0" -edition = "2024" +edition.workspace = true description = "Self-hosted movie diary with REST API and ActivityPub federation" license = "MIT" diff --git a/crates/server/Cargo.toml b/crates/server/Cargo.toml index 04be03c3..4f5fdacf 100644 --- a/crates/server/Cargo.toml +++ b/crates/server/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "server" version = "0.1.0" -edition = "2024" +edition.workspace = true description = "Self-hosted movie diary with REST API and ActivityPub federation" license = "MIT" diff --git a/crates/tui/Cargo.toml b/crates/tui/Cargo.toml index 1afb8dcb..af761643 100644 --- a/crates/tui/Cargo.toml +++ b/crates/tui/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "tui" version = "0.1.0" -edition = "2024" +edition.workspace = true [features] default = [] @@ -16,7 +16,7 @@ windows = ["dep:windows-native-keyring-store"] ratatui = "0.30.0" keyring-core = "1.0.0" directories = "6" -csv = "1" +csv = { workspace = true } apple-native-keyring-store = { version = "1.0.0", optional = true, features = [ "keychain", diff --git a/crates/worker/Cargo.toml b/crates/worker/Cargo.toml index 54809513..ce02a2d5 100644 --- a/crates/worker/Cargo.toml +++ b/crates/worker/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "worker" version = "0.1.0" -edition = "2024" +edition.workspace = true [features] default = ["sqlite", "sqlite-federation"]