[package] name = "kei" version = "0.26.1-dev" edition = "2024" # src/download/paths.rs calls str::floor_char_boundary, stabilized in 1.91. # Cargo refuses to build on older toolchains with a clean error naming this # version; the clippy::incompatible_msrv lint below catches regressions on PR. rust-version = "1.91" [[bin]] name = "kei" path = "src/main.rs" [features] # XMP metadata embedding + sidecar writing. Default-on; build with # `--no-default-features` on platforms where Adobe's vendored C++ XMP # Toolkit does not compile (notably FreeBSD - see #256). Disabling drops XMP # embedding and sidecars; native JPEG/TIFF EXIF writes # remain available through little_exif. default = ["xmp"] xmp = ["dep:xmp_toolkit"] # Re-exports parser entry points for cargo-fuzz harnesses (`fuzz/`). # Off by default; production builds never see the `kei::__fuzz` module. __fuzz_internals = [] [package.metadata.cargo-udeps.ignore] # Used only by no-default-features builds (`#[cfg(not(feature = "xmp"))]`). # The unused-deps CI job runs the default feature set, so cargo-udeps cannot # see the native EXIF path even though `cargo test --no-default-features` # exercises it. normal = ["little_exif"] [dependencies] # CLI clap = { version = "4", features = ["derive", "env"] } # Async runtime tokio = { version = "1", features = ["fs", "io-util", "macros", "process", "rt-multi-thread", "signal", "sync", "time"] } # HTTP reqwest = { version = "0.13", default-features = false, features = ["rustls", "cookies", "json", "stream", "http2"] } bytes = "1" futures-util = "0.3" tokio-stream = "0.1" tokio-util = "0.7" cookie = "0.18" # Bandwidth throttling (token-bucket for global download rate cap) async-speed-limit = "0.4" # Serialization serde = { version = "1", features = ["derive", "rc"] } # Durable JSON evidence must retain float bits for exact identity checks. serde_json = { version = "1", features = ["float_roundtrip"] } toml = "1" # Crypto (SRP auth) sha2 = "0.10" pbkdf2 = "0.12" num-bigint = "0.4" base64 = "0.22" uuid = { version = "1", features = ["v4"] } # Credential security secrecy = "0.10" aes-gcm = "0.10" # Dates chrono = { version = "0.4", default-features = false, features = ["clock", "serde", "std"] } # Logging tracing = "0.1" tracing-subscriber = { version = "0.3", features = ["env-filter"] } # `non_blocking` writer wraps stderr so that a slow consumer (Docker stdout # pipe, screen PTY, journald) cannot back-pressure into every tracing call # and stall the runtime. Configured `lossy(true)` in `lib.rs` so events drop # under saturation instead of parking spawn-blocking workers and the # heartbeat task on the writer mutex (issue #347). tracing-appender = "0.2" # Metrics / observability prometheus-client = "0.22" axum = { version = "0.8", default-features = false, features = ["http1", "tokio"] } # Progress / UI indicatif = "0.18" rpassword = "7" dialoguer = "0.12" # Already pulled in transitively by indicatif; declared explicitly so the # personality layer can use `console::Style` for friendly-mode glyph color # without depending on indicatif's internals. console = "0.16" # XMP metadata via Adobe's XMP Toolkit (vendored C++ source). # Handles JPEG, PNG, TIFF, MP4, MOV, PSD, PDF, and more via XMPFiles. # Does NOT handle HEIC - the bundled XMPFiles has no HEIF handler. HEIC # containers use kei's byte-preserving XMP item writer instead. # Optional (behind the `xmp` feature, default-on) so platforms without a # working C++ toolchain for the vendored sources can opt out. See #256. xmp_toolkit = { version = "1", optional = true } # Native EXIF writing used by `set_exif_*` settings, including builds that # disable the default `xmp` feature. little_exif = "0.6" # ISO-BMFF (MP4 / HEIF / HEIC) atom parsing. HEIC's XMP packet lives in a # `mime`-type item inside the `meta` box; mp4-atom reads existing packets and # remains test-only for fixture construction because production HEIC writes # preserve raw item-graph bytes. # # Pinned because kei depends on specific HEIF fixes included in 0.11.0: # - #123 `uri ` infe-item support for iOS 17+ HEICs (kei #274) # - #153 colr cursor advance for `prof` / `rICC` ICC profiles, covering # HDR HEIC parser fixtures (kei #269, #276) # - #157 bound `Vec::with_capacity` in `parse_vorbis_comment` / `Avcc`, # fixes the fuzz-found OOM (upstream #154) # - #161 drops mp4-atom's unmaintained `paste` dep in favor of `pastey` # - #163 returns Err instead of panicking on reserved iloc *_size nibbles # found by fuzzing (upstream #158) mp4-atom = "=0.11.0" # Error handling anyhow = "1" thiserror = "2" # Random rand = "0.10.1" # File locking fs4 = "0.13" # Glob pattern matching glob = "0.3" # Bit flags bitflags = "2" # Misc url = "2" dirs = "6" async-trait = "0.1" # SQLite state tracking rusqlite = { version = "0.39", features = ["bundled", "backup"] } # Binary plist decoding for iCloud *Enc fields (location, keywords, etc.) plist = "1" # System libc = "0.2" rustc-hash = "2.1" data-encoding = "2.10.0" [target.'cfg(target_os = "macos")'.dependencies] keyring = { version = "3", features = ["apple-native"] } [target.'cfg(target_os = "linux")'.dependencies] keyring = { version = "3", features = ["sync-secret-service"] } sd-notify = "0.5" # FreeBSD: Secret Service D-Bus API is available via ports (dbus + a # keyring daemon like gnome-keyring or KWallet). If no daemon is running, # `CredentialStore` falls back to the encrypted file backend. See #256. [target.'cfg(target_os = "freebsd")'.dependencies] keyring = { version = "3", features = ["sync-secret-service"] } [target.'cfg(target_os = "windows")'.dependencies] keyring = { version = "3", features = ["windows-native"] } windows-service = "0.7" # Used only for the named ERROR_* constants returned by windows-service # error variants (1060, 1063). 0.52 matches the version windows-service # 0.7 already brings in transitively, so no extra version in the build. windows-sys = { version = "0.52", features = ["Win32_Foundation", "Win32_Storage_FileSystem"] } [dev-dependencies] assert_cmd = "2" predicates = "3" tempfile = "3" dotenvy = "0.15" rusqlite = { version = "0.39", features = ["bundled", "backup"] } http = "1" wiremock = "0.6" tracing-test = "0.2" sha1 = "0.10" # test-util enables `tokio::test(start_paused = true)` so retry/backoff # tests in the auth layer don't sleep through real wall-clock delays. tokio = { version = "1", features = ["test-util"] } [[test]] name = "cli" path = "tests/cli.rs" [[test]] name = "sync" path = "tests/sync.rs" [[test]] name = "state_auth" path = "tests/state_auth.rs" [lints.clippy] # Baseline lint group. Lower priority so individual lints below can override. all = { level = "warn", priority = -1 } # ── Don't Panic ─────────────────────────────────────────────────────── unwrap_used = "warn" expect_used = "warn" panic = "warn" todo = "warn" unimplemented = "warn" unreachable = "warn" get_unwrap = "warn" indexing_slicing = "warn" string_slice = "warn" unwrap_in_result = "warn" panic_in_result_fn = "warn" # ── Don't Fail Silently ─────────────────────────────────────────────── let_underscore_future = "warn" unused_result_ok = "warn" map_err_ignore = "warn" # ── Don't Do Bad Async Stuff ───────────────────────────────────────── await_holding_lock = "warn" await_holding_refcell_ref = "warn" large_futures = "warn" # ── Don't Do Unsafe Things with Memory ──────────────────────────────── mem_forget = "warn" undocumented_unsafe_blocks = "warn" multiple_unsafe_ops_per_block = "warn" # ── Don't Do Bad Things with Numbers ───────────────────────────────── cast_possible_truncation = "warn" cast_precision_loss = "warn" cast_sign_loss = "warn" float_cmp = "warn" float_cmp_const = "warn" lossy_float_literal = "warn" invalid_upcast_comparisons = "warn" # ── Don't Do Bad Things That Are Easy to Avoid ──────────────────────── rc_mutex = "warn" debug_assert_with_mut_call = "warn" iter_not_returning_iterator = "warn" expl_impl_clone_on_copy = "warn" dbg_macro = "warn" # ── Logging hygiene ────────────────────────────────────────────────── print_stdout = "warn" print_stderr = "warn" # ── MSRV ───────────────────────────────────────────────────────────── incompatible_msrv = "warn"