chore: cargo workspace skeleton

Eleven empty crates per DESIGN.md §11, versions and lints pinned once in the
root manifest, Justfile carrying the §12 gate, nix dev shell.

The frontend steps in `just ci` no-op until web/ exists. arr-core carries one
placeholder test because `cargo nextest` fails a zero-test run, and relaxing
that would let a broken filter go green later.

Closes #1

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Claude
2026-08-22 19:20:47 +01:00
parent de3e77537f
commit f4e63f1ac5
28 changed files with 384 additions and 0 deletions
Generated
+47
View File
@@ -0,0 +1,47 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "arr-api"
version = "0.1.0"
[[package]]
name = "arr-compat"
version = "0.1.0"
[[package]]
name = "arr-core"
version = "0.1.0"
[[package]]
name = "arr-daemon"
version = "0.1.0"
[[package]]
name = "arr-db"
version = "0.1.0"
[[package]]
name = "arr-dl"
version = "0.1.0"
[[package]]
name = "arr-e2e"
version = "0.1.0"
[[package]]
name = "arr-indexer"
version = "0.1.0"
[[package]]
name = "arr-meta"
version = "0.1.0"
[[package]]
name = "arr-parse"
version = "0.1.0"
[[package]]
name = "arr-probe"
version = "0.1.0"
+69
View File
@@ -0,0 +1,69 @@
[workspace]
resolver = "2"
members = ["crates/*"]
[workspace.package]
version = "0.1.0"
edition = "2021"
license = "MIT"
repository = "https://git.naps.pt/yolo/arr"
publish = false
[workspace.dependencies]
# Internal
arr-api = { path = "crates/arr-api" }
arr-compat = { path = "crates/arr-compat" }
arr-core = { path = "crates/arr-core" }
arr-db = { path = "crates/arr-db" }
arr-dl = { path = "crates/arr-dl" }
arr-indexer = { path = "crates/arr-indexer" }
arr-meta = { path = "crates/arr-meta" }
arr-parse = { path = "crates/arr-parse" }
arr-probe = { path = "crates/arr-probe" }
# Runtime and HTTP
axum = "0.8"
tokio = { version = "1", features = ["macros", "rt-multi-thread", "sync", "time", "net", "io-util", "fs", "signal", "process"] }
tower-http = { version = "0.6", features = ["trace"] }
reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls"] }
# Persistence
sqlx = { version = "0.8", default-features = false, features = ["runtime-tokio", "sqlite", "macros", "migrate", "chrono", "json"] }
# API surface
utoipa = { version = "5", features = ["axum_extras", "chrono"] }
utoipa-axum = "0.2"
utoipa-scalar = { version = "0.3", features = ["axum"] }
# Embedded frontend
include_dir = "0.7"
mime_guess = "2"
# Serialisation
serde = { version = "1", features = ["derive"] }
serde_json = "1"
quick-xml = { version = "0.37", features = ["serialize"] }
toml = "0.8"
# Odds and ends
chrono = { version = "0.4", default-features = false, features = ["clock", "serde"] }
clap = { version = "4.5", features = ["derive", "env"] }
thiserror = "2"
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
uuid = { version = "1", features = ["v4", "serde"] }
# Test-only
wiremock = "0.6"
[workspace.lints.rust]
unused_crate_dependencies = "warn"
missing_debug_implementations = "warn"
[workspace.lints.clippy]
pedantic = { level = "warn", priority = -1 }
unwrap_used = "warn"
[profile.release]
lto = "thin"
codegen-units = 1
+55
View File
@@ -0,0 +1,55 @@
# Dev entry points. `just` with no args lists them.
# The full per-push gate, same as CI. Run this before pushing.
ci: fmt-check lint deps test web-check
# Formatting, checked not applied.
fmt-check:
cargo fmt --all -- --check
# Format every Rust source.
fmt:
cargo fmt --all
# Clippy, warnings denied.
lint:
cargo clippy --all-targets -- -D warnings
# Unused workspace dependencies. Stable toolchain, unlike cargo-udeps.
deps:
cargo machete
# Unit and integration tests, excluding the e2e crate.
test:
cargo nextest run --workspace --exclude arr-e2e
# End-to-end tests. Needs a Transmission container; not part of the push gate.
e2e:
cargo nextest run -p arr-e2e
# Frontend lint and typecheck. No-op until web/ exists (issue #6).
web-check:
#!/usr/bin/env bash
set -euo pipefail
if [ ! -d web ]; then echo "web/ not present yet, skipping"; exit 0; fi
biome ci web/
pnpm -C web install --frozen-lockfile
pnpm -C web exec tsc -b --noEmit
# Build the SPA and the binary that embeds it.
build:
#!/usr/bin/env bash
set -euo pipefail
if [ -d web ]; then
pnpm -C web install --frozen-lockfile
pnpm -C web build
fi
cargo build --release
# Advisory and licence scan. Scheduled, not part of the push gate.
audit:
cargo deny check
# Install the tools the gate needs.
setup:
cargo install cargo-nextest cargo-machete --locked
+12
View File
@@ -0,0 +1,12 @@
[package]
name = "arr-api"
version.workspace = true
edition.workspace = true
license.workspace = true
repository.workspace = true
publish = false
[dependencies]
[lints]
workspace = true
+1
View File
@@ -0,0 +1 @@
//! arr-api — see DESIGN.md.
+12
View File
@@ -0,0 +1,12 @@
[package]
name = "arr-compat"
version.workspace = true
edition.workspace = true
license.workspace = true
repository.workspace = true
publish = false
[dependencies]
[lints]
workspace = true
+1
View File
@@ -0,0 +1 @@
//! arr-compat — see DESIGN.md.
+12
View File
@@ -0,0 +1,12 @@
[package]
name = "arr-core"
version.workspace = true
edition.workspace = true
license.workspace = true
repository.workspace = true
publish = false
[dependencies]
[lints]
workspace = true
+14
View File
@@ -0,0 +1,14 @@
//! arr-core — domain types, policy engine, scoring. See DESIGN.md §4 and §5.
//!
//! This crate must never depend on `axum`, `sqlx` or `reqwest`. It holds the
//! logic that is tested constantly and it has to stay fast to compile.
#[cfg(test)]
mod tests {
/// Placeholder so the test gate is meaningful on an empty workspace —
/// `cargo nextest` fails a run with zero tests, and relaxing that would
/// let a broken filter go green later. Replaced by real tests in the
/// domain-types and policy-engine issues.
#[test]
fn workspace_builds_and_tests_run() {}
}
+16
View File
@@ -0,0 +1,16 @@
[package]
name = "arr-daemon"
version.workspace = true
edition.workspace = true
license.workspace = true
repository.workspace = true
publish = false
[[bin]]
name = "arr"
path = "src/main.rs"
[dependencies]
[lints]
workspace = true
+5
View File
@@ -0,0 +1,5 @@
//! arr — reconcile loop and process entry point. See DESIGN.md §8.
fn main() {
println!("arr");
}
+12
View File
@@ -0,0 +1,12 @@
[package]
name = "arr-db"
version.workspace = true
edition.workspace = true
license.workspace = true
repository.workspace = true
publish = false
[dependencies]
[lints]
workspace = true
+1
View File
@@ -0,0 +1 @@
//! arr-db — see DESIGN.md.
+12
View File
@@ -0,0 +1,12 @@
[package]
name = "arr-dl"
version.workspace = true
edition.workspace = true
license.workspace = true
repository.workspace = true
publish = false
[dependencies]
[lints]
workspace = true
+1
View File
@@ -0,0 +1 @@
//! arr-dl — see DESIGN.md.
+12
View File
@@ -0,0 +1,12 @@
[package]
name = "arr-e2e"
version.workspace = true
edition.workspace = true
license.workspace = true
repository.workspace = true
publish = false
[dependencies]
[lints]
workspace = true
+1
View File
@@ -0,0 +1 @@
//! arr-e2e — see DESIGN.md.
+12
View File
@@ -0,0 +1,12 @@
[package]
name = "arr-indexer"
version.workspace = true
edition.workspace = true
license.workspace = true
repository.workspace = true
publish = false
[dependencies]
[lints]
workspace = true
+1
View File
@@ -0,0 +1 @@
//! arr-indexer — see DESIGN.md.
+12
View File
@@ -0,0 +1,12 @@
[package]
name = "arr-meta"
version.workspace = true
edition.workspace = true
license.workspace = true
repository.workspace = true
publish = false
[dependencies]
[lints]
workspace = true
+1
View File
@@ -0,0 +1 @@
//! arr-meta — see DESIGN.md.
+12
View File
@@ -0,0 +1,12 @@
[package]
name = "arr-parse"
version.workspace = true
edition.workspace = true
license.workspace = true
repository.workspace = true
publish = false
[dependencies]
[lints]
workspace = true
+1
View File
@@ -0,0 +1 @@
//! arr-parse — see DESIGN.md.
+12
View File
@@ -0,0 +1,12 @@
[package]
name = "arr-probe"
version.workspace = true
edition.workspace = true
license.workspace = true
repository.workspace = true
publish = false
[dependencies]
[lints]
workspace = true
+1
View File
@@ -0,0 +1 @@
//! arr-probe — see DESIGN.md.
+45
View File
@@ -0,0 +1,45 @@
{
description = "arr single-service replacement for Radarr and Sonarr";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
flake-utils.url = "github:numtide/flake-utils";
rust-overlay = {
url = "github:oxalica/rust-overlay";
inputs.nixpkgs.follows = "nixpkgs";
};
};
outputs = { self, nixpkgs, flake-utils, rust-overlay }:
flake-utils.lib.eachDefaultSystem (system:
let
pkgs = import nixpkgs {
inherit system;
overlays = [ (import rust-overlay) ];
};
rust = pkgs.rust-bin.fromRustupToolchainFile ./rust-toolchain.toml;
in
{
devShells.default = pkgs.mkShell {
buildInputs = with pkgs; [
rust
cargo-nextest
cargo-machete
cargo-deny
just
sqlite
# arr-probe shells out to ffprobe; the e2e fixtures need ffmpeg.
ffmpeg
# Frontend, from issue #6 onwards.
nodejs
pnpm
biome
];
shellHook = ''
echo "arr dev shell 'just' lists the entry points"
'';
};
});
}
+3
View File
@@ -0,0 +1,3 @@
[toolchain]
channel = "stable"
components = ["rustfmt", "clippy"]
+1
View File
@@ -0,0 +1 @@
edition = "2021"