diff --git a/Cargo.lock b/Cargo.lock index aeff650..a92f667 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,7 +8,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f7b0a21988c1bf877cf4759ef5ddaac04c1c9fe808c9142ecb78ba97d97a28a" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.10.0", "bytes", "futures-core", "futures-sink", @@ -21,16 +21,15 @@ dependencies = [ [[package]] name = "actix-http" -version = "3.11.0" +version = "3.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44dfe5c9e0004c623edc65391dfd51daa201e7e30ebd9c9bedf873048ec32bc2" +checksum = "7926860314cbe2fb5d1f13731e387ab43bd32bca224e82e6e2db85de0a3dba49" dependencies = [ "actix-codec", "actix-rt", "actix-service", "actix-utils", - "base64 0.22.1", - "bitflags 2.9.1", + "bitflags 2.10.0", "bytes", "bytestring", "derive_more", @@ -42,12 +41,9 @@ dependencies = [ "httpdate", "itoa", "language-tags", - "local-channel", "mime", "percent-encoding", "pin-project-lite", - "rand 0.9.2", - "sha1", "smallvec", "tokio", "tokio-util", @@ -70,9 +66,9 @@ dependencies = [ [[package]] name = "actix-rt" -version = "2.10.0" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24eda4e2a6e042aa4e55ac438a2ae052d3b5da0ecf83d7411e1a368946925208" +checksum = "92589714878ca59a7626ea19734f0e07a6a875197eec751bb5d3f99e64998c63" dependencies = [ "futures-core", "tokio", @@ -117,9 +113,9 @@ dependencies = [ [[package]] name = "actix-web" -version = "4.11.0" +version = "4.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a597b77b5c6d6a1e1097fddde329a83665e25c5437c696a3a9a4aa514a614dea" +checksum = "1654a77ba142e37f049637a3e5685f864514af11fcbc51cb51eb6596afe5b8d6" dependencies = [ "actix-codec", "actix-http", @@ -148,7 +144,7 @@ dependencies = [ "serde_json", "serde_urlencoded", "smallvec", - "socket2 0.5.10", + "socket2 0.6.1", "time", "tracing", "url", @@ -156,9 +152,9 @@ dependencies = [ [[package]] name = "addr2line" -version = "0.24.2" +version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" +checksum = "1b5d307320b3181d6d7954e663bd7c774a838b8220fe0593c86d9fb09f498b4b" dependencies = [ "gimli", ] @@ -183,9 +179,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" dependencies = [ "memchr", ] @@ -196,12 +192,6 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" -[[package]] -name = "android-tzdata" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" - [[package]] name = "android_system_properties" version = "0.1.5" @@ -213,9 +203,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.19" +version = "0.6.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "301af1932e46185686725e0fad2f8f2aa7da69dd70bf6ecc44d6b703844a3933" +checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" dependencies = [ "anstyle", "anstyle-parse", @@ -228,9 +218,9 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.11" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" +checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" [[package]] name = "anstyle-parse" @@ -243,44 +233,29 @@ dependencies = [ [[package]] name = "anstyle-query" -version = "1.1.3" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8bdeb6047d8983be085bab0ba1472e6dc604e7041dbf6fcd5e71523014fae9" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] name = "anstyle-wincon" -version = "3.0.9" +version = "3.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "403f75924867bb1033c59fbf0797484329750cfbe3c4325cd33127941fabc882" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] name = "anyhow" -version = "1.0.98" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" - -[[package]] -name = "arrayvec" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" - -[[package]] -name = "ascii-canvas" -version = "3.0.0" +version = "1.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8824ecca2e851cec16968d54a01dd372ef8f95b244fb84b84e70128be347c3c6" -dependencies = [ - "term 0.7.0", -] +checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" [[package]] name = "assert-json-diff" @@ -294,179 +269,41 @@ dependencies = [ [[package]] name = "ast_node" -version = "3.0.4" +version = "5.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a184645bcc6f52d69d8e7639720699c6a99efb711f886e251ed1d16db8dd90e" +checksum = "2eb025ef00a6da925cf40870b9c8d008526b6004ece399cb0974209720f0b194" dependencies = [ "quote", "swc_macros_common", "syn", ] -[[package]] -name = "async-channel" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" -dependencies = [ - "concurrent-queue", - "event-listener 2.5.3", - "futures-core", -] - -[[package]] -name = "async-channel" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2" -dependencies = [ - "concurrent-queue", - "event-listener-strategy", - "futures-core", - "pin-project-lite", -] - -[[package]] -name = "async-executor" -version = "1.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb812ffb58524bdd10860d7d974e2f01cc0950c2438a74ee5ec2e2280c6c4ffa" -dependencies = [ - "async-task", - "concurrent-queue", - "fastrand 2.3.0", - "futures-lite 2.6.1", - "pin-project-lite", - "slab", -] - -[[package]] -name = "async-global-executor" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05b1b633a2115cd122d73b955eadd9916c18c8f510ec9cd1686404c60ad1c29c" -dependencies = [ - "async-channel 2.5.0", - "async-executor", - "async-io", - "async-lock", - "blocking", - "futures-lite 2.6.1", - "once_cell", -] - -[[package]] -name = "async-io" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19634d6336019ef220f09fd31168ce5c184b295cbf80345437cc36094ef223ca" -dependencies = [ - "async-lock", - "cfg-if", - "concurrent-queue", - "futures-io", - "futures-lite 2.6.1", - "parking", - "polling 3.10.0", - "rustix", - "slab", - "windows-sys 0.60.2", -] - [[package]] name = "async-lock" -version = "3.4.1" +version = "3.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fd03604047cee9b6ce9de9f70c6cd540a0520c813cbd49bae61f33ab80ed1dc" +checksum = "290f7f2596bd5b78a9fec8088ccd89180d7f9f55b94b0576823bbbdc72ee8311" dependencies = [ - "event-listener 5.4.1", + "event-listener", "event-listener-strategy", "pin-project-lite", ] [[package]] name = "async-object-pool" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "333c456b97c3f2d50604e8b2624253b7f787208cb72eb75e64b0ad11b221652c" -dependencies = [ - "async-std", -] - -[[package]] -name = "async-process" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65daa13722ad51e6ab1a1b9c01299142bc75135b337923cfa10e79bbbd669f00" -dependencies = [ - "async-channel 2.5.0", - "async-io", - "async-lock", - "async-signal", - "async-task", - "blocking", - "cfg-if", - "event-listener 5.4.1", - "futures-lite 2.6.1", - "rustix", -] - -[[package]] -name = "async-signal" -version = "0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f567af260ef69e1d52c2b560ce0ea230763e6fbb9214a85d768760a920e3e3c1" -dependencies = [ - "async-io", - "async-lock", - "atomic-waker", - "cfg-if", - "futures-core", - "futures-io", - "rustix", - "signal-hook-registry", - "slab", - "windows-sys 0.60.2", -] - -[[package]] -name = "async-std" -version = "1.13.1" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "730294c1c08c2e0f85759590518f6333f0d5a0a766a27d519c1b244c3dfd8a24" +checksum = "e1ac0219111eb7bb7cb76d4cf2cb50c598e7ae549091d3616f9e95442c18486f" dependencies = [ - "async-channel 1.9.0", - "async-global-executor", - "async-io", "async-lock", - "async-process", - "crossbeam-utils", - "futures-channel", - "futures-core", - "futures-io", - "futures-lite 2.6.1", - "gloo-timers", - "kv-log-macro", - "log", - "memchr", - "once_cell", - "pin-project-lite", - "pin-utils", - "slab", - "wasm-bindgen-futures", + "event-listener", ] -[[package]] -name = "async-task" -version = "4.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" - [[package]] name = "async-trait" -version = "0.1.88" +version = "0.1.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", @@ -487,9 +324,9 @@ checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "backtrace" -version = "0.3.75" +version = "0.3.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" +checksum = "bb531853791a215d7c62a30daf0dde835f381ab5de4589cfe7c649d2cbe92bd6" dependencies = [ "addr2line", "cfg-if", @@ -497,7 +334,7 @@ dependencies = [ "miniz_oxide", "object", "rustc-demangle", - "windows-targets 0.52.6", + "windows-link", ] [[package]] @@ -514,20 +351,9 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "base64ct" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" - -[[package]] -name = "basic-cookies" -version = "0.1.5" +version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67bd8fd42c16bdb08688243dc5f0cc117a3ca9efeeaba3a345a18a6159ad96f7" -dependencies = [ - "lalrpop", - "lalrpop-util", - "regex", -] +checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" [[package]] name = "better_scoped_tls" @@ -538,21 +364,6 @@ dependencies = [ "scoped-tls", ] -[[package]] -name = "bit-set" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" -dependencies = [ - "bit-vec", -] - -[[package]] -name = "bit-vec" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" - [[package]] name = "bitflags" version = "1.3.2" @@ -561,9 +372,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.9.1" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" +checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" [[package]] name = "block-buffer" @@ -575,23 +386,19 @@ dependencies = [ ] [[package]] -name = "blocking" -version = "1.6.2" +name = "block2" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e83f8d02be6967315521be875afa792a316e28d57b5a2d401897e2a7921b7f21" +checksum = "cdeb9d870516001442e364c5220d3574d2da8dc765554b4a617230d33fa58ef5" dependencies = [ - "async-channel 2.5.0", - "async-task", - "futures-io", - "futures-lite 2.6.1", - "piper", + "objc2", ] [[package]] name = "bumpalo" -version = "3.19.0" +version = "3.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" +checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" [[package]] name = "byteorder" @@ -601,9 +408,12 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.10.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" +checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" +dependencies = [ + "serde", +] [[package]] name = "bytes-str" @@ -617,52 +427,54 @@ dependencies = [ [[package]] name = "bytestring" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e465647ae23b2823b0753f50decb2d5a86d2bb2cac04788fafd1f80e45378e5f" +checksum = "113b4343b5f6617e7ad401ced8de3cc8b012e73a594347c307b90db3e9271289" dependencies = [ "bytes", ] -[[package]] -name = "castaway" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2698f953def977c68f935bb0dfa959375ad4638570e969e2f1e9f433cbf1af6" - [[package]] name = "cc" -version = "1.2.31" +version = "1.2.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3a42d84bb6b69d3a8b3eaacf0d88f179e1929695e1ad012b6cf64d9caaa5fd2" +checksum = "cd4932aefd12402b36c60956a4fe0035421f544799057659ff86f923657aada3" dependencies = [ + "find-msvc-tools", "shlex", ] [[package]] name = "cfg-if" -version = "1.0.1" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "cfg_aliases" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "chrono" -version = "0.4.41" +version = "0.4.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d" +checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2" dependencies = [ - "android-tzdata", "iana-time-zone", + "js-sys", "num-traits", "serde", + "wasm-bindgen", "windows-link", ] [[package]] name = "clap" -version = "4.5.42" +version = "4.5.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed87a9d530bb41a67537289bafcac159cb3ee28460e0a4571123d2a778a6a882" +checksum = "c6e6ff9dcd79cff5cd969a17a545d79e84ab086e444102a591e288a8aa3ce394" dependencies = [ "clap_builder", "clap_derive", @@ -679,9 +491,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.42" +version = "4.5.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64f4f3f3c77c94aff3c7e9aac9a2ca1974a5adf392a8bb751e827d6d127ab966" +checksum = "fa42cf4d2b7a41bc8f663a7cab4031ebafa1bf3875705bfaf8466dc60ab52c00" dependencies = [ "anstream", "anstyle", @@ -691,9 +503,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.41" +version = "4.5.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef4f52386a59ca4c860f7393bcf8abd8dfd91ecccc0f774635ff68e92eeef491" +checksum = "2a0b5487afeab2deb2ff4e03a807ad1a03ac532ff5a2cee5d86884440c7f7671" dependencies = [ "heck", "proc-macro2", @@ -703,9 +515,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.7.5" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" +checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d" [[package]] name = "colorchoice" @@ -715,11 +527,10 @@ checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" [[package]] name = "colored" -version = "2.2.0" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c" +checksum = "fde0e0ec90c9dfb3b4b1a0891a7dcd0e2bffde2f7efed5fe7c9bb00e5bfb915e" dependencies = [ - "lazy_static", "windows-sys 0.59.0", ] @@ -734,15 +545,24 @@ dependencies = [ [[package]] name = "console" -version = "0.16.0" +version = "0.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e09ced7ebbccb63b4c65413d821f2e00ce54c5ca4514ddc6b3c892fdbcbc69d" +checksum = "03e45a4a8926227e4197636ba97a9fc9b00477e9f4bd711395687c5f0734bec4" dependencies = [ "encode_unicode", "libc", "once_cell", - "unicode-width 0.2.1", - "windows-sys 0.60.2", + "unicode-width 0.2.2", + "windows-sys 0.61.2", +] + +[[package]] +name = "convert_case" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9" +dependencies = [ + "unicode-segmentation", ] [[package]] @@ -795,17 +615,11 @@ version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" -[[package]] -name = "crunchy" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" - [[package]] name = "crypto-common" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" dependencies = [ "generic-array", "typenum", @@ -813,73 +627,65 @@ dependencies = [ [[package]] name = "csv" -version = "1.3.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acdc4883a9c96732e4733212c01447ebd805833b7275a73ca3ee080fd77afdaf" +checksum = "52cd9d68cf7efc6ddfaaee42e7288d3a99d613d4b50f76ce9827ae0c6e14f938" dependencies = [ "csv-core", "itoa", "ryu", - "serde", + "serde_core", ] [[package]] name = "csv-core" -version = "0.1.12" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d02f3b0da4c6504f86e9cd789d8dbafab48c2321be74e9987593de5a894d93d" +checksum = "704a3c26996a80471189265814dbc2c257598b96b8a7feae2d31ace646bb9782" dependencies = [ "memchr", ] [[package]] -name = "curl" -version = "0.4.48" +name = "darling" +version = "0.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e2d5c8f48d9c0c23250e52b55e82a6ab4fdba6650c931f5a0a57a43abda812b" +checksum = "9cdf337090841a411e2a7f3deb9187445851f91b309c0c0a29e05f74a00a48c0" dependencies = [ - "curl-sys", - "libc", - "openssl-probe", - "openssl-sys", - "schannel", - "socket2 0.5.10", - "windows-sys 0.59.0", + "darling_core 0.21.3", + "darling_macro 0.21.3", ] [[package]] -name = "curl-sys" -version = "0.4.82+curl-8.14.1" +name = "darling" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4d63638b5ec65f1a4ae945287b3fd035be4554bbaf211901159c9a2a74fb5be" +checksum = "25ae13da2f202d56bd7f91c25fba009e7717a1e4a1cc98a76d844b65ae912e9d" dependencies = [ - "cc", - "libc", - "libnghttp2-sys", - "libz-sys", - "openssl-sys", - "pkg-config", - "vcpkg", - "windows-sys 0.59.0", + "darling_core 0.23.0", + "darling_macro 0.23.0", ] [[package]] -name = "darling" -version = "0.20.11" +name = "darling_core" +version = "0.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" +checksum = "1247195ecd7e3c85f83c8d2a366e4210d588e802133e1e355180a9870b517ea4" dependencies = [ - "darling_core", - "darling_macro", + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn", ] [[package]] name = "darling_core" -version = "0.20.11" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" +checksum = "9865a50f7c335f53564bb694ef660825eb8610e0a53d3e11bf1b0d3df31e03b0" dependencies = [ - "fnv", "ident_case", "proc-macro2", "quote", @@ -889,20 +695,31 @@ dependencies = [ [[package]] name = "darling_macro" -version = "0.20.11" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" +dependencies = [ + "darling_core 0.21.3", + "quote", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" +checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" dependencies = [ - "darling_core", + "darling_core 0.23.0", "quote", "syn", ] [[package]] name = "data-encoding" -version = "2.9.0" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476" +checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea" [[package]] name = "debugid" @@ -926,31 +743,33 @@ dependencies = [ [[package]] name = "deranged" -version = "0.4.0" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" +checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" dependencies = [ "powerfmt", - "serde", + "serde_core", ] [[package]] name = "derive_more" -version = "2.0.1" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678" +checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134" dependencies = [ "derive_more-impl", ] [[package]] name = "derive_more-impl" -version = "2.0.1" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" +checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" dependencies = [ + "convert_case", "proc-macro2", "quote", + "rustc_version", "syn", "unicode-xid", ] @@ -993,7 +812,7 @@ dependencies = [ "libc", "option-ext", "redox_users 0.5.2", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -1007,6 +826,16 @@ dependencies = [ "winapi", ] +[[package]] +name = "dispatch2" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89a09f22a6c6069a18470eb92d2298acf25463f14256d24778e1230d789a2aec" +dependencies = [ + "bitflags 2.10.0", + "objc2", +] + [[package]] name = "displaydoc" version = "0.2.5" @@ -1030,15 +859,6 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" -[[package]] -name = "ena" -version = "0.14.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d248bdd43ce613d87415282f69b9bb99d947d290b10962dd6c56233312c2ad5" -dependencies = [ - "log", -] - [[package]] name = "encode_unicode" version = "1.0.0" @@ -1072,20 +892,14 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "errno" -version = "0.3.13" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] -[[package]] -name = "event-listener" -version = "2.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" - [[package]] name = "event-listener" version = "5.4.1" @@ -1103,24 +917,21 @@ version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" dependencies = [ - "event-listener 5.4.1", + "event-listener", "pin-project-lite", ] [[package]] name = "fastrand" -version = "1.9.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" -dependencies = [ - "instant", -] +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] -name = "fastrand" -version = "2.3.0" +name = "find-msvc-tools" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" +checksum = "f449e6c6c08c865631d4890cfacf252b3d396c9bcc83adb6623cdb02a8336c41" [[package]] name = "findshlibs" @@ -1134,12 +945,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "fixedbitset" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" - [[package]] name = "fnv" version = "1.0.7" @@ -1169,18 +974,18 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] name = "form_urlencoded" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" dependencies = [ "percent-encoding", ] [[package]] name = "from_variant" -version = "2.0.2" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "308530a56b099da144ebc5d8e179f343ad928fa2b3558d1eb3db9af18d6eff43" +checksum = "e5ff35a391aef949120a0340d690269b3d9f63460a6106e99bd07b961f345ea9" dependencies = [ "swc_macros_common", "syn", @@ -1235,38 +1040,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] -name = "futures-lite" -version = "1.13.0" +name = "futures-macro" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" -dependencies = [ - "fastrand 1.9.0", - "futures-core", - "futures-io", - "memchr", - "parking", - "pin-project-lite", - "waker-fn", -] - -[[package]] -name = "futures-lite" -version = "2.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" -dependencies = [ - "fastrand 2.3.0", - "futures-core", - "futures-io", - "parking", - "pin-project-lite", -] - -[[package]] -name = "futures-macro" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", @@ -1285,6 +1062,12 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" +[[package]] +name = "futures-timer" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24" + [[package]] name = "futures-util" version = "0.3.31" @@ -1315,50 +1098,38 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" dependencies = [ "cfg-if", "libc", - "wasi 0.11.1+wasi-snapshot-preview1", + "wasi", ] [[package]] name = "getrandom" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", "libc", "r-efi", - "wasi 0.14.2+wasi-0.2.4", + "wasip2", ] [[package]] name = "gimli" -version = "0.31.1" +version = "0.32.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" +checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" [[package]] name = "glob" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" - -[[package]] -name = "gloo-timers" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbb143cf96099802033e0d4f4963b19fd2e0b728bcf076cd9cf7f6634f092994" -dependencies = [ - "futures-channel", - "futures-core", - "js-sys", - "wasm-bindgen", -] +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" [[package]] name = "h2" @@ -1372,7 +1143,7 @@ dependencies = [ "futures-sink", "futures-util", "http 0.2.12", - "indexmap 2.10.0", + "indexmap 2.13.0", "slab", "tokio", "tokio-util", @@ -1381,17 +1152,17 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.11" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17da50a276f1e01e0ba6c029e47b7100754904ee8a278f886546e98575380785" +checksum = "2f44da3a8150a6703ed5d34e164b875fd14c2cdab9af1252a9a1020bde2bdc54" dependencies = [ "atomic-waker", "bytes", "fnv", "futures-core", "futures-sink", - "http 1.3.1", - "indexmap 2.10.0", + "http 1.4.0", + "indexmap 2.13.0", "slab", "tokio", "tokio-util", @@ -1416,9 +1187,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.4" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" [[package]] name = "headers" @@ -1428,13 +1199,28 @@ checksum = "06683b93020a07e3dbcf5f8c0f6d40080d725bea7936fc01ad345c01b97dc270" dependencies = [ "base64 0.21.7", "bytes", - "headers-core", + "headers-core 0.2.0", "http 0.2.12", "httpdate", "mime", "sha1", ] +[[package]] +name = "headers" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3314d5adb5d94bcdf56771f2e50dbbc80bb4bdf88967526706205ac9eff24eb" +dependencies = [ + "base64 0.22.1", + "bytes", + "headers-core 0.3.0", + "http 1.4.0", + "httpdate", + "mime", + "sha1", +] + [[package]] name = "headers-core" version = "0.2.0" @@ -1444,6 +1230,15 @@ dependencies = [ "http 0.2.12", ] +[[package]] +name = "headers-core" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54b4a22553d4242c49fddb9ba998a99962b5cc6f22cb5a3482bec22522403ce4" +dependencies = [ + "http 1.4.0", +] + [[package]] name = "heck" version = "0.5.0" @@ -1464,9 +1259,9 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "hostname" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a56f203cd1c76362b69e3863fd987520ac36cf70a8c92627449b2f64a8cf7d65" +checksum = "617aaa3557aef3810a6369d0a99fac8a080891b68bd9f9812a1eeda0c0730cbd" dependencies = [ "cfg-if", "libc", @@ -1475,14 +1270,15 @@ dependencies = [ [[package]] name = "hstr" -version = "2.1.0" +version = "3.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31f11d91d7befd2ffd9d216e9e5ea1fae6174b20a2a1b67a688138003d2f4122" +checksum = "0c43c0a9e8fbdb3bb9dc8eee85e1e2ac81605418b4c83b6b7413cbf14d56ca5c" dependencies = [ "hashbrown 0.14.5", "new_debug_unreachable", "once_cell", "rustc-hash", + "serde", "triomphe", ] @@ -1499,12 +1295,11 @@ dependencies = [ [[package]] name = "http" -version = "1.3.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" +checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" dependencies = [ "bytes", - "fnv", "itoa", ] @@ -1535,7 +1330,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", - "http 1.3.1", + "http 1.4.0", ] [[package]] @@ -1546,7 +1341,7 @@ checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" dependencies = [ "bytes", "futures-core", - "http 1.3.1", + "http 1.4.0", "http-body 1.0.1", "pin-project-lite", ] @@ -1565,29 +1360,35 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "httpmock" -version = "0.6.8" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b02e044d3b4c2f94936fb05f9649efa658ca788f44eb6b87554e2033fc8ce93" +checksum = "511f510e9b1888d67f10bab4397f8b019d2a9b249a2c10acbce2d705b1b32e26" dependencies = [ "assert-json-diff", "async-object-pool", "async-trait", - "base64 0.21.7", - "basic-cookies", + "base64 0.22.1", + "bytes", "crossbeam-utils", "form_urlencoded", + "futures-timer", "futures-util", - "hyper 0.14.32", - "isahc", - "lazy_static", - "levenshtein", - "log", + "headers 0.4.1", + "http 1.4.0", + "http-body-util", + "hyper 1.8.1", + "hyper-util", + "path-tree", "regex", "serde", "serde_json", "serde_regex", "similar", + "stringmetrics", + "tabwriter", + "thiserror 2.0.17", "tokio", + "tracing", "url", ] @@ -1617,19 +1418,22 @@ dependencies = [ [[package]] name = "hyper" -version = "1.6.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" +checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" dependencies = [ + "atomic-waker", "bytes", "futures-channel", - "futures-util", - "h2 0.4.11", - "http 1.3.1", + "futures-core", + "h2 0.4.13", + "http 1.4.0", "http-body 1.0.1", "httparse", + "httpdate", "itoa", "pin-project-lite", + "pin-utils", "smallvec", "tokio", "want", @@ -1641,8 +1445,8 @@ version = "0.27.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" dependencies = [ - "http 1.3.1", - "hyper 1.6.0", + "http 1.4.0", + "hyper 1.8.1", "hyper-util", "rustls", "rustls-pki-types", @@ -1659,7 +1463,7 @@ checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" dependencies = [ "bytes", "http-body-util", - "hyper 1.6.0", + "hyper 1.8.1", "hyper-util", "native-tls", "tokio", @@ -1669,23 +1473,23 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.16" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d9b05277c7e8da2c93a568989bb6207bef0112e8d17df7a6eda4a3cf143bc5e" +checksum = "727805d60e7938b76b826a6ef209eb70eaa1812794f9424d4a4e2d740662df5f" dependencies = [ "base64 0.22.1", "bytes", "futures-channel", "futures-core", "futures-util", - "http 1.3.1", + "http 1.4.0", "http-body 1.0.1", - "hyper 1.6.0", + "hyper 1.8.1", "ipnet", "libc", "percent-encoding", "pin-project-lite", - "socket2 0.6.0", + "socket2 0.6.1", "system-configuration", "tokio", "tower-service", @@ -1695,9 +1499,9 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.63" +version = "0.1.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" +checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -1719,9 +1523,9 @@ dependencies = [ [[package]] name = "icu_collections" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" +checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" dependencies = [ "displaydoc", "potential_utf", @@ -1732,9 +1536,9 @@ dependencies = [ [[package]] name = "icu_locale_core" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" +checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" dependencies = [ "displaydoc", "litemap", @@ -1745,11 +1549,10 @@ dependencies = [ [[package]] name = "icu_normalizer" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" +checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" dependencies = [ - "displaydoc", "icu_collections", "icu_normalizer_data", "icu_properties", @@ -1760,42 +1563,38 @@ dependencies = [ [[package]] name = "icu_normalizer_data" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" +checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" [[package]] name = "icu_properties" -version = "2.0.1" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" +checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" dependencies = [ - "displaydoc", "icu_collections", "icu_locale_core", "icu_properties_data", "icu_provider", - "potential_utf", "zerotrie", "zerovec", ] [[package]] name = "icu_properties_data" -version = "2.0.1" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" +checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" [[package]] name = "icu_provider" -version = "2.0.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" +checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" dependencies = [ "displaydoc", "icu_locale_core", - "stable_deref_trait", - "tinystr", "writeable", "yoke", "zerofrom", @@ -1811,9 +1610,9 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "idna" -version = "1.0.3" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" dependencies = [ "idna_adapter", "smallvec", @@ -1849,24 +1648,25 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.10.0" +version = "2.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" +checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" dependencies = [ "equivalent", - "hashbrown 0.15.4", + "hashbrown 0.16.1", "serde", + "serde_core", ] [[package]] name = "indicatif" -version = "0.18.0" +version = "0.18.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70a646d946d06bedbbc4cac4c218acf4bbf2d87757a784857025f4d447e4e1cd" +checksum = "9375e112e4b463ec1b1c6c011953545c65a30164fbab5b581df32b3abf0dcb88" dependencies = [ "console", "portable-atomic", - "unicode-width 0.2.1", + "unicode-width 0.2.2", "unit-prefix", "web-time", ] @@ -1880,17 +1680,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "io-uring" -version = "0.7.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d93587f37623a1a17d94ef2bc9ada592f5465fe7732084ab7beefabe5c77c0c4" -dependencies = [ - "bitflags 2.9.1", - "cfg-if", - "libc", -] - [[package]] name = "ipnet" version = "2.11.0" @@ -1899,9 +1688,9 @@ checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" [[package]] name = "iri-string" -version = "0.7.8" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbc5ebe9c3a1a7a5127f920a418f7585e9e758e911d0466ed004f393b0e380b2" +checksum = "c91338f0783edbd6195decb37bae672fd3b165faffb89bf7b9e6942f8b1a731a" dependencies = [ "memchr", "serde", @@ -1921,113 +1710,37 @@ dependencies = [ [[package]] name = "is-terminal" -version = "0.4.16" +version = "0.4.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9" +checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" dependencies = [ "hermit-abi", "libc", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] name = "is_terminal_polyfill" -version = "1.70.1" +version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" - -[[package]] -name = "isahc" -version = "1.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "334e04b4d781f436dc315cb1e7515bd96826426345d498149e4bde36b67f8ee9" -dependencies = [ - "async-channel 1.9.0", - "castaway", - "crossbeam-utils", - "curl", - "curl-sys", - "encoding_rs", - "event-listener 2.5.3", - "futures-lite 1.13.0", - "http 0.2.12", - "log", - "mime", - "once_cell", - "polling 2.8.0", - "slab", - "sluice", - "tracing", - "tracing-futures", - "url", - "waker-fn", -] - -[[package]] -name = "itertools" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" -dependencies = [ - "either", -] +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" [[package]] name = "itoa" -version = "1.0.15" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" +checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" [[package]] name = "js-sys" -version = "0.3.77" +version = "0.3.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +checksum = "464a3709c7f55f1f721e5389aa6ea4e3bc6aba669353300af094b29ffbdde1d8" dependencies = [ "once_cell", "wasm-bindgen", ] -[[package]] -name = "kv-log-macro" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f" -dependencies = [ - "log", -] - -[[package]] -name = "lalrpop" -version = "0.20.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55cb077ad656299f160924eb2912aa147d7339ea7d69e1b5517326fdcec3c1ca" -dependencies = [ - "ascii-canvas", - "bit-set", - "ena", - "itertools", - "lalrpop-util", - "petgraph", - "pico-args", - "regex", - "regex-syntax", - "string_cache", - "term 0.7.0", - "tiny-keccak", - "unicode-xid", - "walkdir", -] - -[[package]] -name = "lalrpop-util" -version = "0.20.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "507460a910eb7b32ee961886ff48539633b788a36b65692b95f225b844c82553" -dependencies = [ - "regex-automata", -] - [[package]] name = "language-tags" version = "0.3.2" @@ -2040,72 +1753,33 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" -[[package]] -name = "levenshtein" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db13adb97ab515a3691f56e4dbab09283d0b86cb45abd991d8634a9d6f501760" - [[package]] name = "libc" -version = "0.2.174" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" - -[[package]] -name = "libnghttp2-sys" -version = "0.1.11+1.64.0" +version = "0.2.180" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b6c24e48a7167cffa7119da39d577fa482e66c688a4aac016bee862e1a713c4" -dependencies = [ - "cc", - "libc", -] +checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc" [[package]] name = "libredox" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "391290121bad3d37fbddad76d8f5d1c1c314cfc646d143d7e07a3086ddff0ce3" -dependencies = [ - "bitflags 2.9.1", - "libc", -] - -[[package]] -name = "libz-sys" -version = "1.1.22" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b70e7a7df205e92a1a4cd9aaae7898dac0aa555503cc0a649494d0d60e7651d" +checksum = "3d0b95e02c851351f877147b7deea7b1afb1df71b63aa5f8270716e0c5720616" dependencies = [ - "cc", + "bitflags 2.10.0", "libc", - "pkg-config", - "vcpkg", ] [[package]] name = "linux-raw-sys" -version = "0.9.4" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" +checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" [[package]] name = "litemap" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" - -[[package]] -name = "local-channel" -version = "0.1.5" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6cbc85e69b8df4b8bb8b89ec634e7189099cea8927a276b7384ce5488e53ec8" -dependencies = [ - "futures-core", - "futures-sink", - "local-waker", -] +checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" [[package]] name = "local-waker" @@ -2115,28 +1789,24 @@ checksum = "4d873d7c67ce09b42110d801813efbc9364414e356be9935700d368351657487" [[package]] name = "lock_api" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" dependencies = [ - "autocfg", "scopeguard", ] [[package]] name = "log" -version = "0.4.27" +version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" -dependencies = [ - "value-bag", -] +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" [[package]] name = "memchr" -version = "2.7.5" +version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" [[package]] name = "mime" @@ -2165,14 +1835,14 @@ dependencies = [ [[package]] name = "mio" -version = "1.0.4" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" +checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" dependencies = [ "libc", "log", - "wasi 0.11.1+wasi-snapshot-preview1", - "windows-sys 0.59.0", + "wasi", + "windows-sys 0.61.2", ] [[package]] @@ -2216,6 +1886,18 @@ version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" +[[package]] +name = "nix" +version = "0.30.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" +dependencies = [ + "bitflags 2.10.0", + "cfg-if", + "cfg_aliases", + "libc", +] + [[package]] name = "num-bigint" version = "0.4.6" @@ -2260,11 +1942,170 @@ dependencies = [ "libc", ] +[[package]] +name = "objc2" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c2599ce0ec54857b29ce62166b0ed9b4f6f1a70ccc9a71165b6154caca8c05" +dependencies = [ + "objc2-encode", +] + +[[package]] +name = "objc2-cloud-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73ad74d880bb43877038da939b7427bba67e9dd42004a18b809ba7d87cee241c" +dependencies = [ + "bitflags 2.10.0", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-data" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b402a653efbb5e82ce4df10683b6b28027616a2715e90009947d50b8dd298fa" +dependencies = [ + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" +dependencies = [ + "bitflags 2.10.0", + "dispatch2", + "objc2", +] + +[[package]] +name = "objc2-core-graphics" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e022c9d066895efa1345f8e33e584b9f958da2fd4cd116792e15e07e4720a807" +dependencies = [ + "bitflags 2.10.0", + "dispatch2", + "objc2", + "objc2-core-foundation", + "objc2-io-surface", +] + +[[package]] +name = "objc2-core-image" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5d563b38d2b97209f8e861173de434bd0214cf020e3423a52624cd1d989f006" +dependencies = [ + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-location" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca347214e24bc973fc025fd0d36ebb179ff30536ed1f80252706db19ee452009" +dependencies = [ + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-text" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cde0dfb48d25d2b4862161a4d5fcc0e3c24367869ad306b0c9ec0073bfed92d" +dependencies = [ + "bitflags 2.10.0", + "objc2", + "objc2-core-foundation", + "objc2-core-graphics", +] + +[[package]] +name = "objc2-encode" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" + +[[package]] +name = "objc2-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" +dependencies = [ + "bitflags 2.10.0", + "block2", + "libc", + "objc2", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-io-surface" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180788110936d59bab6bd83b6060ffdfffb3b922ba1396b312ae795e1de9d81d" +dependencies = [ + "bitflags 2.10.0", + "objc2", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-quartz-core" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96c1358452b371bf9f104e21ec536d37a650eb10f7ee379fff67d2e08d537f1f" +dependencies = [ + "bitflags 2.10.0", + "objc2", + "objc2-core-foundation", + "objc2-foundation", +] + +[[package]] +name = "objc2-ui-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d87d638e33c06f577498cbcc50491496a3ed4246998a7fbba7ccb98b1e7eab22" +dependencies = [ + "bitflags 2.10.0", + "block2", + "objc2", + "objc2-cloud-kit", + "objc2-core-data", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-core-image", + "objc2-core-location", + "objc2-core-text", + "objc2-foundation", + "objc2-quartz-core", + "objc2-user-notifications", +] + +[[package]] +name = "objc2-user-notifications" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9df9128cbbfef73cda168416ccf7f837b62737d748333bfe9ab71c245d76613e" +dependencies = [ + "objc2", + "objc2-foundation", +] + [[package]] name = "object" -version = "0.36.7" +version = "0.37.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" +checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" dependencies = [ "memchr", ] @@ -2277,17 +2118,17 @@ checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "once_cell_polyfill" -version = "1.70.1" +version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" [[package]] name = "openssl" -version = "0.10.73" +version = "0.10.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8505734d46c8ab1e19a1dce3aef597ad87dcb4c37e7188231769bd6bd51cebf8" +checksum = "08838db121398ad17ab8531ce9de97b244589089e290a384c900cb9ff7434328" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.10.0", "cfg-if", "foreign-types", "libc", @@ -2315,18 +2156,18 @@ checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" [[package]] name = "openssl-src" -version = "300.5.1+3.5.1" +version = "300.5.4+3.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "735230c832b28c000e3bc117119e6466a663ec73506bc0a9907ea4187508e42a" +checksum = "a507b3792995dae9b0df8a1c1e3771e8418b7c2d9f0baeba32e6fe8b06c7cb72" dependencies = [ "cc", ] [[package]] name = "openssl-sys" -version = "0.9.109" +version = "0.9.111" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90096e2e47630d78b7d1c20952dc621f957103f8bc2c8359ec81290d75238571" +checksum = "82cab2d520aa75e3c58898289429321eb788c3106963d0dc886ec7a5f4adc321" dependencies = [ "cc", "libc", @@ -2343,14 +2184,18 @@ checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" [[package]] name = "os_info" -version = "3.12.0" +version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0e1ac5fde8d43c34139135df8ea9ee9465394b2d8d20f032d38998f64afffc3" +checksum = "e4022a17595a00d6a369236fdae483f0de7f0a339960a53118b818238e132224" dependencies = [ + "android_system_properties", "log", - "plist", + "nix", + "objc2", + "objc2-foundation", + "objc2-ui-kit", "serde", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -2372,12 +2217,12 @@ dependencies = [ [[package]] name = "parking_lot" -version = "0.12.4" +version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" dependencies = [ "lock_api", - "parking_lot_core 0.9.11", + "parking_lot_core 0.9.12", ] [[package]] @@ -2396,15 +2241,30 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.11" +version = "0.9.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.5.17", + "redox_syscall 0.5.18", + "smallvec", + "windows-link", +] + +[[package]] +name = "pastey" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b867cad97c0791bbd3aaa6472142568c6c9e8f71937e98379f584cfb0cf35bec" + +[[package]] +name = "path-tree" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a97453bc21a968f722df730bfe11bd08745cb50d1300b0df2bda131dece136" +dependencies = [ "smallvec", - "windows-targets 0.52.6", ] [[package]] @@ -2418,19 +2278,9 @@ dependencies = [ [[package]] name = "percent-encoding" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" - -[[package]] -name = "petgraph" -version = "0.6.5" +version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" -dependencies = [ - "fixedbitset", - "indexmap 2.10.0", -] +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "phf" @@ -2474,12 +2324,6 @@ dependencies = [ "siphasher 1.0.1", ] -[[package]] -name = "pico-args" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315" - [[package]] name = "pin-project" version = "1.1.10" @@ -2512,77 +2356,23 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" -[[package]] -name = "piper" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066" -dependencies = [ - "atomic-waker", - "fastrand 2.3.0", - "futures-io", -] - [[package]] name = "pkg-config" version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" -[[package]] -name = "plist" -version = "1.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3af6b589e163c5a788fab00ce0c0366f6efbb9959c2f9874b224936af7fce7e1" -dependencies = [ - "base64 0.22.1", - "indexmap 2.10.0", - "quick-xml", - "serde", - "time", -] - -[[package]] -name = "polling" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" -dependencies = [ - "autocfg", - "bitflags 1.3.2", - "cfg-if", - "concurrent-queue", - "libc", - "log", - "pin-project-lite", - "windows-sys 0.48.0", -] - -[[package]] -name = "polling" -version = "3.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5bd19146350fe804f7cb2669c851c03d69da628803dab0d98018142aaa5d829" -dependencies = [ - "cfg-if", - "concurrent-queue", - "hermit-abi", - "pin-project-lite", - "rustix", - "windows-sys 0.60.2", -] - [[package]] name = "portable-atomic" -version = "1.11.1" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" +checksum = "f89776e4d69bb58bc6993e99ffa1d11f228b839984854c7daeb5d37f87cbe950" [[package]] name = "potential_utf" -version = "0.1.2" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585" +checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" dependencies = [ "zerovec", ] @@ -2602,12 +2392,6 @@ dependencies = [ "zerocopy", ] -[[package]] -name = "precomputed-hash" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" - [[package]] name = "prettytable-rs" version = "0.10.0" @@ -2624,9 +2408,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.95" +version = "1.0.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +checksum = "535d180e0ecab6268a3e718bb9fd44db66bbbc256257165fc699dadf70d16fe7" dependencies = [ "unicode-ident", ] @@ -2651,29 +2435,11 @@ dependencies = [ "thiserror 1.0.69", ] -[[package]] -name = "psm" -version = "0.1.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e944464ec8536cd1beb0bbfd96987eb5e3b72f2ecdafdc5c769a37f1fa2ae1f" -dependencies = [ - "cc", -] - -[[package]] -name = "quick-xml" -version = "0.38.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9845d9dccf565065824e69f9f235fafba1587031eda353c1f1561cd6a6be78f4" -dependencies = [ - "memchr", -] - [[package]] name = "quote" -version = "1.0.40" +version = "1.0.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +checksum = "dc74d9a594b72ae6656596548f56f667211f8a97b3d4c3d467150794690dc40a" dependencies = [ "proc-macro2", ] @@ -2702,7 +2468,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" dependencies = [ "rand_chacha 0.9.0", - "rand_core 0.9.3", + "rand_core 0.9.4", ] [[package]] @@ -2722,7 +2488,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" dependencies = [ "ppv-lite86", - "rand_core 0.9.3", + "rand_core 0.9.4", ] [[package]] @@ -2731,23 +2497,23 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.16", + "getrandom 0.2.17", ] [[package]] name = "rand_core" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +checksum = "4f1b3bc831f92381018fd9c6350b917c7b21f1eed35a65a51900e0e55a3d7afa" dependencies = [ - "getrandom 0.3.3", + "getrandom 0.3.4", ] [[package]] name = "rayon" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" dependencies = [ "either", "rayon-core", @@ -2755,9 +2521,9 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.12.1" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" dependencies = [ "crossbeam-deque", "crossbeam-utils", @@ -2774,11 +2540,11 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.17" +version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.10.0", ] [[package]] @@ -2787,7 +2553,7 @@ version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" dependencies = [ - "getrandom 0.2.16", + "getrandom 0.2.17", "libredox", "thiserror 1.0.69", ] @@ -2798,25 +2564,25 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" dependencies = [ - "getrandom 0.2.16", + "getrandom 0.2.17", "libredox", - "thiserror 2.0.12", + "thiserror 2.0.17", ] [[package]] name = "ref-cast" -version = "1.0.24" +version = "1.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a0ae411dbe946a674d89546582cea4ba2bb8defac896622d6496f14c23ba5cf" +checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d" dependencies = [ "ref-cast-impl", ] [[package]] name = "ref-cast-impl" -version = "1.0.24" +version = "1.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1165225c21bff1f3bbce98f5a1f889949bc902d3575308cc7b0de30b4f6d27c7" +checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" dependencies = [ "proc-macro2", "quote", @@ -2825,9 +2591,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.11.1" +version = "1.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" dependencies = [ "aho-corasick", "memchr", @@ -2837,9 +2603,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.9" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" dependencies = [ "aho-corasick", "memchr", @@ -2848,21 +2614,21 @@ dependencies = [ [[package]] name = "regex-lite" -version = "0.1.6" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53a49587ad06b26609c52e423de037e7f57f20d53535d66e08c695f347df952a" +checksum = "8d942b98df5e658f56f20d592c7f868833fe38115e65c33003d8cd224b0155da" [[package]] name = "regex-syntax" -version = "0.8.5" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" +checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" [[package]] name = "reqwest" -version = "0.12.22" +version = "0.12.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbc931937e6ca3a06e3b6c0aa7841849b160a90351d6ab467a8b9b9959767531" +checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" dependencies = [ "base64 0.22.1", "bytes", @@ -2870,11 +2636,11 @@ dependencies = [ "futures-channel", "futures-core", "futures-util", - "h2 0.4.11", - "http 1.3.1", + "h2 0.4.13", + "http 1.4.0", "http-body 1.0.1", "http-body-util", - "hyper 1.6.0", + "hyper 1.8.1", "hyper-rustls", "hyper-tls", "hyper-util", @@ -2909,12 +2675,47 @@ checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" dependencies = [ "cc", "cfg-if", - "getrandom 0.2.16", + "getrandom 0.2.17", "libc", "untrusted", "windows-sys 0.52.0", ] +[[package]] +name = "rmcp" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "528d42f8176e6e5e71ea69182b17d1d0a19a6b3b894b564678b74cd7cab13cfa" +dependencies = [ + "async-trait", + "base64 0.22.1", + "chrono", + "futures", + "pastey", + "pin-project-lite", + "rmcp-macros", + "schemars 1.2.0", + "serde", + "serde_json", + "thiserror 2.0.17", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "rmcp-macros" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3f81daaa494eb8e985c9462f7d6ce1ab05e5299f48aafd76cdd3d8b060e6f59" +dependencies = [ + "darling 0.23.0", + "proc-macro2", + "quote", + "serde_json", + "syn", +] + [[package]] name = "rpassword" version = "7.4.0" @@ -2959,22 +2760,22 @@ dependencies = [ [[package]] name = "rustix" -version = "1.0.8" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8" +checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.10.0", "errno", "libc", "linux-raw-sys", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] name = "rustls" -version = "0.23.31" +version = "0.23.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0ebcbd2f03de0fc1122ad9bb24b127a5a6cd51d72604a3f3c50ac459762b6cc" +checksum = "c665f33d38cea657d9614f766881e4d510e0eda4239891eea56b4cadcf01801b" dependencies = [ "once_cell", "rustls-pki-types", @@ -2983,29 +2784,20 @@ dependencies = [ "zeroize", ] -[[package]] -name = "rustls-pemfile" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" -dependencies = [ - "rustls-pki-types", -] - [[package]] name = "rustls-pki-types" -version = "1.12.0" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" +checksum = "21e6f2ab2928ca4291b86736a8bd920a277a399bba1589409d72154ff87c1282" dependencies = [ "zeroize", ] [[package]] name = "rustls-webpki" -version = "0.103.4" +version = "0.103.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a17884ae0c1b773f1ccd2bd4a8c72f16da897310a98b0e84bf349ad5ead92fc" +checksum = "2ffdfa2f5286e2247234e03f680868ac2815974dc39e00ea15adc445d0aafe52" dependencies = [ "ring", "rustls-pki-types", @@ -3014,15 +2806,15 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.21" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] name = "ryu" -version = "1.0.20" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" +checksum = "a50f4cf475b65d88e057964e0e9bb1f0aa9bbb2036dc65c64596b42932536984" [[package]] name = "same-file" @@ -3035,11 +2827,23 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.27" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" +checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", +] + +[[package]] +name = "schemars" +version = "0.8.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fbf2ae1b8bc8e02df939598064d22402220cd5bbcca1c76f7d6a310974d5615" +dependencies = [ + "dyn-clone", + "schemars_derive 0.8.22", + "serde", + "serde_json", ] [[package]] @@ -3056,16 +2860,42 @@ dependencies = [ [[package]] name = "schemars" -version = "1.0.4" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82d20c4491bc164fa2f6c5d44565947a52ad80b9505d8e36f8d54c27c739fcd0" +checksum = "54e910108742c57a770f492731f99be216a52fadd361b06c8fb59d74ccc267d2" dependencies = [ + "chrono", "dyn-clone", "ref-cast", + "schemars_derive 1.2.0", "serde", "serde_json", ] +[[package]] +name = "schemars_derive" +version = "0.8.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32e265784ad618884abaea0600a9adf15393368d840e0222d101a072f3f7534d" +dependencies = [ + "proc-macro2", + "quote", + "serde_derive_internals", + "syn", +] + +[[package]] +name = "schemars_derive" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4908ad288c5035a8eb12cfdf0d49270def0a268ee162b75eeee0f85d155a7c45" +dependencies = [ + "proc-macro2", + "quote", + "serde_derive_internals", + "syn", +] + [[package]] name = "scoped-tls" version = "1.0.1" @@ -3080,7 +2910,7 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "screenly" -version = "1.0.5" +version = "1.1.0" dependencies = [ "anyhow", "clap", @@ -3100,7 +2930,9 @@ dependencies = [ "rayon", "regex", "reqwest", + "rmcp", "rpassword", + "schemars 0.8.22", "sentry", "sentry-anyhow", "serde", @@ -3116,8 +2948,8 @@ dependencies = [ "swc_ecma_parser", "temp-env", "tempfile", - "term 1.1.0", - "thiserror 2.0.12", + "term 1.2.1", + "thiserror 2.0.17", "tokio", "tokio-stream", "walkdir", @@ -3130,7 +2962,7 @@ version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.10.0", "core-foundation", "core-foundation-sys", "libc", @@ -3139,9 +2971,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.14.0" +version = "2.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" +checksum = "cc1f0cbffaac4852523ce30d8bd3c5cdc873501d96ff467ca09b6767bb8cd5c0" dependencies = [ "core-foundation-sys", "libc", @@ -3149,15 +2981,15 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.26" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" [[package]] name = "sentry" -version = "0.42.0" +version = "0.46.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "989425268ab5c011e06400187eed6c298272f8ef913e49fcadc3fda788b45030" +checksum = "2f925d575b468e88b079faf590a8dd0c9c99e2ec29e9bab663ceb8b45056312f" dependencies = [ "httpdate", "native-tls", @@ -3175,9 +3007,9 @@ dependencies = [ [[package]] name = "sentry-actix" -version = "0.42.0" +version = "0.46.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5c675bdf6118764a8e265c3395c311b4d905d12866c92df52870c0223d2ffc1" +checksum = "18bac0f6b8621fa0f85e298901e51161205788322e1a995e3764329020368058" dependencies = [ "actix-http", "actix-web", @@ -3188,9 +3020,9 @@ dependencies = [ [[package]] name = "sentry-anyhow" -version = "0.42.0" +version = "0.46.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1b4523c2595d6730bfbe401e95a6423fe9cb16dc3b6046f340551591cffe723" +checksum = "c8e45993dc8981e7fb5d9b7f701f65ca5c9fb0390263e47f612f30bea3d35fac" dependencies = [ "anyhow", "sentry-backtrace", @@ -3199,9 +3031,9 @@ dependencies = [ [[package]] name = "sentry-backtrace" -version = "0.42.0" +version = "0.46.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68e299dd3f7bcf676875eee852c9941e1d08278a743c32ca528e2debf846a653" +checksum = "6cb1ef7534f583af20452b1b1bf610a60ed9c8dd2d8485e7bd064efc556a78fb" dependencies = [ "backtrace", "regex", @@ -3210,9 +3042,9 @@ dependencies = [ [[package]] name = "sentry-contexts" -version = "0.42.0" +version = "0.46.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fac0c5d6892cd4c414492fc957477b620026fb3411fca9fa12774831da561c88" +checksum = "ebd6be899d9938390b6d1ec71e2f53bd9e57b6a9d8b1d5b049e5c364e7da9078" dependencies = [ "hostname", "libc", @@ -3224,9 +3056,9 @@ dependencies = [ [[package]] name = "sentry-core" -version = "0.42.0" +version = "0.46.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "deaa38b94e70820ff3f1f9db3c8b0aef053b667be130f618e615e0ff2492cbcc" +checksum = "26ab054c34b87f96c3e4701bea1888317cde30cc7e4a6136d2c48454ab96661c" dependencies = [ "rand 0.9.2", "sentry-types", @@ -3237,9 +3069,9 @@ dependencies = [ [[package]] name = "sentry-debug-images" -version = "0.42.0" +version = "0.46.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00950648aa0d371c7f57057434ad5671bd4c106390df7e7284739330786a01b6" +checksum = "5637ec550dc6f8c49a711537950722d3fc4baa6fd433c371912104eaff31e2a5" dependencies = [ "findshlibs", "sentry-core", @@ -3247,9 +3079,9 @@ dependencies = [ [[package]] name = "sentry-panic" -version = "0.42.0" +version = "0.46.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b7a23b13c004873de3ce7db86eb0f59fe4adfc655a31f7bbc17fd10bacc9bfe" +checksum = "3f02c7162f7b69b8de872b439d4696dc1d65f80b13ddd3c3831723def4756b63" dependencies = [ "sentry-backtrace", "sentry-core", @@ -3257,11 +3089,11 @@ dependencies = [ [[package]] name = "sentry-tracing" -version = "0.42.0" +version = "0.46.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fac841c7050aa73fc2bec8f7d8e9cb1159af0b3095757b99820823f3e54e5080" +checksum = "e1dd47df349a80025819f3d25c3d2f751df705d49c65a4cdc0f130f700972a48" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.10.0", "sentry-backtrace", "sentry-core", "tracing-core", @@ -3270,16 +3102,16 @@ dependencies = [ [[package]] name = "sentry-types" -version = "0.42.0" +version = "0.46.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e477f4d4db08ddb4ab553717a8d3a511bc9e81dde0c808c680feacbb8105c412" +checksum = "eecbd63e9d15a26a40675ed180d376fcb434635d2e33de1c24003f61e3e2230d" dependencies = [ "debugid", "hex", "rand 0.9.2", "serde", "serde_json", - "thiserror 2.0.12", + "thiserror 2.0.17", "time", "url", "uuid", @@ -3321,16 +3153,28 @@ dependencies = [ "syn", ] +[[package]] +name = "serde_derive_internals" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "serde_json" -version = "1.0.142" +version = "1.0.149" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "030fedb782600dcbd6f02d479bf0d817ac3bb40d644745b769d6a96bc3afc5a7" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" dependencies = [ "itoa", "memchr", - "ryu", "serde", + "serde_core", + "zmij", ] [[package]] @@ -3357,19 +3201,18 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.14.0" +version = "3.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2c45cd61fefa9db6f254525d46e392b852e0e61d9a1fd36e5bd183450a556d5" +checksum = "4fa237f2807440d238e0364a218270b98f767a00d3dada77b1c53ae88940e2e7" dependencies = [ "base64 0.22.1", "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.10.0", + "indexmap 2.13.0", "schemars 0.9.0", - "schemars 1.0.4", - "serde", - "serde_derive", + "schemars 1.2.0", + "serde_core", "serde_json", "serde_with_macros", "time", @@ -3377,11 +3220,11 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.14.0" +version = "3.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de90945e6565ce0d9a25098082ed4ee4002e047cb59892c318d66821e14bb30f" +checksum = "52a8e3ca0ca629121f70ab50f95249e5a6f925cc0f6ffe8256c45b728875706c" dependencies = [ - "darling", + "darling 0.21.3", "proc-macro2", "quote", "syn", @@ -3393,7 +3236,7 @@ version = "0.9.34+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" dependencies = [ - "indexmap 2.10.0", + "indexmap 2.13.0", "itoa", "ryu", "serde", @@ -3430,10 +3273,11 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "signal-hook-registry" -version = "1.4.6" +version = "1.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b" +checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" dependencies = [ + "errno", "libc", ] @@ -3445,14 +3289,14 @@ checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa" [[package]] name = "simple_logger" -version = "4.3.3" +version = "5.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e7e46c8c90251d47d08b28b8a419ffb4aede0f87c2eea95e17d1d5bacbf3ef1" +checksum = "291bee647ce7310b0ea721bfd7e0525517b4468eb7c7e15eb8bd774343179702" dependencies = [ "colored", "log", "time", - "windows-sys 0.48.0", + "windows-sys 0.61.2", ] [[package]] @@ -3473,17 +3317,6 @@ version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" -[[package]] -name = "sluice" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d7400c0eff44aa2fcb5e31a5f24ba9716ed90138769e4977a2ba6014ae63eb5" -dependencies = [ - "async-channel 1.9.0", - "futures-core", - "futures-io", -] - [[package]] name = "smallvec" version = "1.15.1" @@ -3513,12 +3346,12 @@ dependencies = [ [[package]] name = "socket2" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807" +checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -3529,22 +3362,9 @@ checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" [[package]] name = "stable_deref_trait" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" - -[[package]] -name = "stacker" -version = "0.1.21" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cddb07e32ddb770749da91081d8d0ac3a16f1a569a18b20348cd371f5dead06b" -dependencies = [ - "cc", - "cfg-if", - "libc", - "psm", - "windows-sys 0.59.0", -] +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" [[package]] name = "static_assertions" @@ -3552,18 +3372,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" -[[package]] -name = "string_cache" -version = "0.8.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf776ba3fa74f83bf4b63c3dcbbf82173db2632ed8452cb2d891d33f459de70f" -dependencies = [ - "new_debug_unreachable", - "parking_lot 0.12.4", - "phf_shared", - "precomputed-hash", -] - [[package]] name = "string_enum" version = "1.0.2" @@ -3575,6 +3383,12 @@ dependencies = [ "syn", ] +[[package]] +name = "stringmetrics" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b3c8667cd96245cbb600b8dec5680a7319edd719c5aa2b5d23c6bff94f39765" + [[package]] name = "strsim" version = "0.11.1" @@ -3607,9 +3421,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "swc_atoms" -version = "7.0.0" +version = "9.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3500dcf04c84606b38464561edc5e46f5132201cb3e23cf9613ed4033d6b1bb2" +checksum = "d4ccbe2ecad10ad7432100f878a107b1d972a8aee83ca53184d00c23a078bb8a" dependencies = [ "hstr", "once_cell", @@ -3618,9 +3432,9 @@ dependencies = [ [[package]] name = "swc_common" -version = "14.0.4" +version = "18.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2bb772b3a26b8b71d4e8c112ced5b5867be2266364b58517407a270328a2696" +checksum = "a1c06698254e9b47daaf9bbb062af489a350bd8d10dfaab0cabbd32d46cec69d" dependencies = [ "anyhow", "ast_node", @@ -3628,7 +3442,6 @@ dependencies = [ "bytes-str", "either", "from_variant", - "new_debug_unreachable", "num-bigint", "once_cell", "rustc-hash", @@ -3638,17 +3451,17 @@ dependencies = [ "swc_eq_ignore_macros", "swc_visit", "tracing", - "unicode-width 0.1.14", + "unicode-width 0.2.2", "url", ] [[package]] name = "swc_ecma_ast" -version = "15.0.0" +version = "19.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65c25af97d53cf8aab66a6c68f3418663313fc969ad267fc2a4d19402c329be1" +checksum = "724195600825cbdd2a899d5473d2ce1f24ae418bff1231f160ecf38a3bc81f46" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.10.0", "is-macro", "num-bigint", "once_cell", @@ -3662,41 +3475,22 @@ dependencies = [ ] [[package]] -name = "swc_ecma_lexer" -version = "23.0.2" +name = "swc_ecma_parser" +version = "32.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "017d06ea85008234aa9fb34d805c7dc563f2ea6e03869ed5ac5a2dc27d561e4d" +checksum = "e1d0c36843109fff178bbedc439b4190daa865d78e553134243a4df220329fdd" dependencies = [ - "arrayvec", - "bitflags 2.9.1", + "bitflags 2.10.0", "either", "num-bigint", "phf", "rustc-hash", "seq-macro", "serde", - "smallvec", "smartstring", - "stacker", - "swc_atoms", - "swc_common", - "swc_ecma_ast", - "tracing", -] - -[[package]] -name = "swc_ecma_parser" -version = "24.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38cd8f08611d6d4927bcd81501813b304885af064b9704ba8e4a0e0f869364e8" -dependencies = [ - "either", - "num-bigint", - "serde", "swc_atoms", "swc_common", "swc_ecma_ast", - "swc_ecma_lexer", "tracing", ] @@ -3734,9 +3528,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.104" +version = "2.0.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" +checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a" dependencies = [ "proc-macro2", "quote", @@ -3769,7 +3563,7 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.10.0", "core-foundation", "system-configuration-sys", ] @@ -3784,26 +3578,35 @@ dependencies = [ "libc", ] +[[package]] +name = "tabwriter" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fce91f2f0ec87dff7e6bcbbeb267439aa1188703003c6055193c821487400432" +dependencies = [ + "unicode-width 0.2.2", +] + [[package]] name = "temp-env" version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96374855068f47402c3121c6eed88d29cb1de8f3ab27090e273e420bdabcf050" dependencies = [ - "parking_lot 0.12.4", + "parking_lot 0.12.5", ] [[package]] name = "tempfile" -version = "3.20.0" +version = "3.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" +checksum = "655da9c7eb6305c55742045d5a8d2037996d61d8de95806335c7c86ce0f82e9c" dependencies = [ - "fastrand 2.3.0", - "getrandom 0.3.3", + "fastrand", + "getrandom 0.3.4", "once_cell", "rustix", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -3819,11 +3622,11 @@ dependencies = [ [[package]] name = "term" -version = "1.1.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a43bddab41f8626c7bdaab872bbba75f8df5847b516d77c569c746e2ae5eb746" +checksum = "d8c27177b12a6399ffc08b98f76f7c9a1f4fe9fc967c784c5a071fa8d93cf7e1" dependencies = [ - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -3837,11 +3640,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.12" +version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" dependencies = [ - "thiserror-impl 2.0.12", + "thiserror-impl 2.0.17", ] [[package]] @@ -3857,9 +3660,9 @@ dependencies = [ [[package]] name = "thiserror-impl" -version = "2.0.12" +version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" dependencies = [ "proc-macro2", "quote", @@ -3868,9 +3671,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.41" +version = "0.3.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" +checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d" dependencies = [ "deranged", "itoa", @@ -3885,34 +3688,25 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.4" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" +checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b" [[package]] name = "time-macros" -version = "0.2.22" +version = "0.2.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" +checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3" dependencies = [ "num-conv", "time-core", ] -[[package]] -name = "tiny-keccak" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" -dependencies = [ - "crunchy", -] - [[package]] name = "tinystr" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" +checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" dependencies = [ "displaydoc", "zerovec", @@ -3920,29 +3714,26 @@ dependencies = [ [[package]] name = "tokio" -version = "1.47.1" +version = "1.49.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038" +checksum = "72a2903cd7736441aac9df9d7688bd0ce48edccaadf181c3b90be801e81d3d86" dependencies = [ - "backtrace", "bytes", - "io-uring", "libc", "mio", - "parking_lot 0.12.4", + "parking_lot 0.12.5", "pin-project-lite", "signal-hook-registry", - "slab", - "socket2 0.6.0", + "socket2 0.6.1", "tokio-macros", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] name = "tokio-macros" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" +checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" dependencies = [ "proc-macro2", "quote", @@ -3961,9 +3752,9 @@ dependencies = [ [[package]] name = "tokio-rustls" -version = "0.26.2" +version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b" +checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" dependencies = [ "rustls", "tokio", @@ -3971,9 +3762,9 @@ dependencies = [ [[package]] name = "tokio-stream" -version = "0.1.17" +version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047" +checksum = "32da49809aab5c3bc678af03902d4ccddea2a87d028d86392a4b1560c6906c70" dependencies = [ "futures-core", "pin-project-lite", @@ -3994,9 +3785,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.16" +version = "0.7.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14307c986784f72ef81c89db7d9e28d6ac26d16213b109ea501696195e6e3ce5" +checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" dependencies = [ "bytes", "futures-core", @@ -4022,14 +3813,14 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.6.6" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" +checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.10.0", "bytes", "futures-util", - "http 1.3.1", + "http 1.4.0", "http-body 1.0.1", "iri-string", "pin-project-lite", @@ -4052,9 +3843,9 @@ checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" -version = "0.1.41" +version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" dependencies = [ "log", "pin-project-lite", @@ -4064,9 +3855,9 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.30" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", @@ -4075,38 +3866,28 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.34" +version = "0.1.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" dependencies = [ "once_cell", "valuable", ] -[[package]] -name = "tracing-futures" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" -dependencies = [ - "pin-project", - "tracing", -] - [[package]] name = "tracing-subscriber" -version = "0.3.20" +version = "0.3.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5" +checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e" dependencies = [ "tracing-core", ] [[package]] name = "triomphe" -version = "0.1.14" +version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef8f7726da4807b58ea5c96fdc122f80702030edc33b35aff9190a51148ccc85" +checksum = "dd69c5aa8f924c7519d6372789a74eac5b94fb0f8fcf0d4a97eb0bfc3e785f39" dependencies = [ "serde", "stable_deref_trait", @@ -4127,7 +3908,7 @@ dependencies = [ "byteorder", "bytes", "data-encoding", - "http 1.3.1", + "http 1.4.0", "httparse", "log", "rand 0.8.5", @@ -4139,9 +3920,9 @@ dependencies = [ [[package]] name = "typenum" -version = "1.18.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" [[package]] name = "uname" @@ -4154,9 +3935,9 @@ dependencies = [ [[package]] name = "unicase" -version = "2.8.1" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" +checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142" [[package]] name = "unicode-id-start" @@ -4166,9 +3947,15 @@ checksum = "81b79ad29b5e19de4260020f8919b443b2ef0277d242ce532ec7b7a2cc8b6007" [[package]] name = "unicode-ident" -version = "1.0.18" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" + +[[package]] +name = "unicode-segmentation" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" [[package]] name = "unicode-width" @@ -4178,9 +3965,9 @@ checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" [[package]] name = "unicode-width" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a1a07cc7db3810833284e8d372ccdc6da29741639ecc70c9ec107df0fa6154c" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" [[package]] name = "unicode-xid" @@ -4190,9 +3977,9 @@ checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" [[package]] name = "unit-prefix" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "323402cff2dd658f39ca17c789b502021b3f18707c91cdf22e3838e1b4023817" +checksum = "81e544489bf3d8ef66c953931f56617f423cd4b5494be343d9b9d3dda037b9a3" [[package]] name = "unsafe-libyaml" @@ -4208,44 +3995,44 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "ureq" -version = "3.0.12" +version = "3.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f0fde9bc91026e381155f8c67cb354bcd35260b2f4a29bcc84639f762760c39" +checksum = "d39cb1dbab692d82a977c0392ffac19e188bd9186a9f32806f0aaa859d75585a" dependencies = [ "base64 0.22.1", "der", "log", "native-tls", "percent-encoding", - "rustls-pemfile", "rustls-pki-types", "ureq-proto", "utf-8", - "webpki-root-certs 0.26.11", + "webpki-root-certs", ] [[package]] name = "ureq-proto" -version = "0.4.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59db78ad1923f2b1be62b6da81fe80b173605ca0d57f85da2e005382adf693f7" +checksum = "d81f9efa9df032be5934a46a068815a10a042b494b6a58cb0a1a97bb5467ed6f" dependencies = [ "base64 0.22.1", - "http 1.3.1", + "http 1.4.0", "httparse", "log", ] [[package]] name = "url" -version = "2.5.4" +version = "2.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" dependencies = [ "form_urlencoded", "idna", "percent-encoding", "serde", + "serde_derive", ] [[package]] @@ -4268,12 +4055,12 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.17.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cf4199d1e5d15ddd86a694e4d0dffa9c323ce759fea589f00fef9d81cc1931d" +checksum = "e2e054861b4bd027cd373e18e8d8d8e6548085000e41290d95ce0c373a654b4a" dependencies = [ "js-sys", - "serde", + "serde_core", "wasm-bindgen", ] @@ -4283,12 +4070,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" -[[package]] -name = "value-bag" -version = "1.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "943ce29a8a743eb10d6082545d861b24f9d1b160b7d741e0f2cdf726bec909c5" - [[package]] name = "vcpkg" version = "0.2.15" @@ -4301,12 +4082,6 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" -[[package]] -name = "waker-fn" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "317211a0dc0ceedd78fb2ca9a44aed3d7b9b26f81870d485c07122b4350673b7" - [[package]] name = "walkdir" version = "2.5.0" @@ -4335,7 +4110,7 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "headers", + "headers 0.3.9", "http 0.2.12", "hyper 0.14.32", "log", @@ -4362,45 +4137,32 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] -name = "wasi" -version = "0.14.2+wasi-0.2.4" +name = "wasip2" +version = "1.0.1+wasi-0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" dependencies = [ - "wit-bindgen-rt", + "wit-bindgen", ] [[package]] name = "wasm-bindgen" -version = "0.2.100" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +checksum = "0d759f433fa64a2d763d1340820e46e111a7a5ab75f993d1852d70b03dbb80fd" dependencies = [ "cfg-if", "once_cell", "rustversion", "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" -dependencies = [ - "bumpalo", - "log", - "proc-macro2", - "quote", - "syn", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.50" +version = "0.4.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" +checksum = "836d9622d604feee9e5de25ac10e3ea5f2d65b41eac0d9ce72eb5deae707ce7c" dependencies = [ "cfg-if", "js-sys", @@ -4411,9 +4173,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.100" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +checksum = "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -4421,31 +4183,31 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.100" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +checksum = "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40" dependencies = [ + "bumpalo", "proc-macro2", "quote", "syn", - "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.100" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +checksum = "cbc538057e648b67f72a982e708d485b2efa771e1ac05fec311f9f63e5800db4" dependencies = [ "unicode-ident", ] [[package]] name = "web-sys" -version = "0.3.77" +version = "0.3.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" +checksum = "9b32828d774c412041098d182a8b38b16ea816958e07cf40eec2bc080ae137ac" dependencies = [ "js-sys", "wasm-bindgen", @@ -4463,18 +4225,9 @@ dependencies = [ [[package]] name = "webpki-root-certs" -version = "0.26.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75c7f0ef91146ebfb530314f5f1d24528d7f0767efbfd31dce919275413e393e" -dependencies = [ - "webpki-root-certs 1.0.2", -] - -[[package]] -name = "webpki-root-certs" -version = "1.0.2" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e4ffd8df1c57e87c325000a3d6ef93db75279dc3a231125aac571650f22b12a" +checksum = "36a29fc0408b113f68cf32637857ab740edfafdf460c326cd2afaa2d84cc05dc" dependencies = [ "rustls-pki-types", ] @@ -4497,11 +4250,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.9" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -4512,9 +4265,9 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-core" -version = "0.61.2" +version = "0.62.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" dependencies = [ "windows-implement", "windows-interface", @@ -4525,9 +4278,9 @@ dependencies = [ [[package]] name = "windows-implement" -version = "0.60.0" +version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", @@ -4536,9 +4289,9 @@ dependencies = [ [[package]] name = "windows-interface" -version = "0.59.1" +version = "0.59.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", @@ -4547,15 +4300,15 @@ dependencies = [ [[package]] name = "windows-link" -version = "0.1.3" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" [[package]] name = "windows-registry" -version = "0.5.3" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b8a9ed28765efc97bbc954883f4e6796c33a06546ebafacbabee9696967499e" +checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720" dependencies = [ "windows-link", "windows-result", @@ -4564,31 +4317,22 @@ dependencies = [ [[package]] name = "windows-result" -version = "0.3.4" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" dependencies = [ "windows-link", ] [[package]] name = "windows-strings" -version = "0.4.2" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" dependencies = [ "windows-link", ] -[[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]] name = "windows-sys" version = "0.52.0" @@ -4613,22 +4357,16 @@ version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" dependencies = [ - "windows-targets 0.53.3", + "windows-targets 0.53.5", ] [[package]] -name = "windows-targets" -version = "0.48.5" +name = "windows-sys" +version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" 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", + "windows-link", ] [[package]] @@ -4649,27 +4387,21 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.53.3" +version = "0.53.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" dependencies = [ "windows-link", - "windows_aarch64_gnullvm 0.53.0", - "windows_aarch64_msvc 0.53.0", - "windows_i686_gnu 0.53.0", - "windows_i686_gnullvm 0.53.0", - "windows_i686_msvc 0.53.0", - "windows_x86_64_gnu 0.53.0", - "windows_x86_64_gnullvm 0.53.0", - "windows_x86_64_msvc 0.53.0", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", ] -[[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" @@ -4678,15 +4410,9 @@ checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_gnullvm" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" [[package]] name = "windows_aarch64_msvc" @@ -4696,15 +4422,9 @@ checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_aarch64_msvc" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" - -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" [[package]] name = "windows_i686_gnu" @@ -4714,9 +4434,9 @@ checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnu" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" [[package]] name = "windows_i686_gnullvm" @@ -4726,15 +4446,9 @@ checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_gnullvm" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" - -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" [[package]] name = "windows_i686_msvc" @@ -4744,15 +4458,9 @@ checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_i686_msvc" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" [[package]] name = "windows_x86_64_gnu" @@ -4762,15 +4470,9 @@ checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnu" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" [[package]] name = "windows_x86_64_gnullvm" @@ -4780,15 +4482,9 @@ checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_gnullvm" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" [[package]] name = "windows_x86_64_msvc" @@ -4798,32 +4494,28 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "windows_x86_64_msvc" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" [[package]] -name = "wit-bindgen-rt" -version = "0.39.0" +name = "wit-bindgen" +version = "0.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" -dependencies = [ - "bitflags 2.9.1", -] +checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" [[package]] name = "writeable" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" +checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" [[package]] name = "yoke" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" +checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" dependencies = [ - "serde", "stable_deref_trait", "yoke-derive", "zerofrom", @@ -4831,9 +4523,9 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" +checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" dependencies = [ "proc-macro2", "quote", @@ -4843,18 +4535,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.26" +version = "0.8.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f" +checksum = "668f5168d10b9ee831de31933dc111a459c97ec93225beb307aed970d1372dfd" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.26" +version = "0.8.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" +checksum = "2c7962b26b0a8685668b671ee4b54d007a67d4eaf05fda79ac0ecf41e32270f1" dependencies = [ "proc-macro2", "quote", @@ -4884,15 +4576,15 @@ dependencies = [ [[package]] name = "zeroize" -version = "1.8.1" +version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" [[package]] name = "zerotrie" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" +checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" dependencies = [ "displaydoc", "yoke", @@ -4901,9 +4593,9 @@ dependencies = [ [[package]] name = "zerovec" -version = "0.11.3" +version = "0.11.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdbb9122ea75b11bf96e7492afb723e8a7fbe12c67417aa95e7e3d18144d37cd" +checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" dependencies = [ "yoke", "zerofrom", @@ -4912,11 +4604,17 @@ dependencies = [ [[package]] name = "zerovec-derive" -version = "0.11.1" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" +checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" dependencies = [ "proc-macro2", "quote", "syn", ] + +[[package]] +name = "zmij" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac93432f5b761b22864c774aac244fa5c0fd877678a4c37ebf6cf42208f9c9ec" diff --git a/Cargo.toml b/Cargo.toml index 21d3763..8286398 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "screenly" -version = "1.0.5" +version = "1.1.0" edition = "2021" [[bin]] @@ -30,28 +30,32 @@ rayon = "1.7.0" regex = "1.9.3" reqwest = { version = "0.12.22", features = ["json", "blocking", "multipart"] } rpassword = "7.2.0" -sentry = "0.42.0" -sentry-anyhow = "0.42.0" +sentry = "0.46" +sentry-anyhow = "0.46" serde = { version = "1.0.147", features = ["derive"] } serde_json = "1.0.87" serde_with = "3.8.3" serde_yaml = "0.9.17" sha1 = "0.10.5" sha2 = "0.10.7" -simple_logger = { version = "4.0.0", features = ["colors"] } +simple_logger = { version = "5", features = ["colors"] } strum = "0.27" strum_macros = "0.27" temp-env = "0.3.6" term = "1.1.0" thiserror = "2.0.12" -tokio = { version = "1.32.0", features = ["rt-multi-thread"] } +tokio = { version = "1.32.0", features = ["rt-multi-thread", "macros"] } tokio-stream = "0.1.14" walkdir = "2.3.3" -warp = "0.3.5" +warp = "0.3" + +# MCP server dependencies +rmcp = { version = "0.12", features = ["schemars", "server", "transport-io"] } +schemars = "0.8" [dev-dependencies] envtestkit = "1.1.2" -httpmock = "0.6" -swc_common = { version = "14.0.4", default-features = false, features = [] } -swc_ecma_parser = { version = "24.0.2", default-features = false, features = [] } +httpmock = "0.8" +swc_common = { version = "18", default-features = false, features = [] } +swc_ecma_parser = { version = "32", default-features = false, features = ["typescript"] } tempfile = "3.8" diff --git a/Dockerfile b/Dockerfile index 712d449..b9efd12 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,7 +3,7 @@ FROM alpine:3 as builder WORKDIR /usr/src/screenly-cli RUN apk add --no-cache wget tar -ARG RELEASE=v1.0.5 +ARG RELEASE=v1.1.0 RUN wget "https://github.com/Screenly/cli/releases/download/$RELEASE/screenly-cli-x86_64-unknown-linux-musl.tar.gz" RUN tar xfz screenly-cli-x86_64-unknown-linux-musl.tar.gz diff --git a/README.md b/README.md index 882be70..10909c4 100644 --- a/README.md +++ b/README.md @@ -67,6 +67,57 @@ $ API_SERVER_NAME=local cargo build --release Explore available commands [here](https://developer.screenly.io/cli/#commands). +## MCP Server (AI Assistant Integration) + +The Screenly CLI includes a built-in [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) server, enabling AI assistants like Claude, Cursor, and others to interact with your Screenly digital signage network. + +### Starting the MCP Server + +```bash +$ screenly mcp +``` + +The server communicates over stdio and exposes the full Screenly API as tools. + +### Available Tools + +| Category | Tools | +|----------|-------| +| **Screens** | `screen_list`, `screen_get` | +| **Assets** | `asset_list`, `asset_get`, `asset_create`, `asset_update`, `asset_delete` | +| **Asset Groups** | `asset_group_list`, `asset_group_create`, `asset_group_update`, `asset_group_delete` | +| **Playlists** | `playlist_list`, `playlist_create`, `playlist_update`, `playlist_delete` | +| **Playlist Items** | `playlist_item_list`, `playlist_item_create`, `playlist_item_update`, `playlist_item_delete` | +| **Labels** | `label_list`, `label_create`, `label_update`, `label_delete`, `label_link_screen`, `label_unlink_screen`, `label_link_playlist`, `label_unlink_playlist` | +| **Shared Playlists** | `shared_playlist_list`, `shared_playlist_create`, `shared_playlist_delete` | +| **Edge Apps** | `edge_app_list`, `edge_app_list_settings`, `edge_app_list_instances` | + +### Configuration Examples + +#### Cursor / Claude Desktop + +Add to your MCP configuration file: + +```json +{ + "mcpServers": { + "screenly": { + "command": "screenly", + "args": ["mcp"], + "env": { + "API_TOKEN": "your-api-token-here" + } + } + } +} +``` + +#### Authentication + +The MCP server uses the same authentication as the CLI: +- Set the `API_TOKEN` environment variable, or +- Run `screenly login` to store credentials in `~/.screenly` + ## GitHub Action Integrate Screenly CLI into your GitHub workflows: diff --git a/docs/CommandLineHelp.md b/docs/CommandLineHelp.md index 299b777..825ec84 100644 --- a/docs/CommandLineHelp.md +++ b/docs/CommandLineHelp.md @@ -46,6 +46,7 @@ This document contains the help content for the `screenly` command-line program. * [`screenly edge-app deploy`↴](#screenly-edge-app-deploy) * [`screenly edge-app delete`↴](#screenly-edge-app-delete) * [`screenly edge-app validate`↴](#screenly-edge-app-validate) +* [`screenly mcp`↴](#screenly-mcp) ## `screenly` @@ -55,12 +56,13 @@ Command line interface is intended for quick interaction with Screenly through t ###### **Subcommands:** -* `login` — Logins with the token and stores it for further use if it's valid. You can set API_TOKEN environment variable to override used API token -* `logout` — Logouts and removes stored token +* `login` — Logs in with the provided token and stores it for further use if valid. You can set the API_TOKEN environment variable to override the stored token +* `logout` — Logs out and removes the stored token * `screen` — Screen related commands * `asset` — Asset related commands * `playlist` — Playlist related commands * `edge-app` — Edge App related commands +* `mcp` — Starts the MCP (Model Context Protocol) server on stdio for AI assistant integration ###### **Options:** @@ -70,7 +72,7 @@ Command line interface is intended for quick interaction with Screenly through t ## `screenly login` -Logins with the token and stores it for further use if it's valid. You can set API_TOKEN environment variable to override used API token +Logs in with the provided token and stores it for further use if valid. You can set the API_TOKEN environment variable to override the stored token **Usage:** `screenly login` @@ -78,7 +80,7 @@ Logins with the token and stores it for further use if it's valid. You can set A ## `screenly logout` -Logouts and removes stored token +Logs out and removes the stored token **Usage:** `screenly logout` @@ -169,10 +171,10 @@ Asset related commands * `add` — Adds a new asset * `delete` — Deletes an asset. This cannot be undone * `inject-js` — Injects JavaScript code inside of the web asset. It will be executed once the asset loads during playback -* `set-headers` — Sets HTTP headers for web asset -* `update-headers` — Updates HTTP headers for web asset -* `basic-auth` — Shortcut for setting up basic authentication headers -* `bearer-auth` — Shortcut for setting up bearer authentication headers +* `set-headers` — Sets HTTP headers for a web asset +* `update-headers` — Updates HTTP headers for a web asset +* `basic-auth` — Sets up basic authentication headers for a web asset +* `bearer-auth` — Sets up bearer authentication headers for a web asset @@ -248,52 +250,52 @@ Injects JavaScript code inside of the web asset. It will be executed once the as ## `screenly asset set-headers` -Sets HTTP headers for web asset +Sets HTTP headers for a web asset **Usage:** `screenly asset set-headers ` ###### **Arguments:** -* `` — UUID of the web asset to set http headers -* `` — HTTP headers in the following form `header1=value1[,header2=value2[,...]]`. This command replaces all headers of the asset with the given headers (when an empty string is given, e.g. --set-headers "", all existing headers are removed, if any) +* `` — UUID of the web asset +* `` — HTTP headers in the form `header1=value1[,header2=value2[,...]]`. This command replaces all headers of the asset with the given headers. Use an empty string (e.g., --set-headers "") to remove all existing headers ## `screenly asset update-headers` -Updates HTTP headers for web asset +Updates HTTP headers for a web asset **Usage:** `screenly asset update-headers ` ###### **Arguments:** -* `` — UUID of the web asset to set http headers -* `` — HTTP headers in the following form `header1=value1[,header2=value2[,...]]`. This command updates only the given headers (adding them if new), leaving any other headers unchanged +* `` — UUID of the web asset +* `` — HTTP headers in the form `header1=value1[,header2=value2[,...]]`. This command updates only the given headers (adding them if new), leaving other headers unchanged ## `screenly asset basic-auth` -Shortcut for setting up basic authentication headers +Sets up basic authentication headers for a web asset **Usage:** `screenly asset basic-auth ` ###### **Arguments:** -* `` — UUID of the web asset to set up basic authentication for +* `` — UUID of the web asset * `` — Basic authentication credentials in "user=password" form ## `screenly asset bearer-auth` -Shortcut for setting up bearer authentication headers +Sets up bearer authentication headers for a web asset **Usage:** `screenly asset bearer-auth ` ###### **Arguments:** -* `` — UUID of the web asset to set up basic authentication for +* `` — UUID of the web asset * `` — Bearer token @@ -312,20 +314,48 @@ Playlist related commands * `delete` — Deletes a playlist. This cannot be undone * `append` — Adds an asset to the end of the playlist * `prepend` — Adds an asset to the beginning of the playlist -* `update` — Patches a given playlist +* `update` — Updates a playlist from JSON input on stdin ## `screenly playlist create` -Creates a new playlist +Creates a new playlist. + +Playlists use a predicate DSL to control when they are shown. The predicate is a boolean expression using these variables: + +$DATE - Current date as Unix timestamp in milliseconds $TIME - Time of day in ms since midnight (0-86400000) $WEEKDAY - Day of week (0=Sun, 1=Mon, ..., 6=Sat) + +Operators: =, <=, >=, <, >, AND, OR, NOT Special: BETWEEN {min, max}, IN {val1, val2, ...} + +Time reference (ms): 32400000=9AM, 43200000=12PM, 61200000=5PM + +Examples: TRUE - Always show $WEEKDAY IN {1, 2, 3, 4, 5} - Weekdays only $TIME BETWEEN {32400000, 61200000} - 9 AM to 5 PM NOT $WEEKDAY IN {0, 6} - Exclude weekends **Usage:** `screenly playlist create [OPTIONS] [PREDICATE]` ###### **Arguments:** * `<TITLE>` — Title of the new playlist -* `<PREDICATE>` — Predicate for the new playlist. If not specified it will be set to "TRUE" +* `<PREDICATE>` — Predicate expression controlling when the playlist is shown. + + Variables: + $DATE - Unix timestamp in milliseconds + $TIME - Milliseconds since midnight (0-86400000) + $WEEKDAY - Day of week (0=Sun, 1=Mon, ..., 6=Sat) + + Operators: =, <=, >=, <, >, AND, OR, NOT + Special: BETWEEN {min, max}, IN {val1, val2, ...} + + Time reference: 32400000=9AM, 43200000=12PM, 61200000=5PM, 72000000=8PM + + Examples: + TRUE - Always show + $WEEKDAY IN {1, 2, 3, 4, 5} - Weekdays only + $TIME BETWEEN {32400000, 61200000} - 9 AM to 5 PM + NOT $WEEKDAY IN {0, 6} - Exclude weekends + + Default: TRUE ###### **Options:** @@ -379,7 +409,7 @@ Adds an asset to the end of the playlist * `<UUID>` — UUID of the playlist * `<ASSET_UUID>` — UUID of the asset -* `<DURATION>` — Duration of the playlist item in seconds. If not specified it will be set to 15 seconds +* `<DURATION>` — Duration of the playlist item in seconds. Defaults to 15 seconds ###### **Options:** @@ -397,7 +427,7 @@ Adds an asset to the beginning of the playlist * `<UUID>` — UUID of the playlist * `<ASSET_UUID>` — UUID of the asset -* `<DURATION>` — Duration of the playlist item in seconds. If not specified it will be set to 15 seconds +* `<DURATION>` — Duration of the playlist item in seconds. Defaults to 15 seconds ###### **Options:** @@ -407,7 +437,7 @@ Adds an asset to the beginning of the playlist ## `screenly playlist update` -Patches a given playlist +Updates a playlist from JSON input on stdin **Usage:** `screenly playlist update` @@ -421,28 +451,28 @@ Edge App related commands ###### **Subcommands:** -* `create` — Creates Edge App in the store +* `create` — Creates an Edge App in the store * `list` — Lists your Edge Apps -* `rename` — Renames Edge App -* `run` — Runs Edge App emulator -* `setting` — Settings commands -* `instance` — Instance commands -* `deploy` — Deploys assets and settings of the Edge App and release it +* `rename` — Renames an Edge App +* `run` — Runs the Edge App emulator +* `setting` — Edge App setting commands +* `instance` — Edge App instance commands +* `deploy` — Deploys assets and settings of the Edge App and releases it * `delete` — Deletes an Edge App. This cannot be undone -* `validate` — Validates Edge App manifest file +* `validate` — Validates the Edge App manifest file ## `screenly edge-app create` -Creates Edge App in the store +Creates an Edge App in the store **Usage:** `screenly edge-app create [OPTIONS] --name <NAME>` ###### **Options:** * `-n`, `--name <NAME>` — Edge App name -* `-p`, `--path <PATH>` — Path to the directory with the manifest. If not specified CLI will use the current working directory +* `-p`, `--path <PATH>` — Path to the directory with the manifest. Defaults to the current working directory * `-i`, `--in-place` — Use an existing Edge App directory with the manifest and index.html @@ -461,41 +491,41 @@ Lists your Edge Apps ## `screenly edge-app rename` -Renames Edge App +Renames an Edge App **Usage:** `screenly edge-app rename [OPTIONS] --name <NAME>` ###### **Options:** -* `-p`, `--path <PATH>` — Path to the directory with the manifest. If not specified CLI will use the current working directory -* `-n`, `--name <NAME>` — Edge App name +* `-p`, `--path <PATH>` — Path to the directory with the manifest. Defaults to the current working directory +* `-n`, `--name <NAME>` — New name for the Edge App ## `screenly edge-app run` -Runs Edge App emulator +Runs the Edge App emulator **Usage:** `screenly edge-app run [OPTIONS]` ###### **Options:** -* `-p`, `--path <PATH>` — Path to the directory with the manifest. If not specified CLI will use the current working directory -* `-s`, `--secrets <SECRETS>` — Secrets to be passed to the Edge App in the form KEY=VALUE. Can be specified multiple times -* `-g`, `--generate-mock-data` — Generates mock data to be used with Edge App run +* `-p`, `--path <PATH>` — Path to the directory with the manifest. Defaults to the current working directory +* `-s`, `--secrets <SECRETS>` — Secrets to pass to the Edge App in the form KEY=VALUE. Can be specified multiple times +* `-g`, `--generate-mock-data` — Generates mock data for use with the Edge App emulator ## `screenly edge-app setting` -Settings commands +Edge App setting commands **Usage:** `screenly edge-app setting <COMMAND>` ###### **Subcommands:** * `list` — Lists Edge App settings -* `set` — Sets Edge App setting +* `set` — Sets an Edge App setting @@ -507,39 +537,39 @@ Lists Edge App settings ###### **Options:** -* `-p`, `--path <PATH>` — Path to the directory with the manifest. If not specified CLI will use the current working directory +* `-p`, `--path <PATH>` — Path to the directory with the manifest. Defaults to the current working directory * `-j`, `--json` — Enables JSON output ## `screenly edge-app setting set` -Sets Edge App setting +Sets an Edge App setting **Usage:** `screenly edge-app setting set [OPTIONS] <SETTING_PAIR>` ###### **Arguments:** -* `<SETTING_PAIR>` — Key value pair of the setting to be set in the form of `key=value` +* `<SETTING_PAIR>` — Key-value pair of the setting in the form `key=value` ###### **Options:** -* `-p`, `--path <PATH>` — Path to the directory with the manifest. If not specified CLI will use the current working directory +* `-p`, `--path <PATH>` — Path to the directory with the manifest. Defaults to the current working directory ## `screenly edge-app instance` -Instance commands +Edge App instance commands **Usage:** `screenly edge-app instance <COMMAND>` ###### **Subcommands:** * `list` — Lists Edge App instances -* `create` — Creates Edge App instance -* `delete` — Deletes Edge App instance -* `update` — Update Edge App instance based on changes in the instance.yml +* `create` — Creates an Edge App instance +* `delete` — Deletes an Edge App instance +* `update` — Updates an Edge App instance based on changes in instance.yml @@ -551,58 +581,58 @@ Lists Edge App instances ###### **Options:** -* `-p`, `--path <PATH>` — Path to the directory with the manifest. If not specified CLI will use the current working directory +* `-p`, `--path <PATH>` — Path to the directory with the manifest. Defaults to the current working directory * `-j`, `--json` — Enables JSON output ## `screenly edge-app instance create` -Creates Edge App instance +Creates an Edge App instance **Usage:** `screenly edge-app instance create [OPTIONS]` ###### **Options:** * `-n`, `--name <NAME>` — Name of the Edge App instance -* `-p`, `--path <PATH>` — Path to the directory with the manifest. If not specified CLI will use the current working directory +* `-p`, `--path <PATH>` — Path to the directory with the manifest. Defaults to the current working directory ## `screenly edge-app instance delete` -Deletes Edge App instance +Deletes an Edge App instance **Usage:** `screenly edge-app instance delete [OPTIONS]` ###### **Options:** -* `-p`, `--path <PATH>` — Path to the directory with the manifest. If not specified CLI will use the current working directory +* `-p`, `--path <PATH>` — Path to the directory with the manifest. Defaults to the current working directory ## `screenly edge-app instance update` -Update Edge App instance based on changes in the instance.yml +Updates an Edge App instance based on changes in instance.yml **Usage:** `screenly edge-app instance update [OPTIONS]` ###### **Options:** -* `-p`, `--path <PATH>` — Path to the directory with the manifest. If not specified CLI will use the current working directory +* `-p`, `--path <PATH>` — Path to the directory with the manifest. Defaults to the current working directory ## `screenly edge-app deploy` -Deploys assets and settings of the Edge App and release it +Deploys assets and settings of the Edge App and releases it **Usage:** `screenly edge-app deploy [OPTIONS]` ###### **Options:** -* `-p`, `--path <PATH>` — Path to the directory with the manifest. If not specified CLI will use the current working directory -* `-d`, `--delete-missing-settings <DELETE_MISSING_SETTINGS>` +* `-p`, `--path <PATH>` — Path to the directory with the manifest. Defaults to the current working directory +* `-d`, `--delete-missing-settings <DELETE_MISSING_SETTINGS>` — Delete settings that exist on the server but not in the manifest Possible values: `true`, `false` @@ -617,19 +647,27 @@ Deletes an Edge App. This cannot be undone ###### **Options:** -* `-p`, `--path <PATH>` — Path to the directory with the manifest. If not specified CLI will use the current working directory +* `-p`, `--path <PATH>` — Path to the directory with the manifest. Defaults to the current working directory ## `screenly edge-app validate` -Validates Edge App manifest file +Validates the Edge App manifest file **Usage:** `screenly edge-app validate [OPTIONS]` ###### **Options:** -* `-p`, `--path <PATH>` — Path to the directory with the manifest. If not specified CLI will use the current working directory +* `-p`, `--path <PATH>` — Path to the directory with the manifest. Defaults to the current working directory + + + +## `screenly mcp` + +Starts the MCP (Model Context Protocol) server on stdio for AI assistant integration + +**Usage:** `screenly mcp` diff --git a/src/api/edge_app/app.rs b/src/api/edge_app/app.rs index 20a3b3d..e22f0b0 100644 --- a/src/api/edge_app/app.rs +++ b/src/api/edge_app/app.rs @@ -75,7 +75,7 @@ impl Api { let apps = serde_json::from_value::<Vec<EdgeApp>>(response)?; if apps.is_empty() { Err(CommandError::AppNotFound(format!( - "Edge app with ID '{app_id}' not found." + "Edge App with ID '{app_id}' not found." ))) } else { Ok(apps[0].clone()) diff --git a/src/cli.rs b/src/cli.rs index b97f17a..91d62b4 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -51,9 +51,9 @@ pub struct Cli { #[derive(Subcommand)] pub enum Commands { - /// Logins with the token and stores it for further use if it's valid. You can set API_TOKEN environment variable to override used API token. + /// Logs in with the provided token and stores it for further use if valid. You can set the API_TOKEN environment variable to override the stored token. Login {}, - /// Logouts and removes stored token. + /// Logs out and removes the stored token. Logout {}, /// Screen related commands. #[command(subcommand)] @@ -67,6 +67,8 @@ pub enum Commands { /// Edge App related commands. #[command(subcommand)] EdgeApp(EdgeAppCommands), + /// Starts the MCP (Model Context Protocol) server on stdio for AI assistant integration. + Mcp {}, /// For generating `docs/CommandLineHelp.md`. #[clap(hide = true)] PrintHelpMarkdown {}, @@ -107,14 +109,49 @@ pub enum ScreenCommands { #[derive(Subcommand, Clone, PartialEq, Eq, PartialOrd, Ord)] pub enum PlaylistCommands { - ///Creates a new playlist. + /// Creates a new playlist. + /// + /// Playlists use a predicate DSL to control when they are shown. + /// The predicate is a boolean expression using these variables: + /// + /// $DATE - Current date as Unix timestamp in milliseconds + /// $TIME - Time of day in ms since midnight (0-86400000) + /// $WEEKDAY - Day of week (0=Sun, 1=Mon, ..., 6=Sat) + /// + /// Operators: =, <=, >=, <, >, AND, OR, NOT + /// Special: BETWEEN {min, max}, IN {val1, val2, ...} + /// + /// Time reference (ms): 32400000=9AM, 43200000=12PM, 61200000=5PM + /// + /// Examples: + /// TRUE - Always show + /// $WEEKDAY IN {1, 2, 3, 4, 5} - Weekdays only + /// $TIME BETWEEN {32400000, 61200000} - 9 AM to 5 PM + /// NOT $WEEKDAY IN {0, 6} - Exclude weekends Create { /// Enables JSON output. #[arg(short, long, action = clap::ArgAction::SetTrue)] json: Option<bool>, /// Title of the new playlist. title: String, - /// Predicate for the new playlist. If not specified it will be set to "TRUE". + /// Predicate expression controlling when the playlist is shown. + /// Uses DSL with $DATE, $TIME, $WEEKDAY variables. Default: "TRUE". + #[arg( + long_help = "Predicate expression controlling when the playlist is shown.\n\n\ + Variables:\n \ + $DATE - Unix timestamp in milliseconds\n \ + $TIME - Milliseconds since midnight (0-86400000)\n \ + $WEEKDAY - Day of week (0=Sun, 1=Mon, ..., 6=Sat)\n\n\ + Operators: =, <=, >=, <, >, AND, OR, NOT\n\ + Special: BETWEEN {min, max}, IN {val1, val2, ...}\n\n\ + Time reference: 32400000=9AM, 43200000=12PM, 61200000=5PM, 72000000=8PM\n\n\ + Examples:\n \ + TRUE - Always show\n \ + $WEEKDAY IN {1, 2, 3, 4, 5} - Weekdays only\n \ + $TIME BETWEEN {32400000, 61200000} - 9 AM to 5 PM\n \ + NOT $WEEKDAY IN {0, 6} - Exclude weekends\n\n\ + Default: TRUE" + )] predicate: Option<String>, }, /// Lists your playlists. @@ -142,7 +179,7 @@ pub enum PlaylistCommands { uuid: String, /// UUID of the asset. asset_uuid: String, - /// Duration of the playlist item in seconds. If not specified it will be set to 15 seconds. + /// Duration of the playlist item in seconds. Defaults to 15 seconds. duration: Option<u32>, }, /// Adds an asset to the beginning of the playlist. @@ -154,10 +191,10 @@ pub enum PlaylistCommands { uuid: String, /// UUID of the asset. asset_uuid: String, - /// Duration of the playlist item in seconds. If not specified it will be set to 15 seconds. + /// Duration of the playlist item in seconds. Defaults to 15 seconds. duration: Option<u32>, }, - /// Patches a given playlist. + /// Updates a playlist from JSON input on stdin. Update {}, } @@ -244,38 +281,38 @@ pub enum AssetCommands { path: String, }, - /// Sets HTTP headers for web asset. + /// Sets HTTP headers for a web asset. SetHeaders { - /// UUID of the web asset to set http headers. + /// UUID of the web asset. uuid: String, - /// HTTP headers in the following form `header1=value1[,header2=value2[,...]]`. This command - /// replaces all headers of the asset with the given headers (when an empty string is given, e.g. --set-headers "", - /// all existing headers are removed, if any) + /// HTTP headers in the form `header1=value1[,header2=value2[,...]]`. This command + /// replaces all headers of the asset with the given headers. Use an empty string + /// (e.g., --set-headers "") to remove all existing headers. #[arg(value_parser = parse_key_values::<Headers>)] headers: Headers, }, - /// Updates HTTP headers for web asset. + /// Updates HTTP headers for a web asset. UpdateHeaders { - /// UUID of the web asset to set http headers. + /// UUID of the web asset. uuid: String, - /// HTTP headers in the following form `header1=value1[,header2=value2[,...]]`. This command updates only the given headers (adding them if new), leaving any other headers unchanged. + /// HTTP headers in the form `header1=value1[,header2=value2[,...]]`. This command updates only the given headers (adding them if new), leaving other headers unchanged. #[arg(value_parser=parse_key_values::<Headers>)] headers: Headers, }, - /// Shortcut for setting up basic authentication headers. + /// Sets up basic authentication headers for a web asset. BasicAuth { - /// UUID of the web asset to set up basic authentication for. + /// UUID of the web asset. uuid: String, /// Basic authentication credentials in "user=password" form. #[arg(value_parser = parse_key_val)] credentials: (String, String), }, - /// Shortcut for setting up bearer authentication headers. + /// Sets up bearer authentication headers for a web asset. BearerAuth { - /// UUID of the web asset to set up basic authentication for. + /// UUID of the web asset. uuid: String, /// Bearer token. token: String, @@ -284,12 +321,12 @@ pub enum AssetCommands { #[derive(Subcommand, Clone, PartialEq, Eq, PartialOrd, Ord)] pub enum EdgeAppCommands { - /// Creates Edge App in the store. + /// Creates an Edge App in the store. Create { - /// Edge App name + /// Edge App name. #[arg(short, long)] name: String, - /// Path to the directory with the manifest. If not specified CLI will use the current working directory. + /// Path to the directory with the manifest. Defaults to the current working directory. #[arg(short, long)] path: Option<String>, /// Use an existing Edge App directory with the manifest and index.html. @@ -303,58 +340,59 @@ pub enum EdgeAppCommands { #[arg(short, long, action = clap::ArgAction::SetTrue)] json: Option<bool>, }, - /// Renames Edge App + /// Renames an Edge App. Rename { - /// Path to the directory with the manifest. If not specified CLI will use the current working directory. + /// Path to the directory with the manifest. Defaults to the current working directory. #[arg(short, long)] path: Option<String>, - /// Edge App name + /// New name for the Edge App. #[arg(short, long)] name: String, }, - /// Runs Edge App emulator. + /// Runs the Edge App emulator. Run { - /// Path to the directory with the manifest. If not specified CLI will use the current working directory. + /// Path to the directory with the manifest. Defaults to the current working directory. #[arg(short, long)] path: Option<String>, - /// Secrets to be passed to the Edge App in the form KEY=VALUE. Can be specified multiple times. + /// Secrets to pass to the Edge App in the form KEY=VALUE. Can be specified multiple times. #[arg(short, long, value_parser = parse_key_values::<Secrets>)] secrets: Option<Secrets>, - /// Generates mock data to be used with Edge App run + /// Generates mock data for use with the Edge App emulator. #[arg(short, long, action = clap::ArgAction::SetTrue)] generate_mock_data: Option<bool>, }, - /// Settings commands. + /// Edge App setting commands. #[command(subcommand)] Setting(EdgeAppSettingsCommands), - /// Instance commands. + /// Edge App instance commands. #[command(subcommand)] Instance(EdgeAppInstanceCommands), - /// Deploys assets and settings of the Edge App and release it. + /// Deploys assets and settings of the Edge App and releases it. Deploy { - /// Path to the directory with the manifest. If not specified CLI will use the current working directory. + /// Path to the directory with the manifest. Defaults to the current working directory. #[arg(short, long)] path: Option<String>, + /// Delete settings that exist on the server but not in the manifest. #[arg(short, long)] delete_missing_settings: Option<bool>, }, /// Deletes an Edge App. This cannot be undone. Delete { - /// Path to the directory with the manifest. If not specified CLI will use the current working directory. + /// Path to the directory with the manifest. Defaults to the current working directory. #[arg(short, long)] path: Option<String>, }, - /// Validates Edge App manifest file + /// Validates the Edge App manifest file. Validate { - /// Path to the directory with the manifest. If not specified CLI will use the current working directory. + /// Path to the directory with the manifest. Defaults to the current working directory. #[arg(short, long)] path: Option<String>, }, @@ -364,7 +402,7 @@ pub enum EdgeAppCommands { pub enum EdgeAppSettingsCommands { /// Lists Edge App settings. List { - /// Path to the directory with the manifest. If not specified CLI will use the current working directory. + /// Path to the directory with the manifest. Defaults to the current working directory. #[arg(short, long)] path: Option<String>, @@ -372,13 +410,13 @@ pub enum EdgeAppSettingsCommands { #[arg(short, long, action = clap::ArgAction::SetTrue)] json: Option<bool>, }, - /// Sets Edge App setting. + /// Sets an Edge App setting. Set { - /// Key value pair of the setting to be set in the form of `key=value`. + /// Key-value pair of the setting in the form `key=value`. #[arg(value_parser = parse_key_val)] setting_pair: (String, String), - /// Path to the directory with the manifest. If not specified CLI will use the current working directory. + /// Path to the directory with the manifest. Defaults to the current working directory. #[arg(short, long)] path: Option<String>, }, @@ -388,7 +426,7 @@ pub enum EdgeAppSettingsCommands { pub enum EdgeAppInstanceCommands { /// Lists Edge App instances. List { - /// Path to the directory with the manifest. If not specified CLI will use the current working directory. + /// Path to the directory with the manifest. Defaults to the current working directory. #[arg(short, long)] path: Option<String>, @@ -396,25 +434,25 @@ pub enum EdgeAppInstanceCommands { #[arg(short, long, action = clap::ArgAction::SetTrue)] json: Option<bool>, }, - /// Creates Edge App instance. + /// Creates an Edge App instance. Create { /// Name of the Edge App instance. #[arg(short, long)] name: Option<String>, - /// Path to the directory with the manifest. If not specified CLI will use the current working directory. + /// Path to the directory with the manifest. Defaults to the current working directory. #[arg(short, long)] path: Option<String>, }, - /// Deletes Edge App instance. + /// Deletes an Edge App instance. Delete { - /// Path to the directory with the manifest. If not specified CLI will use the current working directory. + /// Path to the directory with the manifest. Defaults to the current working directory. #[arg(short, long)] path: Option<String>, }, - /// Update Edge App instance based on changes in the instance.yml. + /// Updates an Edge App instance based on changes in instance.yml. Update { - /// Path to the directory with the manifest. If not specified CLI will use the current working directory. + /// Path to the directory with the manifest. Defaults to the current working directory. #[arg(short, long)] path: Option<String>, }, @@ -526,12 +564,33 @@ pub fn handle_cli(cli: &Cli) { info!("Logout successful."); std::process::exit(0); } + Commands::Mcp {} => { + handle_cli_mcp_command(); + } Commands::PrintHelpMarkdown {} => { clap_markdown::print_help_markdown::<Cli>(); } } } +pub fn handle_cli_mcp_command() { + use crate::mcp::ScreenlyMcpServer; + + let server = match ScreenlyMcpServer::new() { + Ok(s) => s, + Err(e) => { + error!("Failed to initialize MCP server: {}", e); + std::process::exit(1); + } + }; + + let rt = tokio::runtime::Runtime::new().expect("Failed to create Tokio runtime"); + if let Err(e) = rt.block_on(server.run()) { + error!("MCP server error: {}", e); + std::process::exit(1); + } +} + fn get_user_input() -> String { let stdin = io::stdin(); let mut user_input = String::new(); @@ -837,17 +896,17 @@ pub fn handle_cli_edge_app_command(command: &EdgeAppCommands) { let manifest_path = match transform_edge_app_path_to_manifest(path) { Ok(path) => path, Err(e) => { - eprintln!("Failed to create edge app: {e}."); + eprintln!("Failed to create Edge App: {e}."); std::process::exit(1); } }; match create_func(&edge_app_command, name, manifest_path.as_path()) { Ok(()) => { - println!("Edge app successfully created."); + println!("Edge App successfully created."); } Err(e) => { - eprintln!("Failed to publish edge app manifest: {e}."); + eprintln!("Failed to publish Edge App manifest: {e}."); std::process::exit(1); } } @@ -861,10 +920,10 @@ pub fn handle_cli_edge_app_command(command: &EdgeAppCommands) { delete_missing_settings, } => match edge_app_command.deploy(path.clone(), *delete_missing_settings) { Ok(revision) => { - println!("Edge app successfully deployed. Revision: {revision}."); + println!("Edge App successfully deployed. Revision: {revision}."); } Err(e) => { - eprintln!("Failed to upload edge app: {e}."); + eprintln!("Failed to upload Edge App: {e}."); std::process::exit(1); } }, @@ -875,10 +934,10 @@ pub fn handle_cli_edge_app_command(command: &EdgeAppCommands) { EdgeAppSettingsCommands::Set { setting_pair, path } => { match edge_app_command.set_setting(path.clone(), &setting_pair.0, &setting_pair.1) { Ok(()) => { - println!("Edge app setting successfully set."); + println!("Edge App setting successfully set."); } Err(e) => { - eprintln!("Failed to set edge app setting: {e}"); + eprintln!("Failed to set Edge App setting: {e}"); std::process::exit(1); } } @@ -914,7 +973,7 @@ pub fn handle_cli_edge_app_command(command: &EdgeAppCommands) { let manifest_path = match transform_edge_app_path_to_manifest(path) { Ok(path) => path, Err(e) => { - eprintln!("Failed to delete edge app: {e}."); + eprintln!("Failed to delete Edge App: {e}."); std::process::exit(1); } }; @@ -941,16 +1000,16 @@ pub fn handle_cli_edge_app_command(command: &EdgeAppCommands) { let actual_app_id = match edge_app_command.get_app_id(path.clone()) { Ok(id) => id, Err(e) => { - error!("Error calling delete Edge App: {e}"); + error!("Error renaming Edge App: {e}"); std::process::exit(1); } }; match edge_app_command.update_name(&actual_app_id, name) { Ok(()) => { - println!("Edge app successfully updated."); + println!("Edge App successfully renamed."); } Err(e) => { - eprintln!("Failed to update edge app: {e}."); + eprintln!("Failed to rename Edge App: {e}."); std::process::exit(1); } } @@ -1039,21 +1098,21 @@ pub fn handle_cli_edge_app_command(command: &EdgeAppCommands) { let manifest = match EdgeAppManifest::new(&manifest_path) { Ok(manifest) => manifest, Err(e) => { - eprintln!("Failed to validate edge app manifest file: {e}."); + eprintln!("Failed to validate Edge App manifest file: {e}."); std::process::exit(1); } }; let instance_manifest = match InstanceManifest::new(&instance_manifest_path) { Ok(manifest) => manifest, Err(e) => { - eprintln!("Failed to validate edge app instance manifest file: {e}."); + eprintln!("Failed to validate Edge App instance manifest file: {e}."); std::process::exit(1); } }; match validate_manifests_dependacies(&manifest, &instance_manifest) { Ok(()) => { - println!("Manifests dependancies are valid."); + println!("Manifest dependencies are valid."); } Err(e) => { eprintln!("{e}"); @@ -1085,14 +1144,14 @@ pub fn handle_cli_edge_app_command(command: &EdgeAppCommands) { }; let new_name = match name { Some(name) => name, - None => "Edge App instance created by Screenly CLI", + None => "New Edge App instance", }; let instance_manifest_path = match transform_instance_path_to_instance_manifest(path) { Ok(path) => path, Err(e) => { - eprintln!("Failed to create edge app instance: {e}."); + eprintln!("Failed to create Edge App instance: {e}."); std::process::exit(1); } }; @@ -1103,10 +1162,10 @@ pub fn handle_cli_edge_app_command(command: &EdgeAppCommands) { new_name, ) { Ok(_some_id) => { - println!("Edge app instance successfully created."); + println!("Edge App instance successfully created."); } Err(e) => { - eprintln!("Failed to create edge app instance: {e}."); + eprintln!("Failed to create Edge App instance: {e}."); std::process::exit(1); } } @@ -1126,12 +1185,12 @@ pub fn handle_cli_edge_app_command(command: &EdgeAppCommands) { Ok(path) => match path.to_str() { Some(path) => path.to_string(), None => { - eprintln!("Failed to delete edge app instance. Invalid path."); + eprintln!("Failed to delete Edge App instance: invalid path."); std::process::exit(1); } }, Err(e) => { - eprintln!("Failed to delete edge app instance. {e:?}"); + eprintln!("Failed to delete Edge App instance: {e:?}"); std::process::exit(1); } }; @@ -1140,10 +1199,10 @@ pub fn handle_cli_edge_app_command(command: &EdgeAppCommands) { .delete_instance(&actual_installation_id, instance_manifest_path) { Ok(()) => { - println!("Edge app instance successfully deleted."); + println!("Edge App instance successfully deleted."); } Err(e) => { - eprintln!("Failed to delete edge app instance: {e}."); + eprintln!("Failed to delete Edge App instance: {e}."); std::process::exit(1); } } @@ -1151,10 +1210,10 @@ pub fn handle_cli_edge_app_command(command: &EdgeAppCommands) { EdgeAppInstanceCommands::Update { path } => { match edge_app_command.update_instance(path.clone()) { Ok(()) => { - println!("Edge app instance successfully updated."); + println!("Edge App instance successfully updated."); } Err(e) => { - eprintln!("Failed to update edge app instance: {e}."); + eprintln!("Failed to update Edge App instance: {e}."); std::process::exit(1); } } diff --git a/src/commands/edge_app/app.rs b/src/commands/edge_app/app.rs index d88622e..6154163 100644 --- a/src/commands/edge_app/app.rs +++ b/src/commands/edge_app/app.rs @@ -33,7 +33,7 @@ use crate::commands::{CommandError, EdgeApps}; impl EdgeAppCommand { pub fn create(&self, name: &str, path: &Path) -> Result<(), CommandError> { let parent_dir_path = path.parent().ok_or(CommandError::FileSystemError( - "Can not obtain edge app root directory.".to_owned(), + "Cannot obtain Edge App root directory.".to_owned(), ))?; let index_html_path = parent_dir_path.join("index.html"); @@ -90,7 +90,7 @@ impl EdgeAppCommand { pub fn create_in_place(&self, name: &str, path: &Path) -> Result<(), CommandError> { let parent_dir_path = path.parent().ok_or(CommandError::FileSystemError( - "Can not obtain edge app root directory.".to_owned(), + "Cannot obtain Edge App root directory.".to_owned(), ))?; let index_html_path = parent_dir_path.join("index.html"); @@ -195,7 +195,7 @@ impl EdgeAppCommand { self.ensure_assets_processing_finished(&actual_app_id, revision)?; // now we freeze it by publishing it self.api.publish_version(&actual_app_id, revision)?; - debug!("Edge app published."); + debug!("Edge App published."); self.promote_version(&actual_app_id, revision, "stable")?; @@ -428,14 +428,14 @@ impl EdgeAppCommand { .collect(), )?; - debug!("Uploading edge app assets"); + debug!("Uploading Edge App assets"); let files_to_upload = changed_files.get_files_to_upload(copied_signatures); if files_to_upload.is_empty() { debug!("No files to upload"); return Ok(()); } - debug!("Uploading edge app files: {files_to_upload:#?}"); + debug!("Uploading Edge App files: {files_to_upload:#?}"); let file_paths: Vec<PathBuf> = files_to_upload .iter() .map(|file| edge_app_dir.join(&file.path)) @@ -1172,7 +1172,7 @@ mod tests { ); // get_entrypoint_mock.assert(); - last_versions_mock.assert_hits(2); + last_versions_mock.assert_calls(2); assets_mock.assert(); file_tree_from_version_mock.assert(); settings_mock.assert(); @@ -1627,13 +1627,13 @@ mod tests { let upload_assets_mock = mock_server.mock(|when, then| { when.method(POST) .path("/v4/assets") - .body_contains("test222"); + .body_includes("test222"); then.status(201).body(""); }); let upload_assets_mock2 = mock_server.mock(|when, then| { when.method(POST) .path("/v4/assets") - .body_contains("test333"); + .body_includes("test333"); then.status(201).body(""); }); @@ -1785,7 +1785,7 @@ mod tests { &changed_files, ); - upload_assets_mock.assert_hits(0); + upload_assets_mock.assert_calls(0); copy_assets_mock.assert(); assert!(result.is_ok()); diff --git a/src/commands/edge_app/server.rs b/src/commands/edge_app/server.rs index 8c26a78..ccff49d 100644 --- a/src/commands/edge_app/server.rs +++ b/src/commands/edge_app/server.rs @@ -128,7 +128,7 @@ async fn generate_content( fs::read_to_string(&file_path).unwrap_or("".to_string()) } else { eprintln!( - "Mock data does not exist. Use \"edge-app run --generate-mock-data\" to create mock data." + "Mock data does not exist. Use \"screenly edge-app run --generate-mock-data\" to create mock data." ); return Err(warp::reject::not_found()); }; diff --git a/src/commands/edge_app/setting.rs b/src/commands/edge_app/setting.rs index 14945f4..2dd4880 100644 --- a/src/commands/edge_app/setting.rs +++ b/src/commands/edge_app/setting.rs @@ -1,8 +1,6 @@ -use std::collections::HashMap; use std::str; use log::debug; -use serde::{Deserialize, Serialize}; use crate::api::edge_app::setting::Setting; use crate::commands::edge_app::EdgeAppCommand; @@ -32,14 +30,6 @@ impl EdgeAppCommand { let _is_setting_global = self.api.is_setting_global(&app_id, setting_key)?; - #[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)] - struct SettingValue { - name: String, - #[serde(rename = "type")] - type_field: String, - edge_app_setting_values: Vec<HashMap<String, String>>, - } - let server_setting_value = { if _is_setting_global { self.api.get_global_setting(&app_id, setting_key)? diff --git a/src/commands/playlist.rs b/src/commands/playlist.rs index c1f6937..a907107 100644 --- a/src/commands/playlist.rs +++ b/src/commands/playlist.rs @@ -575,7 +575,7 @@ mod tests { delete_mock.assert(); post_mock.assert(); - get_mock.assert_hits(2); + get_mock.assert_calls(2); get_items_mock.assert(); assert!(result.is_ok()); } diff --git a/src/main.rs b/src/main.rs index ce4e956..7733f59 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,6 +2,7 @@ mod api; mod authentication; mod cli; mod commands; +mod mcp; mod pb_signature; mod signature; diff --git a/src/mcp/mod.rs b/src/mcp/mod.rs new file mode 100644 index 0000000..c449fc5 --- /dev/null +++ b/src/mcp/mod.rs @@ -0,0 +1,12 @@ +//! MCP (Model Context Protocol) server implementation for Screenly CLI. +//! +//! This module provides an MCP server that exposes the Screenly v4 API as tools +//! that can be used by AI assistants. + +pub mod server; +pub mod tools; + +#[cfg(test)] +mod tests; + +pub use server::ScreenlyMcpServer; diff --git a/src/mcp/server.rs b/src/mcp/server.rs new file mode 100644 index 0000000..3ef881b --- /dev/null +++ b/src/mcp/server.rs @@ -0,0 +1,630 @@ +//! MCP server handler implementation. + +use std::sync::Arc; + +use rmcp::handler::server::router::tool::ToolRouter; +use rmcp::handler::server::wrapper::Parameters; +use rmcp::model::{ServerCapabilities, ServerInfo}; +use rmcp::{schemars, tool, tool_handler, tool_router, ServiceExt}; +use serde::Deserialize; +use serde_json::json; + +use crate::authentication::Authentication; +use crate::mcp::tools::asset::AssetTools; +use crate::mcp::tools::asset_group::AssetGroupTools; +use crate::mcp::tools::edge_app::EdgeAppTools; +use crate::mcp::tools::label::LabelTools; +use crate::mcp::tools::playlist::PlaylistTools; +use crate::mcp::tools::playlist_item::PlaylistItemTools; +use crate::mcp::tools::screen::ScreenTools; +use crate::mcp::tools::shared_playlist::SharedPlaylistTools; + +// ============ PARAMETER STRUCTS ============ + +#[derive(Debug, Deserialize, schemars::JsonSchema)] +pub struct UuidParam { + #[schemars(description = "UUID of the resource")] + pub uuid: String, +} + +#[derive(Debug, Deserialize, schemars::JsonSchema)] +pub struct AssetCreateParam { + #[schemars(description = "Title of the asset")] + pub title: String, + #[schemars(description = "Source URL of the asset (web page, image, or video URL)")] + pub source_url: String, +} + +#[derive(Debug, Deserialize, schemars::JsonSchema)] +pub struct AssetUpdateParam { + #[schemars(description = "UUID of the asset to update")] + pub uuid: String, + #[schemars(description = "New title for the asset")] + pub title: Option<String>, + #[schemars(description = "JavaScript code to inject into web assets")] + pub js_injection: Option<String>, + #[schemars(description = "HTTP headers as JSON object")] + pub headers: Option<String>, +} + +#[derive(Debug, Deserialize, schemars::JsonSchema)] +pub struct TitleParam { + #[schemars(description = "Title")] + pub title: String, +} + +#[derive(Debug, Deserialize, schemars::JsonSchema)] +pub struct AssetGroupUpdateParam { + #[schemars(description = "UUID of the asset group")] + pub uuid: String, + #[schemars(description = "New title")] + pub title: String, +} + +/// Predicate DSL documentation for playlist scheduling. +/// +/// Predicates are boolean expressions that control when a playlist is shown. +/// They use three context variables: +/// - `$DATE`: Current date as Unix timestamp in milliseconds +/// - `$TIME`: Time of day in milliseconds since midnight (0-86400000) +/// - `$WEEKDAY`: Day of week (0=Sunday, 1=Monday, ..., 6=Saturday) +/// +/// Operators: `=`, `<=`, `>=`, `<`, `>`, `AND`, `OR`, `NOT` +/// Special: `BETWEEN {min, max}`, `IN {val1, val2, ...}` +/// +/// Examples: +/// - `TRUE` - Always show +/// - `$WEEKDAY IN {1, 2, 3, 4, 5}` - Weekdays only +/// - `$TIME BETWEEN {32400000, 61200000}` - 9 AM to 5 PM +/// - `$TIME >= 32400000 AND $TIME <= 61200000 AND NOT $WEEKDAY IN {0, 6}` - Business hours +const _PREDICATE_DSL_DOCS: () = (); + +#[derive(Debug, Deserialize, schemars::JsonSchema)] +pub struct PlaylistCreateParam { + #[schemars(description = "Title of the playlist")] + pub title: String, + #[schemars( + description = "Predicate expression for when to show the playlist. Uses DSL with $DATE (ms timestamp), $TIME (ms since midnight, 0-86400000), $WEEKDAY (0=Sun to 6=Sat). Examples: 'TRUE' (always), '$WEEKDAY IN {1,2,3,4,5}' (weekdays), '$TIME BETWEEN {32400000, 61200000}' (9AM-5PM). Operators: =, <=, >=, <, >, AND, OR, NOT, BETWEEN {min,max}, IN {values}. Default: TRUE" + )] + pub predicate: Option<String>, + #[schemars(description = "Whether this is a priority playlist")] + pub priority: Option<bool>, + #[schemars(description = "Whether the playlist is enabled")] + pub is_enabled: Option<bool>, +} + +#[derive(Debug, Deserialize, schemars::JsonSchema)] +pub struct PlaylistUpdateParam { + #[schemars(description = "UUID of the playlist")] + pub uuid: String, + #[schemars(description = "New title")] + pub title: Option<String>, + #[schemars( + description = "New predicate expression. Uses DSL with $DATE (ms timestamp), $TIME (ms since midnight, 0-86400000), $WEEKDAY (0=Sun to 6=Sat). Examples: 'TRUE' (always), '$WEEKDAY IN {1,2,3,4,5}' (weekdays), '$TIME BETWEEN {32400000, 61200000}' (9AM-5PM). Operators: =, <=, >=, <, >, AND, OR, NOT, BETWEEN {min,max}, IN {values}" + )] + pub predicate: Option<String>, + #[schemars(description = "Set as priority playlist")] + pub priority: Option<bool>, + #[schemars(description = "Enable or disable")] + pub is_enabled: Option<bool>, +} + +#[derive(Debug, Deserialize, schemars::JsonSchema)] +pub struct PlaylistItemCreateParam { + #[schemars(description = "UUID of the playlist")] + pub playlist_uuid: String, + #[schemars(description = "UUID of the asset to add")] + pub asset_uuid: String, + #[schemars(description = "Duration in seconds")] + pub duration: u32, + #[schemars(description = "Position in the playlist")] + pub position: Option<u64>, +} + +#[derive(Debug, Deserialize, schemars::JsonSchema)] +pub struct PlaylistItemUpdateParam { + #[schemars(description = "UUID of the playlist")] + pub playlist_uuid: String, + #[schemars(description = "UUID of the playlist item")] + pub item_uuid: String, + #[schemars(description = "New duration in seconds")] + pub duration: Option<u32>, + #[schemars(description = "New position")] + pub position: Option<u64>, +} + +#[derive(Debug, Deserialize, schemars::JsonSchema)] +pub struct PlaylistItemDeleteParam { + #[schemars(description = "UUID of the playlist")] + pub playlist_uuid: String, + #[schemars(description = "UUID of the playlist item")] + pub item_uuid: String, +} + +#[derive(Debug, Deserialize, schemars::JsonSchema)] +pub struct NameParam { + #[schemars(description = "Name")] + pub name: String, +} + +#[derive(Debug, Deserialize, schemars::JsonSchema)] +pub struct LabelUpdateParam { + #[schemars(description = "UUID of the label")] + pub uuid: String, + #[schemars(description = "New name")] + pub name: String, +} + +#[derive(Debug, Deserialize, schemars::JsonSchema)] +pub struct LabelScreenParam { + #[schemars(description = "UUID of the label")] + pub label_uuid: String, + #[schemars(description = "UUID of the screen")] + pub screen_uuid: String, +} + +#[derive(Debug, Deserialize, schemars::JsonSchema)] +pub struct LabelPlaylistParam { + #[schemars(description = "UUID of the label")] + pub label_uuid: String, + #[schemars(description = "UUID of the playlist")] + pub playlist_uuid: String, +} + +#[derive(Debug, Deserialize, schemars::JsonSchema)] +pub struct SharedPlaylistParam { + #[schemars(description = "UUID of the playlist")] + pub playlist_uuid: String, + #[schemars(description = "UUID of the team")] + pub team_uuid: String, +} + +#[derive(Debug, Deserialize, schemars::JsonSchema)] +pub struct AppUuidParam { + #[schemars(description = "UUID of the Edge App")] + pub app_uuid: String, +} + +// ============ SERVER STRUCT ============ + +/// MCP Server for Screenly API +#[derive(Clone)] +pub struct ScreenlyMcpServer { + auth: Arc<Authentication>, + tool_router: ToolRouter<Self>, +} + +impl ScreenlyMcpServer { + /// Create a new ScreenlyMcpServer instance. + pub fn new() -> Result<Self, crate::authentication::AuthenticationError> { + let auth = Authentication::new()?; + Ok(Self { + auth: Arc::new(auth), + tool_router: Self::tool_router(), + }) + } + + /// Run the MCP server on stdio transport. + pub async fn run(self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> { + let service = self + .serve(rmcp::transport::stdio()) + .await + .inspect_err(|e| { + log::error!("Failed to start MCP server: {}", e); + })?; + service.waiting().await?; + Ok(()) + } +} + +// ============ TOOL IMPLEMENTATIONS ============ + +#[tool_router] +impl ScreenlyMcpServer { + // ============ SCREEN TOOLS ============ + + #[tool(description = "List all screens with their status, hardware info, and sync state.")] + fn screen_list(&self) -> String { + match ScreenTools::list(&self.auth) { + Ok(result) => result, + Err(e) => json!({"error": e}).to_string(), + } + } + + #[tool(description = "Get a screen by UUID.")] + fn screen_get(&self, Parameters(UuidParam { uuid }): Parameters<UuidParam>) -> String { + match ScreenTools::get(&self.auth, &uuid) { + Ok(result) => result, + Err(e) => json!({"error": e}).to_string(), + } + } + + // ============ ASSET TOOLS ============ + + #[tool(description = "List all assets with their type, status, and metadata.")] + fn asset_list(&self) -> String { + match AssetTools::list(&self.auth) { + Ok(result) => result, + Err(e) => json!({"error": e}).to_string(), + } + } + + #[tool(description = "Get an asset by UUID.")] + fn asset_get(&self, Parameters(UuidParam { uuid }): Parameters<UuidParam>) -> String { + match AssetTools::get(&self.auth, &uuid) { + Ok(result) => result, + Err(e) => json!({"error": e}).to_string(), + } + } + + #[tool(description = "Create a new asset from a URL. Supports web pages, images, and videos.")] + fn asset_create( + &self, + Parameters(AssetCreateParam { title, source_url }): Parameters<AssetCreateParam>, + ) -> String { + match AssetTools::create(&self.auth, &title, &source_url) { + Ok(result) => result, + Err(e) => json!({"error": e}).to_string(), + } + } + + #[tool(description = "Update an asset's properties (title, js_injection, headers).")] + fn asset_update( + &self, + Parameters(AssetUpdateParam { + uuid, + title, + js_injection, + headers, + }): Parameters<AssetUpdateParam>, + ) -> String { + match AssetTools::update(&self.auth, &uuid, title, js_injection, headers) { + Ok(result) => result, + Err(e) => json!({"error": e}).to_string(), + } + } + + #[tool(description = "Delete an asset by UUID.")] + fn asset_delete(&self, Parameters(UuidParam { uuid }): Parameters<UuidParam>) -> String { + match AssetTools::delete(&self.auth, &uuid) { + Ok(result) => result, + Err(e) => json!({"error": e}).to_string(), + } + } + + // ============ ASSET GROUP TOOLS ============ + + #[tool(description = "List all asset groups (folders for organizing assets).")] + fn asset_group_list(&self) -> String { + match AssetGroupTools::list(&self.auth) { + Ok(result) => result, + Err(e) => json!({"error": e}).to_string(), + } + } + + #[tool(description = "Create a new asset group.")] + fn asset_group_create( + &self, + Parameters(TitleParam { title }): Parameters<TitleParam>, + ) -> String { + match AssetGroupTools::create(&self.auth, &title) { + Ok(result) => result, + Err(e) => json!({"error": e}).to_string(), + } + } + + #[tool(description = "Update an asset group.")] + fn asset_group_update( + &self, + Parameters(AssetGroupUpdateParam { uuid, title }): Parameters<AssetGroupUpdateParam>, + ) -> String { + match AssetGroupTools::update(&self.auth, &uuid, &title) { + Ok(result) => result, + Err(e) => json!({"error": e}).to_string(), + } + } + + #[tool(description = "Delete an asset group. WARNING: Also deletes all assets in the group.")] + fn asset_group_delete(&self, Parameters(UuidParam { uuid }): Parameters<UuidParam>) -> String { + match AssetGroupTools::delete(&self.auth, &uuid) { + Ok(result) => result, + Err(e) => json!({"error": e}).to_string(), + } + } + + // ============ PLAYLIST TOOLS ============ + + #[tool(description = "List all playlists.")] + fn playlist_list(&self) -> String { + match PlaylistTools::list(&self.auth) { + Ok(result) => result, + Err(e) => json!({"error": e}).to_string(), + } + } + + #[tool(description = "Create a new playlist.")] + fn playlist_create( + &self, + Parameters(PlaylistCreateParam { + title, + predicate, + priority, + is_enabled, + }): Parameters<PlaylistCreateParam>, + ) -> String { + match PlaylistTools::create(&self.auth, &title, predicate, priority, is_enabled) { + Ok(result) => result, + Err(e) => json!({"error": e}).to_string(), + } + } + + #[tool(description = "Update a playlist.")] + fn playlist_update( + &self, + Parameters(PlaylistUpdateParam { + uuid, + title, + predicate, + priority, + is_enabled, + }): Parameters<PlaylistUpdateParam>, + ) -> String { + match PlaylistTools::update(&self.auth, &uuid, title, predicate, priority, is_enabled) { + Ok(result) => result, + Err(e) => json!({"error": e}).to_string(), + } + } + + #[tool(description = "Delete a playlist by UUID.")] + fn playlist_delete(&self, Parameters(UuidParam { uuid }): Parameters<UuidParam>) -> String { + match PlaylistTools::delete(&self.auth, &uuid) { + Ok(result) => result, + Err(e) => json!({"error": e}).to_string(), + } + } + + // ============ PLAYLIST ITEM TOOLS ============ + + #[tool(description = "List all items in a playlist.")] + fn playlist_item_list(&self, Parameters(UuidParam { uuid }): Parameters<UuidParam>) -> String { + match PlaylistItemTools::list(&self.auth, &uuid) { + Ok(result) => result, + Err(e) => json!({"error": e}).to_string(), + } + } + + #[tool(description = "Add an asset to a playlist.")] + fn playlist_item_create( + &self, + Parameters(PlaylistItemCreateParam { + playlist_uuid, + asset_uuid, + duration, + position, + }): Parameters<PlaylistItemCreateParam>, + ) -> String { + match PlaylistItemTools::create(&self.auth, &playlist_uuid, &asset_uuid, duration, position) + { + Ok(result) => result, + Err(e) => json!({"error": e}).to_string(), + } + } + + #[tool(description = "Update a playlist item (duration, position).")] + fn playlist_item_update( + &self, + Parameters(PlaylistItemUpdateParam { + playlist_uuid, + item_uuid, + duration, + position, + }): Parameters<PlaylistItemUpdateParam>, + ) -> String { + match PlaylistItemTools::update(&self.auth, &playlist_uuid, &item_uuid, duration, position) + { + Ok(result) => result, + Err(e) => json!({"error": e}).to_string(), + } + } + + #[tool(description = "Remove an item from a playlist.")] + fn playlist_item_delete( + &self, + Parameters(PlaylistItemDeleteParam { + playlist_uuid, + item_uuid, + }): Parameters<PlaylistItemDeleteParam>, + ) -> String { + match PlaylistItemTools::delete(&self.auth, &playlist_uuid, &item_uuid) { + Ok(result) => result, + Err(e) => json!({"error": e}).to_string(), + } + } + + // ============ LABEL TOOLS ============ + + #[tool(description = "List all labels. Labels group screens and target playlists.")] + fn label_list(&self) -> String { + match LabelTools::list(&self.auth) { + Ok(result) => result, + Err(e) => json!({"error": e}).to_string(), + } + } + + #[tool(description = "Create a new label.")] + fn label_create(&self, Parameters(NameParam { name }): Parameters<NameParam>) -> String { + match LabelTools::create(&self.auth, &name) { + Ok(result) => result, + Err(e) => json!({"error": e}).to_string(), + } + } + + #[tool(description = "Update a label.")] + fn label_update( + &self, + Parameters(LabelUpdateParam { uuid, name }): Parameters<LabelUpdateParam>, + ) -> String { + match LabelTools::update(&self.auth, &uuid, &name) { + Ok(result) => result, + Err(e) => json!({"error": e}).to_string(), + } + } + + #[tool(description = "Delete a label.")] + fn label_delete(&self, Parameters(UuidParam { uuid }): Parameters<UuidParam>) -> String { + match LabelTools::delete(&self.auth, &uuid) { + Ok(result) => result, + Err(e) => json!({"error": e}).to_string(), + } + } + + #[tool(description = "Attach a label to a screen.")] + fn label_link_screen( + &self, + Parameters(LabelScreenParam { + label_uuid, + screen_uuid, + }): Parameters<LabelScreenParam>, + ) -> String { + match LabelTools::link_screen(&self.auth, &label_uuid, &screen_uuid) { + Ok(result) => result, + Err(e) => json!({"error": e}).to_string(), + } + } + + #[tool(description = "Remove a label from a screen.")] + fn label_unlink_screen( + &self, + Parameters(LabelScreenParam { + label_uuid, + screen_uuid, + }): Parameters<LabelScreenParam>, + ) -> String { + match LabelTools::unlink_screen(&self.auth, &label_uuid, &screen_uuid) { + Ok(result) => result, + Err(e) => json!({"error": e}).to_string(), + } + } + + #[tool(description = "Attach a label to a playlist.")] + fn label_link_playlist( + &self, + Parameters(LabelPlaylistParam { + label_uuid, + playlist_uuid, + }): Parameters<LabelPlaylistParam>, + ) -> String { + match LabelTools::link_playlist(&self.auth, &label_uuid, &playlist_uuid) { + Ok(result) => result, + Err(e) => json!({"error": e}).to_string(), + } + } + + #[tool(description = "Remove a label from a playlist.")] + fn label_unlink_playlist( + &self, + Parameters(LabelPlaylistParam { + label_uuid, + playlist_uuid, + }): Parameters<LabelPlaylistParam>, + ) -> String { + match LabelTools::unlink_playlist(&self.auth, &label_uuid, &playlist_uuid) { + Ok(result) => result, + Err(e) => json!({"error": e}).to_string(), + } + } + + // ============ SHARED PLAYLIST TOOLS ============ + + #[tool(description = "List shared playlists.")] + fn shared_playlist_list(&self) -> String { + match SharedPlaylistTools::list(&self.auth) { + Ok(result) => result, + Err(e) => json!({"error": e}).to_string(), + } + } + + #[tool(description = "Share a playlist with another team.")] + fn shared_playlist_create( + &self, + Parameters(SharedPlaylistParam { + playlist_uuid, + team_uuid, + }): Parameters<SharedPlaylistParam>, + ) -> String { + match SharedPlaylistTools::create(&self.auth, &playlist_uuid, &team_uuid) { + Ok(result) => result, + Err(e) => json!({"error": e}).to_string(), + } + } + + #[tool(description = "Unshare a playlist from a team.")] + fn shared_playlist_delete( + &self, + Parameters(SharedPlaylistParam { + playlist_uuid, + team_uuid, + }): Parameters<SharedPlaylistParam>, + ) -> String { + match SharedPlaylistTools::delete(&self.auth, &playlist_uuid, &team_uuid) { + Ok(result) => result, + Err(e) => json!({"error": e}).to_string(), + } + } + + // ============ EDGE APP TOOLS ============ + + #[tool(description = "List all Edge Apps.")] + fn edge_app_list(&self) -> String { + match EdgeAppTools::list(&self.auth) { + Ok(result) => result, + Err(e) => json!({"error": e}).to_string(), + } + } + + #[tool(description = "List settings for an Edge App.")] + fn edge_app_list_settings( + &self, + Parameters(AppUuidParam { app_uuid }): Parameters<AppUuidParam>, + ) -> String { + match EdgeAppTools::list_settings(&self.auth, &app_uuid) { + Ok(result) => result, + Err(e) => json!({"error": e}).to_string(), + } + } + + #[tool(description = "List instances of an Edge App.")] + fn edge_app_list_instances( + &self, + Parameters(AppUuidParam { app_uuid }): Parameters<AppUuidParam>, + ) -> String { + match EdgeAppTools::list_instances(&self.auth, &app_uuid) { + Ok(result) => result, + Err(e) => json!({"error": e}).to_string(), + } + } +} + +// ============ SERVER HANDLER ============ + +#[tool_handler] +impl rmcp::ServerHandler for ScreenlyMcpServer { + fn get_info(&self) -> ServerInfo { + ServerInfo { + instructions: Some( + "Screenly MCP Server - Manage digital signage screens, assets, and playlists. \ + Use API_TOKEN environment variable or ~/.screenly file for authentication.\n\n\ + PLAYLIST PREDICATES: Playlists use a predicate DSL for scheduling. Variables: \ + $DATE (Unix ms), $TIME (ms since midnight, 0-86400000), $WEEKDAY (0=Sun..6=Sat). \ + Operators: =, <=, >=, <, >, AND, OR, NOT, BETWEEN {min,max}, IN {values}. \ + Examples: 'TRUE' (always show), '$WEEKDAY IN {1,2,3,4,5}' (weekdays only), \ + '$TIME BETWEEN {32400000, 61200000}' (9AM-5PM), \ + '$TIME >= 32400000 AND $TIME <= 61200000 AND NOT $WEEKDAY IN {0, 6}' (business hours). \ + Time reference: 32400000=9AM, 43200000=12PM, 61200000=5PM, 72000000=8PM." + .to_string(), + ), + capabilities: ServerCapabilities::builder().enable_tools().build(), + ..Default::default() + } + } +} diff --git a/src/mcp/tests.rs b/src/mcp/tests.rs new file mode 100644 index 0000000..86a4a0b --- /dev/null +++ b/src/mcp/tests.rs @@ -0,0 +1,637 @@ +//! Unit tests for MCP tools. + +use httpmock::Method::{DELETE, GET, PATCH, POST}; +use httpmock::MockServer; +use serde_json::json; + +use crate::authentication::{Authentication, Config}; +use crate::mcp::tools::asset::AssetTools; +use crate::mcp::tools::asset_group::AssetGroupTools; +use crate::mcp::tools::edge_app::EdgeAppTools; +use crate::mcp::tools::label::LabelTools; +use crate::mcp::tools::playlist::PlaylistTools; +use crate::mcp::tools::playlist_item::PlaylistItemTools; +use crate::mcp::tools::screen::ScreenTools; +use crate::mcp::tools::shared_playlist::SharedPlaylistTools; + +fn setup_auth(mock_server: &MockServer) -> Authentication { + let config = Config::new(mock_server.base_url()); + Authentication::new_with_config(config, "test_token") +} + +// ============ SCREEN TESTS ============ + +#[test] +fn test_screen_list() { + let mock_server = MockServer::start(); + mock_server.mock(|when, then| { + when.method(GET) + .path("/v4/screens") + .header("Authorization", "Token test_token"); + then.status(200) + .json_body(json!([{"id": "screen-1", "name": "Test Screen"}])); + }); + + let auth = setup_auth(&mock_server); + let result = ScreenTools::list(&auth); + assert!(result.is_ok()); + let body = result.unwrap(); + assert!(body.contains("screen-1")); + assert!(body.contains("Test Screen")); +} + +#[test] +fn test_screen_get() { + let mock_server = MockServer::start(); + mock_server.mock(|when, then| { + when.method(GET) + .path("/v4/screens") + .query_param("id", "eq.screen-uuid") + .header("Authorization", "Token test_token"); + then.status(200) + .json_body(json!([{"id": "screen-uuid", "name": "My Screen"}])); + }); + + let auth = setup_auth(&mock_server); + let result = ScreenTools::get(&auth, "screen-uuid"); + assert!(result.is_ok()); + let body = result.unwrap(); + assert!(body.contains("screen-uuid")); +} + +// ============ ASSET TESTS ============ + +#[test] +fn test_asset_list() { + let mock_server = MockServer::start(); + mock_server.mock(|when, then| { + when.method(GET) + .path("/v4/assets") + .query_param("type", "neq.edge-app-file") + .header("Authorization", "Token test_token"); + then.status(200) + .json_body(json!([{"id": "asset-1", "title": "Test Asset"}])); + }); + + let auth = setup_auth(&mock_server); + let result = AssetTools::list(&auth); + assert!(result.is_ok()); + let body = result.unwrap(); + assert!(body.contains("asset-1")); +} + +#[test] +fn test_asset_get() { + let mock_server = MockServer::start(); + mock_server.mock(|when, then| { + when.method(GET) + .path("/v4/assets") + .query_param("id", "eq.asset-uuid") + .header("Authorization", "Token test_token"); + then.status(200) + .json_body(json!([{"id": "asset-uuid", "title": "My Asset"}])); + }); + + let auth = setup_auth(&mock_server); + let result = AssetTools::get(&auth, "asset-uuid"); + assert!(result.is_ok()); + let body = result.unwrap(); + assert!(body.contains("asset-uuid")); +} + +#[test] +fn test_asset_create() { + let mock_server = MockServer::start(); + mock_server.mock(|when, then| { + when.method(POST) + .path("/v4/assets") + .header("Authorization", "Token test_token") + .json_body(json!({"title": "New Asset", "source_url": "https://example.com"})); + then.status(201) + .json_body(json!({"id": "new-asset-id", "title": "New Asset"})); + }); + + let auth = setup_auth(&mock_server); + let result = AssetTools::create(&auth, "New Asset", "https://example.com"); + assert!(result.is_ok()); + let body = result.unwrap(); + assert!(body.contains("new-asset-id")); +} + +#[test] +fn test_asset_update() { + let mock_server = MockServer::start(); + mock_server.mock(|when, then| { + when.method(PATCH) + .path("/v4/assets") + .query_param("id", "eq.asset-uuid") + .header("Authorization", "Token test_token"); + then.status(200) + .json_body(json!([{"id": "asset-uuid", "title": "Updated Title"}])); + }); + + let auth = setup_auth(&mock_server); + let result = AssetTools::update( + &auth, + "asset-uuid", + Some("Updated Title".to_string()), + None, + None, + ); + assert!(result.is_ok()); +} + +#[test] +fn test_asset_update_no_fields() { + let mock_server = MockServer::start(); + let auth = setup_auth(&mock_server); + let result = AssetTools::update(&auth, "asset-uuid", None, None, None); + assert!(result.is_err()); + assert!(result.unwrap_err().contains("No fields to update")); +} + +#[test] +fn test_asset_delete() { + let mock_server = MockServer::start(); + mock_server.mock(|when, then| { + when.method(DELETE) + .path("/v4/assets") + .query_param("id", "eq.asset-uuid") + .header("Authorization", "Token test_token"); + then.status(204); + }); + + let auth = setup_auth(&mock_server); + let result = AssetTools::delete(&auth, "asset-uuid"); + assert!(result.is_ok()); + assert!(result.unwrap().contains("deleted")); +} + +// ============ ASSET GROUP TESTS ============ + +#[test] +fn test_asset_group_list() { + let mock_server = MockServer::start(); + mock_server.mock(|when, then| { + when.method(GET) + .path("/v4/asset-groups") + .header("Authorization", "Token test_token"); + then.status(200) + .json_body(json!([{"id": "group-1", "title": "Test Group"}])); + }); + + let auth = setup_auth(&mock_server); + let result = AssetGroupTools::list(&auth); + assert!(result.is_ok()); + let body = result.unwrap(); + assert!(body.contains("group-1")); +} + +#[test] +fn test_asset_group_create() { + let mock_server = MockServer::start(); + mock_server.mock(|when, then| { + when.method(POST) + .path("/v4/asset-groups") + .header("Authorization", "Token test_token"); + then.status(201) + .json_body(json!({"id": "new-group-id", "title": "New Group"})); + }); + + let auth = setup_auth(&mock_server); + let result = AssetGroupTools::create(&auth, "New Group"); + assert!(result.is_ok()); +} + +#[test] +fn test_asset_group_update() { + let mock_server = MockServer::start(); + mock_server.mock(|when, then| { + when.method(PATCH) + .path("/v4/asset-groups") + .query_param("id", "eq.group-uuid") + .header("Authorization", "Token test_token"); + then.status(200) + .json_body(json!([{"id": "group-uuid", "title": "Updated Group"}])); + }); + + let auth = setup_auth(&mock_server); + let result = AssetGroupTools::update(&auth, "group-uuid", "Updated Group"); + assert!(result.is_ok()); +} + +#[test] +fn test_asset_group_delete() { + let mock_server = MockServer::start(); + mock_server.mock(|when, then| { + when.method(DELETE) + .path("/v4/asset-groups") + .query_param("id", "eq.group-uuid") + .header("Authorization", "Token test_token"); + then.status(204); + }); + + let auth = setup_auth(&mock_server); + let result = AssetGroupTools::delete(&auth, "group-uuid"); + assert!(result.is_ok()); +} + +// ============ PLAYLIST TESTS ============ + +#[test] +fn test_playlist_list() { + let mock_server = MockServer::start(); + mock_server.mock(|when, then| { + when.method(GET) + .path("/v4/playlists") + .header("Authorization", "Token test_token"); + then.status(200) + .json_body(json!([{"id": "playlist-1", "title": "Test Playlist"}])); + }); + + let auth = setup_auth(&mock_server); + let result = PlaylistTools::list(&auth); + assert!(result.is_ok()); + let body = result.unwrap(); + assert!(body.contains("playlist-1")); +} + +#[test] +fn test_playlist_create() { + let mock_server = MockServer::start(); + mock_server.mock(|when, then| { + when.method(POST) + .path("/v4/playlists") + .header("Authorization", "Token test_token"); + then.status(201) + .json_body(json!({"id": "new-playlist-id", "title": "New Playlist"})); + }); + + let auth = setup_auth(&mock_server); + let result = PlaylistTools::create(&auth, "New Playlist", None, None, None); + assert!(result.is_ok()); +} + +#[test] +fn test_playlist_update() { + let mock_server = MockServer::start(); + mock_server.mock(|when, then| { + when.method(PATCH) + .path("/v4/playlists") + .query_param("id", "eq.playlist-uuid") + .header("Authorization", "Token test_token"); + then.status(200) + .json_body(json!([{"id": "playlist-uuid", "title": "Updated"}])); + }); + + let auth = setup_auth(&mock_server); + let result = PlaylistTools::update( + &auth, + "playlist-uuid", + Some("Updated".to_string()), + None, + None, + None, + ); + assert!(result.is_ok()); +} + +#[test] +fn test_playlist_update_no_fields() { + let mock_server = MockServer::start(); + let auth = setup_auth(&mock_server); + let result = PlaylistTools::update(&auth, "playlist-uuid", None, None, None, None); + assert!(result.is_err()); +} + +#[test] +fn test_playlist_delete() { + let mock_server = MockServer::start(); + mock_server.mock(|when, then| { + when.method(DELETE) + .path("/v4/playlists") + .query_param("id", "eq.playlist-uuid") + .header("Authorization", "Token test_token"); + then.status(204); + }); + + let auth = setup_auth(&mock_server); + let result = PlaylistTools::delete(&auth, "playlist-uuid"); + assert!(result.is_ok()); +} + +// ============ PLAYLIST ITEM TESTS ============ + +#[test] +fn test_playlist_item_list() { + let mock_server = MockServer::start(); + mock_server.mock(|when, then| { + when.method(GET) + .path("/v4/playlist-items") + .query_param("playlist_id", "eq.playlist-uuid") + .header("Authorization", "Token test_token"); + then.status(200) + .json_body(json!([{"id": "item-1", "asset_id": "asset-1"}])); + }); + + let auth = setup_auth(&mock_server); + let result = PlaylistItemTools::list(&auth, "playlist-uuid"); + assert!(result.is_ok()); +} + +#[test] +fn test_playlist_item_create() { + let mock_server = MockServer::start(); + // First mock for getting current positions + mock_server.mock(|when, then| { + when.method(GET) + .path("/v4/playlist-items") + .query_param("select", "position") + .query_param("playlist_id", "eq.playlist-uuid") + .header("Authorization", "Token test_token"); + then.status(200).json_body(json!([])); + }); + // Second mock for creating item + mock_server.mock(|when, then| { + when.method(POST) + .path("/v4/playlist-items") + .header("Authorization", "Token test_token"); + then.status(201) + .json_body(json!([{"id": "new-item-id", "playlist_id": "playlist-uuid"}])); + }); + + let auth = setup_auth(&mock_server); + let result = PlaylistItemTools::create(&auth, "playlist-uuid", "asset-uuid", 30, None); + assert!(result.is_ok()); +} + +#[test] +fn test_playlist_item_update() { + let mock_server = MockServer::start(); + mock_server.mock(|when, then| { + when.method(PATCH) + .path("/v4/playlist-items") + .query_param("playlist_id", "eq.playlist-uuid") + .query_param("id", "eq.item-uuid") + .header("Authorization", "Token test_token"); + then.status(200).json_body(json!([{"id": "item-uuid"}])); + }); + + let auth = setup_auth(&mock_server); + let result = PlaylistItemTools::update(&auth, "playlist-uuid", "item-uuid", Some(60), None); + assert!(result.is_ok()); +} + +#[test] +fn test_playlist_item_delete() { + let mock_server = MockServer::start(); + mock_server.mock(|when, then| { + when.method(DELETE) + .path("/v4/playlist-items") + .query_param("playlist_id", "eq.playlist-uuid") + .query_param("id", "eq.item-uuid") + .header("Authorization", "Token test_token"); + then.status(204); + }); + + let auth = setup_auth(&mock_server); + let result = PlaylistItemTools::delete(&auth, "playlist-uuid", "item-uuid"); + assert!(result.is_ok()); +} + +// ============ LABEL TESTS ============ + +#[test] +fn test_label_list() { + let mock_server = MockServer::start(); + mock_server.mock(|when, then| { + when.method(GET) + .path("/v4/labels") + .header("Authorization", "Token test_token"); + then.status(200) + .json_body(json!([{"id": "label-1", "name": "Test Label"}])); + }); + + let auth = setup_auth(&mock_server); + let result = LabelTools::list(&auth); + assert!(result.is_ok()); +} + +#[test] +fn test_label_create() { + let mock_server = MockServer::start(); + mock_server.mock(|when, then| { + when.method(POST) + .path("/v4/labels") + .header("Authorization", "Token test_token"); + then.status(201) + .json_body(json!({"id": "new-label-id", "name": "New Label"})); + }); + + let auth = setup_auth(&mock_server); + let result = LabelTools::create(&auth, "New Label"); + assert!(result.is_ok()); +} + +#[test] +fn test_label_update() { + let mock_server = MockServer::start(); + mock_server.mock(|when, then| { + when.method(PATCH) + .path("/v4/labels") + .query_param("id", "eq.label-uuid") + .header("Authorization", "Token test_token"); + then.status(200) + .json_body(json!([{"id": "label-uuid", "name": "Updated Label"}])); + }); + + let auth = setup_auth(&mock_server); + let result = LabelTools::update(&auth, "label-uuid", "Updated Label"); + assert!(result.is_ok()); +} + +#[test] +fn test_label_delete() { + let mock_server = MockServer::start(); + mock_server.mock(|when, then| { + when.method(DELETE) + .path("/v4/labels") + .query_param("id", "eq.label-uuid") + .header("Authorization", "Token test_token"); + then.status(204); + }); + + let auth = setup_auth(&mock_server); + let result = LabelTools::delete(&auth, "label-uuid"); + assert!(result.is_ok()); +} + +#[test] +fn test_label_link_screen() { + let mock_server = MockServer::start(); + mock_server.mock(|when, then| { + when.method(POST) + .path("/v4/labels/screens") + .header("Authorization", "Token test_token"); + then.status(201) + .json_body(json!({"label_id": "label-uuid", "screen_id": "screen-uuid"})); + }); + + let auth = setup_auth(&mock_server); + let result = LabelTools::link_screen(&auth, "label-uuid", "screen-uuid"); + assert!(result.is_ok()); +} + +#[test] +fn test_label_unlink_screen() { + let mock_server = MockServer::start(); + mock_server.mock(|when, then| { + when.method(DELETE) + .path("/v4/labels/screens") + .query_param("label_id", "eq.label-uuid") + .query_param("screen_id", "eq.screen-uuid") + .header("Authorization", "Token test_token"); + then.status(204); + }); + + let auth = setup_auth(&mock_server); + let result = LabelTools::unlink_screen(&auth, "label-uuid", "screen-uuid"); + assert!(result.is_ok()); +} + +#[test] +fn test_label_link_playlist() { + let mock_server = MockServer::start(); + mock_server.mock(|when, then| { + when.method(POST) + .path("/v4/labels/playlists") + .header("Authorization", "Token test_token"); + then.status(201) + .json_body(json!({"label_id": "label-uuid", "playlist_id": "playlist-uuid"})); + }); + + let auth = setup_auth(&mock_server); + let result = LabelTools::link_playlist(&auth, "label-uuid", "playlist-uuid"); + assert!(result.is_ok()); +} + +#[test] +fn test_label_unlink_playlist() { + let mock_server = MockServer::start(); + mock_server.mock(|when, then| { + when.method(DELETE) + .path("/v4/labels/playlists") + .query_param("label_id", "eq.label-uuid") + .query_param("playlist_id", "eq.playlist-uuid") + .header("Authorization", "Token test_token"); + then.status(204); + }); + + let auth = setup_auth(&mock_server); + let result = LabelTools::unlink_playlist(&auth, "label-uuid", "playlist-uuid"); + assert!(result.is_ok()); +} + +// ============ SHARED PLAYLIST TESTS ============ + +#[test] +fn test_shared_playlist_list() { + let mock_server = MockServer::start(); + mock_server.mock(|when, then| { + when.method(GET) + .path("/v4/playlists/shared") + .header("Authorization", "Token test_token"); + then.status(200).json_body(json!([])); + }); + + let auth = setup_auth(&mock_server); + let result = SharedPlaylistTools::list(&auth); + assert!(result.is_ok()); +} + +#[test] +fn test_shared_playlist_create() { + let mock_server = MockServer::start(); + mock_server.mock(|when, then| { + when.method(POST) + .path("/v4/playlists/shared") + .header("Authorization", "Token test_token"); + then.status(201) + .json_body(json!({"playlist_id": "playlist-uuid", "team_id": "team-uuid"})); + }); + + let auth = setup_auth(&mock_server); + let result = SharedPlaylistTools::create(&auth, "playlist-uuid", "team-uuid"); + assert!(result.is_ok()); +} + +#[test] +fn test_shared_playlist_delete() { + let mock_server = MockServer::start(); + mock_server.mock(|when, then| { + when.method(DELETE) + .path("/v4/playlists/shared") + .query_param("playlist_id", "eq.playlist-uuid") + .query_param("team_id", "eq.team-uuid") + .header("Authorization", "Token test_token"); + then.status(204); + }); + + let auth = setup_auth(&mock_server); + let result = SharedPlaylistTools::delete(&auth, "playlist-uuid", "team-uuid"); + assert!(result.is_ok()); +} + +// ============ EDGE APP TESTS ============ + +#[test] +fn test_edge_app_list() { + let mock_server = MockServer::start(); + mock_server.mock(|when, then| { + when.method(GET) + .path("/v4/edge-apps") + .query_param("select", "id,name") + .query_param("deleted", "eq.false") + .header("Authorization", "Token test_token"); + then.status(200) + .json_body(json!([{"id": "app-1", "name": "Test App"}])); + }); + + let auth = setup_auth(&mock_server); + let result = EdgeAppTools::list(&auth); + assert!(result.is_ok()); +} + +#[test] +fn test_edge_app_list_settings() { + let mock_server = MockServer::start(); + mock_server.mock(|when, then| { + when.method(GET) + .path("/v4.1/edge-apps/settings") + .query_param("app_id", "eq.app-uuid") + .header("Authorization", "Token test_token"); + then.status(200) + .json_body(json!([{"name": "setting1", "type": "string"}])); + }); + + let auth = setup_auth(&mock_server); + let result = EdgeAppTools::list_settings(&auth, "app-uuid"); + assert!(result.is_ok()); +} + +#[test] +fn test_edge_app_list_instances() { + let mock_server = MockServer::start(); + mock_server.mock(|when, then| { + when.method(GET) + .path("/v4.1/edge-apps/installations") + .query_param("app_id", "eq.app-uuid") + .header("Authorization", "Token test_token"); + then.status(200) + .json_body(json!([{"id": "instance-1", "name": "Test Instance"}])); + }); + + let auth = setup_auth(&mock_server); + let result = EdgeAppTools::list_instances(&auth, "app-uuid"); + assert!(result.is_ok()); +} diff --git a/src/mcp/tools/asset.rs b/src/mcp/tools/asset.rs new file mode 100644 index 0000000..104c04a --- /dev/null +++ b/src/mcp/tools/asset.rs @@ -0,0 +1,89 @@ +//! Asset-related MCP tools. + +use serde_json::json; + +use crate::authentication::Authentication; +use crate::commands; + +/// Asset tools for the MCP server. +pub struct AssetTools; + +impl AssetTools { + /// List all assets (excluding edge-app-file type). + pub fn list(auth: &Authentication) -> Result<String, String> { + let result = commands::get(auth, "v4/assets?type=neq.edge-app-file") + .map_err(|e| format!("Failed to list assets: {}", e))?; + + serde_json::to_string_pretty(&result) + .map_err(|e| format!("Failed to serialize response: {}", e)) + } + + /// Get an asset by UUID. + pub fn get(auth: &Authentication, uuid: &str) -> Result<String, String> { + let endpoint = format!("v4/assets?id=eq.{}", uuid); + let result = + commands::get(auth, &endpoint).map_err(|e| format!("Failed to get asset: {}", e))?; + + serde_json::to_string_pretty(&result) + .map_err(|e| format!("Failed to serialize response: {}", e)) + } + + /// Create a new asset from a URL. + pub fn create(auth: &Authentication, title: &str, source_url: &str) -> Result<String, String> { + let payload = json!({ + "title": title, + "source_url": source_url, + }); + + let result = commands::post(auth, "v4/assets", &payload) + .map_err(|e| format!("Failed to create asset: {}", e))?; + + serde_json::to_string_pretty(&result) + .map_err(|e| format!("Failed to serialize response: {}", e)) + } + + /// Update an asset. + pub fn update( + auth: &Authentication, + uuid: &str, + title: Option<String>, + js_injection: Option<String>, + headers: Option<String>, + ) -> Result<String, String> { + let mut payload = serde_json::Map::new(); + + if let Some(t) = title { + payload.insert("title".to_string(), json!(t)); + } + + if let Some(js) = js_injection { + payload.insert("js_injection".to_string(), json!(js)); + } + + if let Some(h) = headers { + // Parse headers as JSON + let headers_json: serde_json::Value = + serde_json::from_str(&h).map_err(|e| format!("Invalid headers JSON: {}", e))?; + payload.insert("headers".to_string(), headers_json); + } + + if payload.is_empty() { + return Err("No fields to update".to_string()); + } + + let endpoint = format!("v4/assets?id=eq.{}", uuid); + let result = commands::patch(auth, &endpoint, &serde_json::Value::Object(payload)) + .map_err(|e| format!("Failed to update asset: {}", e))?; + + serde_json::to_string_pretty(&result) + .map_err(|e| format!("Failed to serialize response: {}", e)) + } + + /// Delete an asset. + pub fn delete(auth: &Authentication, uuid: &str) -> Result<String, String> { + let endpoint = format!("v4/assets?id=eq.{}", uuid); + commands::delete(auth, &endpoint).map_err(|e| format!("Failed to delete asset: {}", e))?; + + Ok(json!({"status": "deleted", "id": uuid}).to_string()) + } +} diff --git a/src/mcp/tools/asset_group.rs b/src/mcp/tools/asset_group.rs new file mode 100644 index 0000000..fd10ca0 --- /dev/null +++ b/src/mcp/tools/asset_group.rs @@ -0,0 +1,56 @@ +//! Asset group (folder) MCP tools. + +use serde_json::json; + +use crate::authentication::Authentication; +use crate::commands; + +/// Asset group tools for the MCP server. +pub struct AssetGroupTools; + +impl AssetGroupTools { + /// List all asset groups. + pub fn list(auth: &Authentication) -> Result<String, String> { + let result = commands::get(auth, "v4/asset-groups") + .map_err(|e| format!("Failed to list asset groups: {}", e))?; + + serde_json::to_string_pretty(&result) + .map_err(|e| format!("Failed to serialize response: {}", e)) + } + + /// Create a new asset group. + pub fn create(auth: &Authentication, title: &str) -> Result<String, String> { + let payload = json!({ + "title": title, + }); + + let result = commands::post(auth, "v4/asset-groups", &payload) + .map_err(|e| format!("Failed to create asset group: {}", e))?; + + serde_json::to_string_pretty(&result) + .map_err(|e| format!("Failed to serialize response: {}", e)) + } + + /// Update an asset group. + pub fn update(auth: &Authentication, uuid: &str, title: &str) -> Result<String, String> { + let payload = json!({ + "title": title, + }); + + let endpoint = format!("v4/asset-groups?id=eq.{}", uuid); + let result = commands::patch(auth, &endpoint, &payload) + .map_err(|e| format!("Failed to update asset group: {}", e))?; + + serde_json::to_string_pretty(&result) + .map_err(|e| format!("Failed to serialize response: {}", e)) + } + + /// Delete an asset group (and all assets within it). + pub fn delete(auth: &Authentication, uuid: &str) -> Result<String, String> { + let endpoint = format!("v4/asset-groups?id=eq.{}", uuid); + commands::delete(auth, &endpoint) + .map_err(|e| format!("Failed to delete asset group: {}", e))?; + + Ok(json!({"status": "deleted", "id": uuid}).to_string()) + } +} diff --git a/src/mcp/tools/edge_app.rs b/src/mcp/tools/edge_app.rs new file mode 100644 index 0000000..57253da --- /dev/null +++ b/src/mcp/tools/edge_app.rs @@ -0,0 +1,44 @@ +//! Edge App MCP tools. + +use crate::authentication::Authentication; +use crate::commands; + +/// Edge App tools for the MCP server. +pub struct EdgeAppTools; + +impl EdgeAppTools { + /// List all Edge Apps. + pub fn list(auth: &Authentication) -> Result<String, String> { + let result = commands::get(auth, "v4/edge-apps?select=id,name&deleted=eq.false") + .map_err(|e| format!("Failed to list Edge Apps: {}", e))?; + + serde_json::to_string_pretty(&result) + .map_err(|e| format!("Failed to serialize response: {}", e)) + } + + /// List settings for an Edge App. + pub fn list_settings(auth: &Authentication, app_uuid: &str) -> Result<String, String> { + let endpoint = format!( + "v4.1/edge-apps/settings?app_id=eq.{}&select=name,type,default_value,optional,title,help_text&order=name.asc", + app_uuid + ); + let result = commands::get(auth, &endpoint) + .map_err(|e| format!("Failed to list Edge App settings: {}", e))?; + + serde_json::to_string_pretty(&result) + .map_err(|e| format!("Failed to serialize response: {}", e)) + } + + /// List instances of an Edge App. + pub fn list_instances(auth: &Authentication, app_uuid: &str) -> Result<String, String> { + let endpoint = format!( + "v4.1/edge-apps/installations?select=id,name&app_id=eq.{}", + app_uuid + ); + let result = commands::get(auth, &endpoint) + .map_err(|e| format!("Failed to list Edge App instances: {}", e))?; + + serde_json::to_string_pretty(&result) + .map_err(|e| format!("Failed to serialize response: {}", e)) + } +} diff --git a/src/mcp/tools/label.rs b/src/mcp/tools/label.rs new file mode 100644 index 0000000..9c157e4 --- /dev/null +++ b/src/mcp/tools/label.rs @@ -0,0 +1,133 @@ +//! Label-related MCP tools. + +use serde_json::json; + +use crate::authentication::Authentication; +use crate::commands; + +/// Label tools for the MCP server. +pub struct LabelTools; + +impl LabelTools { + /// List all labels. + pub fn list(auth: &Authentication) -> Result<String, String> { + let result = commands::get(auth, "v4/labels") + .map_err(|e| format!("Failed to list labels: {}", e))?; + + serde_json::to_string_pretty(&result) + .map_err(|e| format!("Failed to serialize response: {}", e)) + } + + /// Create a new label. + pub fn create(auth: &Authentication, name: &str) -> Result<String, String> { + let payload = json!({ + "name": name, + }); + + let result = commands::post(auth, "v4/labels", &payload) + .map_err(|e| format!("Failed to create label: {}", e))?; + + serde_json::to_string_pretty(&result) + .map_err(|e| format!("Failed to serialize response: {}", e)) + } + + /// Update a label. + pub fn update(auth: &Authentication, uuid: &str, name: &str) -> Result<String, String> { + let payload = json!({ + "name": name, + }); + + let endpoint = format!("v4/labels?id=eq.{}", uuid); + let result = commands::patch(auth, &endpoint, &payload) + .map_err(|e| format!("Failed to update label: {}", e))?; + + serde_json::to_string_pretty(&result) + .map_err(|e| format!("Failed to serialize response: {}", e)) + } + + /// Delete a label. + pub fn delete(auth: &Authentication, uuid: &str) -> Result<String, String> { + let endpoint = format!("v4/labels?id=eq.{}", uuid); + commands::delete(auth, &endpoint).map_err(|e| format!("Failed to delete label: {}", e))?; + + Ok(json!({"status": "deleted", "id": uuid}).to_string()) + } + + /// Attach a label to a screen. + pub fn link_screen( + auth: &Authentication, + label_uuid: &str, + screen_uuid: &str, + ) -> Result<String, String> { + let payload = json!({ + "label_id": label_uuid, + "screen_id": screen_uuid, + }); + + let result = commands::post(auth, "v4/labels/screens", &payload) + .map_err(|e| format!("Failed to link label to screen: {}", e))?; + + serde_json::to_string_pretty(&result) + .map_err(|e| format!("Failed to serialize response: {}", e)) + } + + /// Remove a label from a screen. + pub fn unlink_screen( + auth: &Authentication, + label_uuid: &str, + screen_uuid: &str, + ) -> Result<String, String> { + let endpoint = format!( + "v4/labels/screens?label_id=eq.{}&screen_id=eq.{}", + label_uuid, screen_uuid + ); + commands::delete(auth, &endpoint) + .map_err(|e| format!("Failed to unlink label from screen: {}", e))?; + + Ok(json!({ + "status": "unlinked", + "label_id": label_uuid, + "screen_id": screen_uuid + }) + .to_string()) + } + + /// Attach a label to a playlist. + pub fn link_playlist( + auth: &Authentication, + label_uuid: &str, + playlist_uuid: &str, + ) -> Result<String, String> { + let payload = json!({ + "label_id": label_uuid, + "playlist_id": playlist_uuid, + }); + + let result = commands::post(auth, "v4/labels/playlists", &payload) + .map_err(|e| format!("Failed to link label to playlist: {}", e))?; + + serde_json::to_string_pretty(&result) + .map_err(|e| format!("Failed to serialize response: {}", e)) + } + + /// Remove a label from a playlist. + pub fn unlink_playlist( + auth: &Authentication, + label_uuid: &str, + playlist_uuid: &str, + ) -> Result<String, String> { + let endpoint = format!( + "v4/labels/playlists?label_id=eq.{}&playlist_id=eq.{}", + label_uuid, playlist_uuid + ); + commands::delete(auth, &endpoint) + .map_err(|e| format!("Failed to unlink label from playlist: {}", e))?; + + Ok(json!({ + "status": "unlinked", + "label_id": label_uuid, + "playlist_id": playlist_uuid + }) + .to_string()) + } +} diff --git a/src/mcp/tools/mod.rs b/src/mcp/tools/mod.rs new file mode 100644 index 0000000..de390ad --- /dev/null +++ b/src/mcp/tools/mod.rs @@ -0,0 +1,10 @@ +//! MCP tool implementations for Screenly API. + +pub mod asset; +pub mod asset_group; +pub mod edge_app; +pub mod label; +pub mod playlist; +pub mod playlist_item; +pub mod screen; +pub mod shared_playlist; diff --git a/src/mcp/tools/playlist.rs b/src/mcp/tools/playlist.rs new file mode 100644 index 0000000..1e456f0 --- /dev/null +++ b/src/mcp/tools/playlist.rs @@ -0,0 +1,91 @@ +//! Playlist-related MCP tools. + +use serde_json::json; + +use crate::authentication::Authentication; +use crate::commands; + +/// Playlist tools for the MCP server. +pub struct PlaylistTools; + +impl PlaylistTools { + /// List all playlists. + pub fn list(auth: &Authentication) -> Result<String, String> { + let result = commands::get(auth, "v4/playlists") + .map_err(|e| format!("Failed to list playlists: {}", e))?; + + serde_json::to_string_pretty(&result) + .map_err(|e| format!("Failed to serialize response: {}", e)) + } + + /// Create a new playlist. + pub fn create( + auth: &Authentication, + title: &str, + predicate: Option<String>, + priority: Option<bool>, + is_enabled: Option<bool>, + ) -> Result<String, String> { + let payload = json!({ + "title": title, + "predicate": predicate.unwrap_or_else(|| "TRUE".to_string()), + "priority": priority.unwrap_or(false), + "is_enabled": is_enabled.unwrap_or(true), + "transitions": true + }); + + let result = commands::post(auth, "v4/playlists", &payload) + .map_err(|e| format!("Failed to create playlist: {}", e))?; + + serde_json::to_string_pretty(&result) + .map_err(|e| format!("Failed to serialize response: {}", e)) + } + + /// Update a playlist. + pub fn update( + auth: &Authentication, + uuid: &str, + title: Option<String>, + predicate: Option<String>, + priority: Option<bool>, + is_enabled: Option<bool>, + ) -> Result<String, String> { + let mut payload = serde_json::Map::new(); + + if let Some(t) = title { + payload.insert("title".to_string(), json!(t)); + } + + if let Some(p) = predicate { + payload.insert("predicate".to_string(), json!(p)); + } + + if let Some(pr) = priority { + payload.insert("priority".to_string(), json!(pr)); + } + + if let Some(e) = is_enabled { + payload.insert("is_enabled".to_string(), json!(e)); + } + + if payload.is_empty() { + return Err("No fields to update".to_string()); + } + + let endpoint = format!("v4/playlists?id=eq.{}", uuid); + let result = commands::patch(auth, &endpoint, &serde_json::Value::Object(payload)) + .map_err(|e| format!("Failed to update playlist: {}", e))?; + + serde_json::to_string_pretty(&result) + .map_err(|e| format!("Failed to serialize response: {}", e)) + } + + /// Delete a playlist. + pub fn delete(auth: &Authentication, uuid: &str) -> Result<String, String> { + let endpoint = format!("v4/playlists?id=eq.{}", uuid); + commands::delete(auth, &endpoint) + .map_err(|e| format!("Failed to delete playlist: {}", e))?; + + Ok(json!({"status": "deleted", "id": uuid}).to_string()) + } +} diff --git a/src/mcp/tools/playlist_item.rs b/src/mcp/tools/playlist_item.rs new file mode 100644 index 0000000..a0ce89e --- /dev/null +++ b/src/mcp/tools/playlist_item.rs @@ -0,0 +1,128 @@ +//! Playlist item MCP tools. + +use serde_json::json; + +use crate::authentication::Authentication; +use crate::commands; + +/// Position spacing for playlist items. Uses large gaps (100,000) between items +/// to allow inserting new items between existing ones without reordering. +/// This follows Screenly's API convention for position-based ordering. +const POSITION_MULTIPLIER: u64 = 100000; + +/// Playlist item tools for the MCP server. +pub struct PlaylistItemTools; + +impl PlaylistItemTools { + /// List all items in a playlist. + pub fn list(auth: &Authentication, playlist_uuid: &str) -> Result<String, String> { + let endpoint = format!( + "v4/playlist-items?playlist_id=eq.{}&order=position.asc", + playlist_uuid + ); + let result = commands::get(auth, &endpoint) + .map_err(|e| format!("Failed to list playlist items: {}", e))?; + + serde_json::to_string_pretty(&result) + .map_err(|e| format!("Failed to serialize response: {}", e)) + } + + /// Add an asset to a playlist. + pub fn create( + auth: &Authentication, + playlist_uuid: &str, + asset_uuid: &str, + duration: u32, + position: Option<u64>, + ) -> Result<String, String> { + // If position is not specified, get the highest position and add after it + let final_position = if let Some(pos) = position { + pos + } else { + // Get the highest position in the playlist + let endpoint = format!( + "v4/playlist-items?select=position&playlist_id=eq.{}&order=position.desc&limit=1", + playlist_uuid + ); + let result = commands::get(auth, &endpoint) + .map_err(|e| format!("Failed to get playlist positions: {}", e))?; + + if let Some(items) = result.as_array() { + if items.is_empty() { + POSITION_MULTIPLIER + } else if let Some(pos) = items[0].get("position").and_then(|p| p.as_u64()) { + pos + POSITION_MULTIPLIER + } else { + POSITION_MULTIPLIER + } + } else { + POSITION_MULTIPLIER + } + }; + + let payload = json!([{ + "playlist_id": playlist_uuid, + "asset_id": asset_uuid, + "duration": duration, + "position": final_position + }]); + + let result = commands::post(auth, "v4/playlist-items", &payload) + .map_err(|e| format!("Failed to create playlist item: {}", e))?; + + serde_json::to_string_pretty(&result) + .map_err(|e| format!("Failed to serialize response: {}", e)) + } + + /// Update a playlist item. + pub fn update( + auth: &Authentication, + playlist_uuid: &str, + item_uuid: &str, + duration: Option<u32>, + position: Option<u64>, + ) -> Result<String, String> { + let mut payload = serde_json::Map::new(); + + if let Some(d) = duration { + payload.insert("duration".to_string(), json!(d)); + } + + if let Some(p) = position { + payload.insert("position".to_string(), json!(p)); + } + + if payload.is_empty() { + return Err("No fields to update".to_string()); + } + + let endpoint = format!( + "v4/playlist-items?playlist_id=eq.{}&id=eq.{}", + playlist_uuid, item_uuid + ); + let result = commands::patch(auth, &endpoint, &serde_json::Value::Object(payload)) + .map_err(|e| format!("Failed to update playlist item: {}", e))?; + + serde_json::to_string_pretty(&result) + .map_err(|e| format!("Failed to serialize response: {}", e)) + } + + /// Remove an item from a playlist. + pub fn delete( + auth: &Authentication, + playlist_uuid: &str, + item_uuid: &str, + ) -> Result<String, String> { + let endpoint = format!( + "v4/playlist-items?playlist_id=eq.{}&id=eq.{}", + playlist_uuid, item_uuid + ); + commands::delete(auth, &endpoint) + .map_err(|e| format!("Failed to delete playlist item: {}", e))?; + + Ok( + json!({"status": "deleted", "playlist_id": playlist_uuid, "item_id": item_uuid}) + .to_string(), + ) + } +} diff --git a/src/mcp/tools/screen.rs b/src/mcp/tools/screen.rs new file mode 100644 index 0000000..534b5ac --- /dev/null +++ b/src/mcp/tools/screen.rs @@ -0,0 +1,28 @@ +//! Screen-related MCP tools. + +use crate::authentication::Authentication; +use crate::commands; + +/// Screen tools for the MCP server. +pub struct ScreenTools; + +impl ScreenTools { + /// List all screens. + pub fn list(auth: &Authentication) -> Result<String, String> { + let result = commands::get(auth, "v4/screens") + .map_err(|e| format!("Failed to list screens: {}", e))?; + + serde_json::to_string_pretty(&result) + .map_err(|e| format!("Failed to serialize response: {}", e)) + } + + /// Get a screen by UUID. + pub fn get(auth: &Authentication, uuid: &str) -> Result<String, String> { + let endpoint = format!("v4/screens?id=eq.{}", uuid); + let result = + commands::get(auth, &endpoint).map_err(|e| format!("Failed to get screen: {}", e))?; + + serde_json::to_string_pretty(&result) + .map_err(|e| format!("Failed to serialize response: {}", e)) + } +} diff --git a/src/mcp/tools/shared_playlist.rs b/src/mcp/tools/shared_playlist.rs new file mode 100644 index 0000000..4a23821 --- /dev/null +++ b/src/mcp/tools/shared_playlist.rs @@ -0,0 +1,59 @@ +//! Shared playlist MCP tools. + +use serde_json::json; + +use crate::authentication::Authentication; +use crate::commands; + +/// Shared playlist tools for the MCP server. +pub struct SharedPlaylistTools; + +impl SharedPlaylistTools { + /// List all shared playlists. + pub fn list(auth: &Authentication) -> Result<String, String> { + let result = commands::get(auth, "v4/playlists/shared") + .map_err(|e| format!("Failed to list shared playlists: {}", e))?; + + serde_json::to_string_pretty(&result) + .map_err(|e| format!("Failed to serialize response: {}", e)) + } + + /// Share a playlist with a team. + pub fn create( + auth: &Authentication, + playlist_uuid: &str, + team_uuid: &str, + ) -> Result<String, String> { + let payload = json!({ + "playlist_id": playlist_uuid, + "team_id": team_uuid, + }); + + let result = commands::post(auth, "v4/playlists/shared", &payload) + .map_err(|e| format!("Failed to share playlist: {}", e))?; + + serde_json::to_string_pretty(&result) + .map_err(|e| format!("Failed to serialize response: {}", e)) + } + + /// Unshare a playlist from a team. + pub fn delete( + auth: &Authentication, + playlist_uuid: &str, + team_uuid: &str, + ) -> Result<String, String> { + let endpoint = format!( + "v4/playlists/shared?playlist_id=eq.{}&team_id=eq.{}", + playlist_uuid, team_uuid + ); + commands::delete(auth, &endpoint) + .map_err(|e| format!("Failed to unshare playlist: {}", e))?; + + Ok(json!({ + "status": "unshared", + "playlist_id": playlist_uuid, + "team_id": team_uuid + }) + .to_string()) + } +}