diff --git a/Cargo.lock b/Cargo.lock index 61154ed..2194b23 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,18 +4,18 @@ version = 3 [[package]] name = "addr2line" -version = "0.24.2" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" dependencies = [ "gimli", ] [[package]] -name = "adler2" -version = "2.0.0" +name = "adler" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "ahash" @@ -118,10 +118,13 @@ dependencies = [ ] [[package]] -name = "anyhow" -version = "1.0.91" +name = "atomic" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c042108f3ed77fd83760a5fd79b53be043192bb3b9dba91d8c574c0ada7850c8" +checksum = "8d818003e740b63afc82337e3160717f4f63078720a810b7b903e70a5d1d2994" +dependencies = [ + "bytemuck", +] [[package]] name = "autocfg" @@ -131,17 +134,17 @@ checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "backtrace" -version = "0.3.74" +version = "0.3.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" +checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" dependencies = [ "addr2line", + "cc", "cfg-if", "libc", "miniz_oxide", "object", "rustc-demangle", - "windows-targets", ] [[package]] @@ -180,6 +183,12 @@ version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" +[[package]] +name = "bytemuck" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8334215b81e418a0a7bdb8ef0849474f40bb10c8b71f1c4ed315cff49f32494d" + [[package]] name = "bytes" version = "1.8.0" @@ -213,7 +222,7 @@ dependencies = [ "num-traits", "serde", "wasm-bindgen", - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -256,6 +265,33 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" +[[package]] +name = "color-eyre" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55146f5e46f237f7423d74111267d4597b59b0dad0ffaf7303bce9945d843ad5" +dependencies = [ + "backtrace", + "color-spantrace", + "eyre", + "indenter", + "once_cell", + "owo-colors", + "tracing-error", +] + +[[package]] +name = "color-spantrace" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd6be1b2a7e382e2b98b43b2adcca6bb0e465af0bdd38123873ae61eb17a72c2" +dependencies = [ + "once_cell", + "owo-colors", + "tracing-core", + "tracing-error", +] + [[package]] name = "colorchoice" version = "1.0.2" @@ -287,6 +323,27 @@ dependencies = [ "powerfmt", ] +[[package]] +name = "directories" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a49173b84e034382284f27f1af4dcbbd231ffa358c0fe316541a7337f376a35" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.48.0", +] + [[package]] name = "dyn-clone" version = "1.0.17" @@ -331,12 +388,45 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "eyre" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec" +dependencies = [ + "indenter", + "once_cell", +] + [[package]] name = "fastrand" version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" +[[package]] +name = "figment" +version = "0.10.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cb01cd46b0cf372153850f4c6c272d9cbea2da513e07538405148f95bd789f3" +dependencies = [ + "atomic", + "pear", + "serde", + "toml", + "uncased", + "version_check", +] + +[[package]] +name = "figment_file_provider_adapter" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "353e1e0896797efa3728309408a50d307284d5af4c733fa1859edae2bc80a314" +dependencies = [ + "figment", +] + [[package]] name = "fnv" version = "1.0.7" @@ -469,9 +559,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.31.1" +version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" [[package]] name = "h2" @@ -681,9 +771,12 @@ dependencies = [ name = "immich-tools" version = "0.1.0" dependencies = [ - "anyhow", "chrono", "clap", + "color-eyre", + "directories", + "figment", + "figment_file_provider_adapter", "futures", "log", "pretty_env_logger", @@ -700,6 +793,12 @@ dependencies = [ "vcard4", ] +[[package]] +name = "indenter" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" + [[package]] name = "indexmap" version = "2.6.0" @@ -711,6 +810,12 @@ dependencies = [ "serde", ] +[[package]] +name = "inlinable_string" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8fae54786f62fb2918dcfae3d568594e50eb9b5c25bf04371af6fe7516452fb" + [[package]] name = "ipnet" version = "2.10.1" @@ -761,6 +866,16 @@ version = "0.2.161" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1" +[[package]] +name = "libredox" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags 2.6.0", + "libc", +] + [[package]] name = "linux-raw-sys" version = "0.4.14" @@ -830,11 +945,11 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "miniz_oxide" -version = "0.8.0" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" +checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" dependencies = [ - "adler2", + "adler", ] [[package]] @@ -884,9 +999,9 @@ dependencies = [ [[package]] name = "object" -version = "0.36.5" +version = "0.32.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" dependencies = [ "memchr", ] @@ -952,6 +1067,18 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + +[[package]] +name = "owo-colors" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" + [[package]] name = "parking_lot" version = "0.12.3" @@ -972,7 +1099,30 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-targets", + "windows-targets 0.52.6", +] + +[[package]] +name = "pear" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdeeaa00ce488657faba8ebf44ab9361f9365a97bd39ffb8a60663f57ff4b467" +dependencies = [ + "inlinable_string", + "pear_codegen", + "yansi", +] + +[[package]] +name = "pear_codegen" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bab5b985dc082b345f812b7df84e1bef27e7207b39e448439ba8bd69c93f147" +dependencies = [ + "proc-macro2", + "proc-macro2-diagnostics", + "quote", + "syn", ] [[package]] @@ -1034,6 +1184,19 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "proc-macro2-diagnostics" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "version_check", + "yansi", +] + [[package]] name = "progenitor" version = "0.8.0" @@ -1118,6 +1281,17 @@ dependencies = [ "bitflags 2.6.0", ] +[[package]] +name = "redox_users" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" +dependencies = [ + "getrandom", + "libredox", + "thiserror", +] + [[package]] name = "regex" version = "1.11.0" @@ -1397,6 +1571,15 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_spanned" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" +dependencies = [ + "serde", +] + [[package]] name = "serde_tokenstream" version = "0.2.2" @@ -1434,6 +1617,15 @@ dependencies = [ "unsafe-libyaml", ] +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + [[package]] name = "shlex" version = "1.3.0" @@ -1575,6 +1767,16 @@ dependencies = [ "syn", ] +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if", + "once_cell", +] + [[package]] name = "time" version = "0.3.36" @@ -1684,6 +1886,40 @@ dependencies = [ "tokio", ] +[[package]] +name = "toml" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.22.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + [[package]] name = "tower-service" version = "0.3.3" @@ -1707,6 +1943,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" dependencies = [ "once_cell", + "valuable", +] + +[[package]] +name = "tracing-error" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d686ec1c0f384b1277f097b2f279a2ecc11afe8c133c1aabf036a27cb4cd206e" +dependencies = [ + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +dependencies = [ + "sharded-slab", + "thread_local", + "tracing-core", ] [[package]] @@ -1762,6 +2020,15 @@ dependencies = [ "typify-impl", ] +[[package]] +name = "uncased" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1b88fcfe09e89d3866a5c11019378088af2d24c3fbd4f0543f96b479ec90697" +dependencies = [ + "version_check", +] + [[package]] name = "unicode-bidi" version = "0.3.17" @@ -1838,6 +2105,12 @@ dependencies = [ "serde", ] +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + [[package]] name = "vcard4" version = "0.5.2" @@ -1986,7 +2259,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -1997,7 +2270,7 @@ checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0" dependencies = [ "windows-result", "windows-strings", - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -2006,7 +2279,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -2016,7 +2289,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" dependencies = [ "windows-result", - "windows-targets", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", ] [[package]] @@ -2025,7 +2307,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -2034,7 +2316,22 @@ version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", ] [[package]] @@ -2043,28 +2340,46 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + [[package]] name = "windows_i686_gnu" version = "0.52.6" @@ -2077,30 +2392,69 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "winnow" +version = "0.6.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" +dependencies = [ + "memchr", +] + +[[package]] +name = "yansi" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" + [[package]] name = "zerocopy" version = "0.7.35" diff --git a/Cargo.toml b/Cargo.toml index 21a5a54..f9d163f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,9 +9,12 @@ categories = ["command-line-utilities"] keywords = ["immich"] [dependencies] -anyhow = "1.0.91" chrono = { version = "0.4.38", features = ["serde"] } clap = { version = "4.5.20", features = ["derive"] } +color-eyre = "0.6.3" +directories = "5.0.1" +figment = { version = "0.10.19", features = ["env", "toml"] } +figment_file_provider_adapter = "0.1.1" futures = "0.3.31" log = "0.4.22" pretty_env_logger = "0.5.0" diff --git a/README.md b/README.md index be56fdc..cce6560 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,32 @@ Some other features are planned, mainly around using external libraries. Feature ideas and contributions welcome. +## Configuration + +Different options need to be passed to this tool, mainly: + + - `server_url`: the API endpoint of the Immich instance to work on + - `api_key`: the API key to use + +These can be passed through different mechanisms: + + - Command line arguments, e.g. `immich-tools --server-url https://photos.example.com/api --api-key api-key-goes-here server version` + - Environment variables prefixed with `IMMICH_TOOLS_`, e.g. `IMMICH_TOOLS_SERVER_URL=https://photos.example.com/api IMMICH_TOOLS_API_KEY=api-key-goes-here immich-tools server version` + - Environment variables pointing to files, suffixed with `_FILE`, e.g. `IMMICHTOOLS_SERVER_URL_FILE=~/immich-url.txt IMMICH_TOOLS_API_KEY_FILE=~/immich-api-key.txt immich-tools server version`. This is mostly useful for secrets. + - Through a configuration file, in a well-known, OS-dependent location (on Linux, `~/.config/immichtools/config.toml`, on macOS, `~/Library/Application Support/fr.enoent.Immich-Tools/config.toml`, and on Windows `~\AppData\Roaming\enoent\Immich Tools\config\config.toml`). Note that keys in this file can also be suffixed with `_file` and point to a file containing the value, instead of the value directly. + +Command line arguments take precedence over environment variables, which in turn take precedence over the configuration file. + +### Example configuration file + +```toml +server_url = "https://photos.example.com/api" +api_key_file = "/home/example/.config/immichtools/apikey" + +[people.sync_date_of_births] +vcard = "/home/example/contacts.vcf" +``` + ## Immich API bindings The bindings are generated automatically from `src/immich-openapi-specs.json`. The file currently in the repo has been generated with Immich v1.118.0, with a few methods removed around file upload (Progenitor doesn't yet support `multipart/form-data` content types, see [here](https://github.com/oxidecomputer/progenitor/issues/518)). All other methods should be available. diff --git a/src/args.rs b/src/args.rs index d40696f..f2c294b 100644 --- a/src/args.rs +++ b/src/args.rs @@ -1,51 +1,66 @@ use std::path::PathBuf; use clap::{command, Parser, Subcommand}; +use serde::Serialize; -#[derive(Parser)] +#[derive(Parser, Serialize)] #[command(version, about, long_about = None)] pub(crate) struct Opts { + /// The Immich API URL - it should probably end in "/api" #[arg(short, long)] - pub server_url: String, + #[serde(skip_serializing_if = "::std::option::Option::is_none")] + pub server_url: Option, + /// The Immich API key #[arg(short, long)] - pub api_key: String, + #[serde(skip_serializing_if = "::std::option::Option::is_none")] + pub api_key: Option, + /// If enabled, actions that would have been performed are only logged #[arg(short, long)] pub dry_run: bool, #[command(subcommand)] + #[serde(flatten)] pub command: Commands, } -#[derive(Subcommand)] +#[derive(Serialize, Subcommand)] pub(crate) enum Commands { /// People related commands + #[serde(rename = "people")] People { #[command(subcommand)] + #[serde(flatten)] people_command: PeopleCommands, }, /// Server related commands + #[serde(rename = "server")] Server { #[command(subcommand)] + #[serde(flatten)] server_command: ServerCommands, }, } -#[derive(Subcommand)] +#[derive(Serialize, Subcommand)] pub(crate) enum PeopleCommands { /// Synchronises date of births from a vcard file + #[serde(rename = "sync_date_of_births")] SyncDateOfBirths { #[arg(short, long)] - vcard_file: PathBuf, + #[serde(skip_serializing_if = "::std::option::Option::is_none")] + vcard: Option, }, /// Lists the people without date of birth + #[serde(rename = "missing_date_of_births")] MissingDateOfBirths {}, } -#[derive(Subcommand)] +#[derive(Serialize, Subcommand)] pub(crate) enum ServerCommands { /// Fetches the version of the server + #[serde(rename = "version")] Version {}, } diff --git a/src/commands/missing_date_of_birth.rs b/src/commands/missing_date_of_birth.rs index 94fd825..14be2b4 100644 --- a/src/commands/missing_date_of_birth.rs +++ b/src/commands/missing_date_of_birth.rs @@ -1,5 +1,5 @@ use crate::{utils::people::fetch_all_contacts, Client}; -use anyhow::Result; +use color_eyre::eyre::Result; use log::*; pub async fn missing_date_of_birth(client: &Client) -> Result<()> { diff --git a/src/commands/server_version.rs b/src/commands/server_version.rs index 75ae0bd..501ad7c 100644 --- a/src/commands/server_version.rs +++ b/src/commands/server_version.rs @@ -1,4 +1,4 @@ -use anyhow::Result; +use color_eyre::eyre::Result; use crate::Client; diff --git a/src/commands/sync_date_of_birth.rs b/src/commands/sync_date_of_birth.rs index 7689814..7b48abb 100644 --- a/src/commands/sync_date_of_birth.rs +++ b/src/commands/sync_date_of_birth.rs @@ -1,7 +1,7 @@ use std::{fs, path::PathBuf}; -use anyhow::Result; use chrono::{Datelike, NaiveDate}; +use color_eyre::eyre::{ContextCompat, Result}; use log::*; use uuid::Uuid; use vcard4::{ @@ -81,17 +81,15 @@ pub async fn sync_date_of_birth( fn vcard_date_to_naive_date(src: DateAndOrTime) -> Result { match src { DateAndOrTime::Date(date) => { - Ok( - NaiveDate::from_ymd_opt(date.year(), date.month() as u32, date.day().into()) - .expect(&format!("Unable to parse {}", date)), - ) + NaiveDate::from_ymd_opt(date.year(), date.month() as u32, date.day().into()) + .wrap_err("Unable to parse a date") } - DateAndOrTime::DateTime(datetime) => Ok(NaiveDate::from_ymd_opt( + DateAndOrTime::DateTime(datetime) => NaiveDate::from_ymd_opt( datetime.year(), datetime.month() as u32, datetime.day().into(), ) - .expect(&format!("Unable to parse {}", datetime))), + .wrap_err("Unable to parse a datetime"), DateAndOrTime::Time(_time) => todo!(), } } diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..2130a02 --- /dev/null +++ b/src/config.rs @@ -0,0 +1,21 @@ +use std::path::PathBuf; + +use serde::{Deserialize, Serialize}; + +#[derive(Deserialize, Serialize)] +pub(crate) struct Config { + pub(crate) server_url: String, + pub(crate) api_key: String, + + pub(crate) people: People, +} + +#[derive(Deserialize, Serialize)] +pub(crate) struct People { + pub(crate) sync_date_of_births: SyncDateOfBirths, +} + +#[derive(Deserialize, Serialize)] +pub(crate) struct SyncDateOfBirths { + pub(crate) vcard: PathBuf, +} diff --git a/src/main.rs b/src/main.rs index f43deb0..53bfa54 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,41 +1,66 @@ include!(concat!(env!("OUT_DIR"), "/codegen.rs")); use crate::args::Commands; -use anyhow::Result; use args::{PeopleCommands, ServerCommands}; use clap::Parser; +use color_eyre::eyre::Result; use commands::missing_date_of_birth::missing_date_of_birth; use commands::server_version::server_version; use commands::sync_date_of_birth::sync_date_of_birth; +use config::Config; +use directories::ProjectDirs; +use figment::providers::{Env, Format, Serialized, Toml}; +use figment::Figment; +use figment_file_provider_adapter::FileAdapter; +use log::*; use reqwest::header; mod args; mod commands; +mod config; mod utils; #[tokio::main] -async fn main() { +async fn main() -> Result<()> { + color_eyre::install()?; pretty_env_logger::init(); + let mut conf_extractor = Figment::new(); + if let Some(project_dirs) = ProjectDirs::from("fr", "enoent", "Immich Tools") { + let config_file_path = project_dirs.config_dir().join("config.toml"); + if config_file_path.exists() { + debug!("Reading configuration from {:?}", config_file_path); + conf_extractor = + conf_extractor.merge(FileAdapter::wrap(Toml::file_exact(config_file_path))); + } + } else { + warn!("Unable to determine configuration file path"); + } + let args = args::Opts::parse(); - let client = get_client(&args.server_url, &args.api_key).unwrap(); + let conf: Config = conf_extractor + .merge(FileAdapter::wrap(Env::prefixed("IMMICH_TOOLS_"))) + .merge(Serialized::defaults(&args)) + .extract()?; - let res = match &args.command { + let client = get_client(&conf.server_url, &conf.api_key)?; + + match &args.command { Commands::People { people_command } => match people_command { PeopleCommands::MissingDateOfBirths {} => missing_date_of_birth(&client).await, - PeopleCommands::SyncDateOfBirths { vcard_file } => { - sync_date_of_birth(&client, vcard_file, args.dry_run).await + PeopleCommands::SyncDateOfBirths { vcard: _ } => { + sync_date_of_birth( + &client, + &conf.people.sync_date_of_births.vcard, + args.dry_run, + ) + .await } }, Commands::Server { server_command } => match server_command { ServerCommands::Version {} => server_version(&client).await, }, - }; - - match res { - Ok(_) => {} - Err(e) => println!("Error: {:?}", e), } } diff --git a/src/utils/people.rs b/src/utils/people.rs index 039e9e2..3fdc1a0 100644 --- a/src/utils/people.rs +++ b/src/utils/people.rs @@ -1,5 +1,5 @@ use crate::{types::PersonResponseDto, Client}; -use anyhow::Result; +use color_eyre::eyre::Result; pub async fn fetch_all_contacts(client: &Client) -> Result> { let mut all_people = Vec::new();