diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index bbfc5aba..ce00eebe 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -24,6 +24,7 @@ on: env: CARGO_TERM_COLOR: always PYO3_USE_ABI3_FORWARD_COMPATIBILITY: 1 + RUST_TOOLCHAIN: "1.95.0" concurrency: group: ${{ github.workflow }}-${{ github.ref }} @@ -39,7 +40,9 @@ jobs: uses: actions/checkout@v6 - name: Install Rust toolchain - uses: dtolnay/rust-toolchain@stable + uses: dtolnay/rust-toolchain@master + with: + toolchain: ${{ env.RUST_TOOLCHAIN }} - name: Install Python uses: actions/setup-python@v6 @@ -68,8 +71,9 @@ jobs: uses: actions/checkout@v6 - name: Install Rust toolchain - uses: dtolnay/rust-toolchain@stable + uses: dtolnay/rust-toolchain@master with: + toolchain: ${{ env.RUST_TOOLCHAIN }} components: clippy - name: Install Python @@ -100,7 +104,9 @@ jobs: uses: actions/checkout@v6 - name: Install Rust toolchain - uses: dtolnay/rust-toolchain@stable + uses: dtolnay/rust-toolchain@master + with: + toolchain: ${{ env.RUST_TOOLCHAIN }} - name: Install Python uses: actions/setup-python@v6 @@ -130,7 +136,9 @@ jobs: uses: actions/checkout@v6 - name: Install Rust toolchain - uses: dtolnay/rust-toolchain@stable + uses: dtolnay/rust-toolchain@master + with: + toolchain: ${{ env.RUST_TOOLCHAIN }} - name: Install Python uses: actions/setup-python@v6 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e73f7600..df70fbdd 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -7,6 +7,7 @@ on: env: CARGO_TERM_COLOR: always + RUST_TOOLCHAIN: "1.95.0" concurrency: group: ${{ github.workflow }}-${{ github.ref }} @@ -47,7 +48,9 @@ jobs: uses: actions/checkout@v6 - name: Install Rust toolchain - uses: dtolnay/rust-toolchain@stable + uses: dtolnay/rust-toolchain@master + with: + toolchain: ${{ env.RUST_TOOLCHAIN }} - name: Cache cargo registry and build uses: actions/cache@v5 diff --git a/Cargo.lock b/Cargo.lock index f1677324..941e1dbd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -41,10 +41,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" dependencies = [ "cfg-if", - "cipher", + "cipher 0.4.4", "cpufeatures 0.2.17", ] +[[package]] +name = "aes" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66bd29a732b644c0431c6140f370d097879203d79b80c94a6747ba0872adaef8" +dependencies = [ + "cipher 0.5.1", + "cpubits", + "cpufeatures 0.3.0", +] + [[package]] name = "aes-gcm" version = "0.10.3" @@ -52,8 +63,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" dependencies = [ "aead", - "aes", - "cipher", + "aes 0.8.4", + "cipher 0.4.4", "ctr", "ghash", "subtle", @@ -70,9 +81,9 @@ dependencies = [ [[package]] name = "aide" -version = "0.16.0-alpha.3" +version = "0.16.0-alpha.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87a769cd3a8984b7236931cd48f4d1f6b99c0475d60987a1f69490b079116306" +checksum = "390515b47251185fa076ac92a7a582d9d383b03e13cef0c801e7670cf928229b" dependencies = [ "aide-macros", "axum", @@ -92,9 +103,9 @@ dependencies = [ [[package]] name = "aide-macros" -version = "0.16.0-alpha.3" +version = "0.16.0-alpha.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15e90573b48a90439758669afeff706f4f6f54515303beba88e118e8c6006d09" +checksum = "44db8ab6bd2a6748ce7c948a4813456bd95ec3bfdbe1635d52f4a0276d0fa4f0" dependencies = [ "darling 0.23.0", "proc-macro2", @@ -249,9 +260,9 @@ dependencies = [ [[package]] name = "async-compression" -version = "0.4.41" +version = "0.4.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0f9ee0f6e02ffd7ad5816e9464499fba7b3effd01123b515c41d1697c43dad1" +checksum = "e79b3f8a79cccc2898f31920fc69f304859b3bd567490f75ebf51ae1c792a9ac" dependencies = [ "compression-codecs", "compression-core", @@ -349,18 +360,18 @@ dependencies = [ [[package]] name = "avif-serialize" -version = "0.8.8" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "375082f007bd67184fb9c0374614b29f9aaa604ec301635f72338bb65386a53d" +checksum = "e7178fe5f7d460b13895ebb9dcb28a3a6216d2df2574a0806cb51b555d297f38" dependencies = [ "arrayvec", ] [[package]] name = "aws-lc-rs" -version = "1.16.2" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a054912289d18629dc78375ba2c3726a3afe3ff71b4edba9dedfca0e3446d1fc" +checksum = "5ec2f1fc3ec205783a5da9a7e6c1509cc69dedf09a1949e412c1e18469326d00" dependencies = [ "aws-lc-sys", "zeroize", @@ -368,9 +379,9 @@ dependencies = [ [[package]] name = "aws-lc-sys" -version = "0.39.1" +version = "0.41.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83a25cf98105baa966497416dbd42565ce3a8cf8dbfd59803ec9ad46f3126399" +checksum = "1a2f9779ce85b93ab6170dd940ad0169b5766ff848247aff13bb788b832fe3f4" dependencies = [ "cc", "cmake", @@ -380,9 +391,9 @@ dependencies = [ [[package]] name = "axum" -version = "0.8.8" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b52af3cb4058c895d37317bb27508dccc8e5f2d39454016b297bf4a400597b8" +checksum = "31b698c5f9a010f6573133b09e0de5408834d0c82f8d7475a89fc1867a71cd90" dependencies = [ "axum-core", "bytes", @@ -445,17 +456,17 @@ checksum = "1e4b40c7323adcfc0a41c4b88143ed58346ff65a288fc144329c5c45e05d70c6" [[package]] name = "bitflags" -version = "2.11.0" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" +checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3" [[package]] name = "bitstream-io" -version = "4.9.0" +version = "4.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60d4bd9d1db2c6bdf285e223a7fa369d5ce98ec767dec949c6ca62863ce61757" +checksum = "7eff00be299a18769011411c9def0d827e8f2d7bf0c3dbf53633147a8867fd1f" dependencies = [ - "core2", + "no_std_io2", ] [[package]] @@ -474,6 +485,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdd35008169921d80bc60d3d0ab416eecb028c4cd653352907921d95084790be" dependencies = [ "hybrid-array", + "zeroize", ] [[package]] @@ -506,6 +518,15 @@ dependencies = [ "alloc-stdlib", ] +[[package]] +name = "bs58" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" +dependencies = [ + "tinyvec", +] + [[package]] name = "built" version = "0.8.0" @@ -589,14 +610,14 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6" dependencies = [ - "cipher", + "cipher 0.4.4", ] [[package]] name = "cc" -version = "1.2.60" +version = "1.2.62" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43c5703da9466b66a946814e1adf53ea2c90f10063b86290cc9eb67ce3478a20" +checksum = "a1dce859f0832a7d088c4f1119888ab94ef4b5d6795d1ce05afb7fe159d79f98" dependencies = [ "find-msvc-tools", "jobserver", @@ -604,12 +625,6 @@ dependencies = [ "shlex", ] -[[package]] -name = "cesu8" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" - [[package]] name = "cfb" version = "0.7.3" @@ -641,7 +656,7 @@ checksum = "6f8d983286843e49675a4b7a2d174efe136dc93a18d69130dd18198a6c167601" dependencies = [ "cfg-if", "cpufeatures 0.3.0", - "rand_core 0.10.0", + "rand_core 0.10.1", ] [[package]] @@ -665,14 +680,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" dependencies = [ "crypto-common 0.1.7", - "inout", + "inout 0.1.4", +] + +[[package]] +name = "cipher" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e34d8227fe1ba289043aeb13792056ff80fd6de1a9f49137a5f499de8e8c78ea" +dependencies = [ + "crypto-common 0.2.1", + "inout 0.2.2", ] [[package]] name = "clap" -version = "4.6.0" +version = "4.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b193af5b67834b676abd72466a96c1024e6a6ad978a1f484bd90b85c94041351" +checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51" dependencies = [ "clap_builder", "clap_derive", @@ -692,9 +717,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.6.0" +version = "4.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1110bd8a634a1ab8cb04345d8d878267d57c3cf1b38d91b71af6686408bbca6a" +checksum = "f2ce8604710f6733aa641a2b3731eaa1e8b3d9973d5e3565da11800813f997a9" dependencies = [ "heck 0.5.0", "proc-macro2", @@ -762,9 +787,9 @@ checksum = "ea0095f6103c2a8b44acd6fd15960c801dafebf02e21940360833e0673f48ba7" [[package]] name = "compression-codecs" -version = "0.4.37" +version = "0.4.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb7b51a7d9c967fc26773061ba86150f19c50c0d65c887cb1fbe295fd16619b7" +checksum = "ce2548391e9c1929c21bf6aa2680af86fe4c1b33e6cea9ac1cfeec0bd11218cf" dependencies = [ "brotli", "compression-core", @@ -776,9 +801,9 @@ dependencies = [ [[package]] name = "compression-core" -version = "0.4.31" +version = "0.4.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75984efb6ed102a0d42db99afb6c1948f0380d1d91808d5529916e6c08b49d8d" +checksum = "cc14f565cf027a105f7a44ccf9e5b424348421a1d8952a8fc9d499d313107789" [[package]] name = "console_error_panic_hook" @@ -821,16 +846,6 @@ dependencies = [ "unicode-segmentation", ] -[[package]] -name = "core-foundation" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" -dependencies = [ - "core-foundation-sys", - "libc", -] - [[package]] name = "core-foundation" version = "0.10.1" @@ -848,13 +863,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] -name = "core2" -version = "0.4.0" +name = "cpubits" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505" -dependencies = [ - "memchr", -] +checksum = "15b85f9c39137c3a891689859392b1bd49812121d0d61c9caf00d46ed5ce06ae" [[package]] name = "cpufeatures" @@ -994,7 +1006,7 @@ version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" dependencies = [ - "cipher", + "cipher 0.4.4", ] [[package]] @@ -1077,9 +1089,9 @@ dependencies = [ [[package]] name = "dashmap" -version = "6.1.0" +version = "6.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf" +checksum = "e6361d5c062261c78a176addb82d4c821ae42bed6089de0e12603cd25de2059c" dependencies = [ "cfg-if", "crossbeam-utils", @@ -1091,9 +1103,9 @@ dependencies = [ [[package]] name = "data-encoding" -version = "2.10.0" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea" +checksum = "a4ae5f15dda3c708c0ade84bfee31ccab44a3da4f88015ed22f63732abe300c8" [[package]] name = "debug_unsafe" @@ -1142,7 +1154,7 @@ dependencies = [ "deluxe-core", "heck 0.4.1", "if_chain", - "proc-macro-crate", + "proc-macro-crate 1.3.1", "proc-macro2", "quote", "syn", @@ -1220,19 +1232,19 @@ checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer 0.10.4", "crypto-common 0.1.7", - "subtle", ] [[package]] name = "digest" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4850db49bf08e663084f7fb5c87d202ef91a3907271aff24a94eb97ff039153c" +checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2" dependencies = [ "block-buffer 0.12.0", "const-oid", "crypto-common 0.2.1", "ctutils", + "zeroize", ] [[package]] @@ -1279,7 +1291,7 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a8bfa975b1aec2145850fcaa1c6fe269a16578c44705a532ae3edc92b8881c7" dependencies = [ - "cipher", + "cipher 0.4.4", ] [[package]] @@ -1391,23 +1403,9 @@ checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" [[package]] name = "fax" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f05de7d48f37cd6730705cbca900770cab77a89f413d23e100ad7fad7795a0ab" -dependencies = [ - "fax_derive", -] - -[[package]] -name = "fax_derive" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0aca10fb742cb43f9e7bb8467c91aa9bcb8e3ffbc6a6f7389bb93ffc920577d" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] +checksum = "caf1079563223d5d59d83c85886a56e586cfd5c1a26292e971a0fa266531ac5a" [[package]] name = "fdeflate" @@ -1432,9 +1430,9 @@ checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" [[package]] name = "fjall" -version = "3.1.3" +version = "3.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdf46551c9abc5fb0e0d540da36c875197285af2a29833892d7d3434b8617343" +checksum = "b62b25b4d815ae178d7d9e4aa32ee59f072efd5431c736abede1e6ee13c8c453" dependencies = [ "byteorder-lite", "byteview", @@ -1659,7 +1657,7 @@ dependencies = [ "js-sys", "libc", "r-efi 6.0.0", - "rand_core 0.10.0", + "rand_core 0.10.1", "wasip2", "wasip3", "wasm-bindgen", @@ -1801,9 +1799,9 @@ checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" [[package]] name = "h2" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f44da3a8150a6703ed5d34e164b875fd14c2cdab9af1252a9a1020bde2bdc54" +checksum = "171fefbc92fe4a4de27e0698d6a5b392d6a0e333506bc49133760b3bcf948733" dependencies = [ "atomic-waker", "bytes", @@ -1858,9 +1856,9 @@ checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" [[package]] name = "hashbrown" -version = "0.17.0" +version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" [[package]] name = "heck" @@ -1890,22 +1888,13 @@ dependencies = [ "serde", ] -[[package]] -name = "hmac" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" -dependencies = [ - "digest 0.10.7", -] - [[package]] name = "hmac" version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6303bc9732ae41b04cb554b844a762b4115a61bfaa81e3e83050991eeb56863f" dependencies = [ - "digest 0.11.2", + "digest 0.11.3", ] [[package]] @@ -1965,9 +1954,9 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "hybrid-array" -version = "0.4.10" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3944cf8cf766b40e2a1a333ee5e9b563f854d5fa49d6a8ca2764e97c6eddb214" +checksum = "9155a582abd142abc056962c29e3ce5ff2ad5469f4246b537ed42c5deba857da" dependencies = [ "typenum", ] @@ -1996,9 +1985,9 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.27.8" +version = "0.27.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2b52f86d1d4bc0d6b4e6826d960b1b333217e07d36b882dca570a5e1c48895b" +checksum = "33ca68d021ef39cf6463ab54c1d0f5daf03377b70561305bb89a8f83aab66e0f" dependencies = [ "http", "hyper", @@ -2027,11 +2016,9 @@ dependencies = [ "percent-encoding", "pin-project-lite", "socket2", - "system-configuration", "tokio", "tower-service", "tracing", - "windows-registry", ] [[package]] @@ -2165,9 +2152,9 @@ dependencies = [ [[package]] name = "idna_adapter" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +checksum = "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714" dependencies = [ "icu_normalizer", "icu_properties", @@ -2215,9 +2202,9 @@ dependencies = [ [[package]] name = "imageproc" -version = "0.26.1" +version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a8046da590889acc65f5880004580ebb269bbef84d6c0f5c543ec2dece46638" +checksum = "645329c490783f3ea465d2b6c7c08286fece97f15e714fd533b6c70a3ead2252" dependencies = [ "ab_glyph", "approx", @@ -2226,7 +2213,7 @@ dependencies = [ "itertools", "nalgebra", "num", - "rand 0.9.3", + "rand 0.9.4", "rand_distr", "rayon", "rustdct", @@ -2234,9 +2221,9 @@ dependencies = [ [[package]] name = "imgref" -version = "1.12.0" +version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7c5cedc30da3a610cac6b4ba17597bdf7152cf974e8aab3afb3d54455e371c8" +checksum = "40fac9d56ed6437b198fddba683305e8e2d651aa42647f00f5ae542e7f5c94a2" [[package]] name = "include_dir" @@ -2275,7 +2262,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" dependencies = [ "equivalent", - "hashbrown 0.17.0", + "hashbrown 0.17.1", "serde", "serde_core", ] @@ -2308,6 +2295,15 @@ dependencies = [ "generic-array", ] +[[package]] +name = "inout" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4250ce6452e92010fdf7268ccc5d14faa80bb12fc741938534c58f16804e03c7" +dependencies = [ + "hybrid-array", +] + [[package]] name = "interpolate_name" version = "0.2.4" @@ -2334,16 +2330,6 @@ version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" -[[package]] -name = "iri-string" -version = "0.7.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25e659a4bb38e810ebc252e53b5814ff908a8c58c2a9ce2fae1bbec24cbf4e20" -dependencies = [ - "memchr", - "serde", -] - [[package]] name = "is_terminal_polyfill" version = "1.70.2" @@ -2367,9 +2353,9 @@ checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] name = "jiff" -version = "0.2.23" +version = "0.2.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a3546dc96b6d42c5f24902af9e2538e82e39ad350b0c766eb3fbf2d8f3d8359" +checksum = "f00b5dbd620d61dfdcb6007c9c1f6054ebd75319f163d886a9055cec1155073d" dependencies = [ "jiff-static", "jiff-tzdb-platform", @@ -2382,9 +2368,9 @@ dependencies = [ [[package]] name = "jiff-static" -version = "0.2.23" +version = "0.2.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a8c8b344124222efd714b73bb41f8b5120b27a7cc1c75593a6ff768d9d05aa4" +checksum = "e000de030ff8022ea1da3f466fbb0f3a809f5e51ed31f6dd931c35181ad8e6d7" dependencies = [ "proc-macro2", "quote", @@ -2408,27 +2394,32 @@ dependencies = [ [[package]] name = "jni" -version = "0.21.1" +version = "0.22.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" +checksum = "5efd9a482cf3a427f00d6b35f14332adc7902ce91efb778580e180ff90fa3498" dependencies = [ - "cesu8", "cfg-if", "combine", - "jni-sys 0.3.1", + "jni-macros", + "jni-sys", "log", - "thiserror 1.0.69", + "simd_cesu8", + "thiserror 2.0.18", "walkdir", - "windows-sys 0.45.0", + "windows-link", ] [[package]] -name = "jni-sys" -version = "0.3.1" +name = "jni-macros" +version = "0.22.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41a652e1f9b6e0275df1f15b32661cf0d4b78d4d87ddec5e0c3c20f097433258" +checksum = "a00109accc170f0bdb141fed3e393c565b6f5e072365c3bd58f5b062591560a3" dependencies = [ - "jni-sys 0.4.1", + "proc-macro2", + "quote", + "rustc_version", + "simd_cesu8", + "syn", ] [[package]] @@ -2462,9 +2453,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.95" +version = "0.3.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2964e92d1d9dc3364cae4d718d93f227e3abb088e747d92e0395bfdedf1c12ca" +checksum = "67df7112613f8bfd9150013a0314e196f4800d3201ae742489d999db2f979f08" dependencies = [ "cfg-if", "futures-util", @@ -2492,15 +2483,15 @@ checksum = "7a79a3332a6609480d7d0c9eab957bca6b455b91bb84e66d19f5ff66294b85b8" [[package]] name = "libbz2-rs-sys" -version = "0.2.2" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c4a545a15244c7d945065b5d392b2d2d7f21526fba56ce51467b06ed445e8f7" +checksum = "34b357333733e8260735ba5894eb928c02ecc69c78715f01a8019e7fa7f2db4c" [[package]] name = "libc" -version = "0.2.184" +version = "0.2.186" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48f5d2a454e16a5ea0f4ced81bd44e4cfc7bd3a507b61887c99fd3538b28e4af" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" [[package]] name = "libfuzzer-sys" @@ -2583,7 +2574,7 @@ version = "0.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73fdcbab5b237a03984f83b1394dc534e0b1960675c7f3ec4d04dccc9032b56d" dependencies = [ - "aes", + "aes 0.8.4", "bitflags", "cbc", "chrono", @@ -2617,9 +2608,9 @@ checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" [[package]] name = "lsm-tree" -version = "3.1.3" +version = "3.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f38ed727e0965f218f4e419231b9ecacc1db8fb5066cf1df0d2bafeef88459d4" +checksum = "e447ac67ff6aef4ec07fc19e507b219336cbba90a697c0dbeb1bf51b91536b67" dependencies = [ "byteorder-lite", "byteview", @@ -2639,9 +2630,9 @@ dependencies = [ [[package]] name = "lz4_flex" -version = "0.13.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db9a0d582c2874f68138a16ce1867e0ffde6c0bb0a0df85e1f36d04146db488a" +checksum = "7ef0d4ed8669f8f8826eb00dc878084aa8f253506c4fd5e8f58f5bce72ddb97e" dependencies = [ "twox-hash", ] @@ -2832,7 +2823,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3ffa00dec017b5b1a8b7cf5e2c008bfda1aa7e0697ac1508b491fdf2622fb4d8" dependencies = [ - "rand 0.8.5", + "rand 0.8.6", ] [[package]] @@ -2841,6 +2832,15 @@ version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" +[[package]] +name = "no_std_io2" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418abd1b6d34fbf6cae440dc874771b0525a604428704c76e48b29a5e67b8003" +dependencies = [ + "memchr", +] + [[package]] name = "nom" version = "7.1.3" @@ -2920,9 +2920,9 @@ dependencies = [ [[package]] name = "num-conv" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6673768db2d862beb9b39a78fdcb1a69439615d5794a1be50caa9bc92c81967" +checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" [[package]] name = "num-derive" @@ -3018,7 +3018,7 @@ dependencies = [ "tokio", "tracing", "uuid", - "zip 8.5.1", + "zip 8.6.0", ] [[package]] @@ -3091,6 +3091,7 @@ dependencies = [ "derive_builder", "derive_more", "jiff", + "oxilangtag", "schemars 1.2.1", "semver", "serde", @@ -3128,13 +3129,13 @@ dependencies = [ "bytes", "derive_more", "futures", - "hmac 0.13.0", + "hmac", "nvisy-core", "nvisy-ontology", "reqwest-middleware", "reqwest-retry", "reqwest-tracing", - "rig-core", + "rig", "schemars 1.2.1", "serde", "serde_json", @@ -3226,6 +3227,15 @@ dependencies = [ "ttf-parser", ] +[[package]] +name = "oxilangtag" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23f3f87617a86af77fa3691e6350483e7154c2ead9f1261b75130e21ca0f8acb" +dependencies = [ + "serde", +] + [[package]] name = "parking_lot" version = "0.12.5" @@ -3263,12 +3273,12 @@ checksum = "35fb2e5f958ec131621fdd531e9fc186ed768cbe395337403ae56c17a74c68ec" [[package]] name = "pbkdf2" -version = "0.12.2" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" +checksum = "112d82ceb8c5bf524d9af484d4e4970c9fd5a0cc15ba14ad93dccd28873b0629" dependencies = [ - "digest 0.10.7", - "hmac 0.12.1", + "digest 0.11.3", + "hmac", ] [[package]] @@ -3370,18 +3380,18 @@ dependencies = [ [[package]] name = "pin-project" -version = "1.1.11" +version = "1.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1749c7ed4bcaf4c3d0a3efc28538844fb29bcdd7d2b67b2be7e20ba861ff517" +checksum = "2466b2336ed02bcdca6b294417127b90ec92038d1d5c4fbeac971a922e0e0924" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.11" +version = "1.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b20ed30f105399776b9c883e68e536ef602a16ae6f596d2c473591d6ad64c6" +checksum = "c96395f0a926bc13b1c17622aaddda1ecb55d49c8f1bf9777e4d877800a43f8b" dependencies = [ "proc-macro2", "quote", @@ -3445,9 +3455,9 @@ checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" [[package]] name = "portable-atomic-util" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "091397be61a01d4be58e7841595bd4bfedb15f1cd54977d79b8271e94ed799a3" +checksum = "c2a106d1259c23fac8e543272398ae0e3c0b8d33c88ed73d0cc71b0f1d902618" dependencies = [ "portable-atomic", ] @@ -3514,7 +3524,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" dependencies = [ "once_cell", - "toml_edit", + "toml_edit 0.19.15", +] + +[[package]] +name = "proc-macro-crate" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" +dependencies = [ + "toml_edit 0.25.11+spec-1.1.0", ] [[package]] @@ -3550,18 +3569,18 @@ dependencies = [ [[package]] name = "profiling" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3eb8486b569e12e2c32ad3e204dbaba5e4b5b216e9367044f25f1dba42341773" +checksum = "3d595e54a326bc53c1c197b32d295e14b169e3cfeaa8dc82b529f947fba6bcf5" dependencies = [ "profiling-procmacros", ] [[package]] name = "profiling-procmacros" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52717f9a02b6965224f95ca2a81e2e0c5c43baacd28ca057577988930b6c3d5b" +checksum = "4488a4a36b9a4ba6b9334a32a39971f77c1436ec82c38707bce707699cc3bbcb" dependencies = [ "quote", "syn", @@ -3569,9 +3588,9 @@ dependencies = [ [[package]] name = "pxfm" -version = "0.1.28" +version = "0.1.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5a041e753da8b807c9255f28de81879c78c876392ff2469cde94799b2896b9d" +checksum = "e0c5ccf5294c6ccd63a74f1565028353830a9c2f5eb0c682c355c471726a6e3f" [[package]] name = "pyo3" @@ -3672,9 +3691,9 @@ checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" [[package]] name = "quick-xml" -version = "0.39.2" +version = "0.39.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "958f21e8e7ceb5a1aa7fa87fab28e7c75976e0bfe7e23ff069e0a260f894067d" +checksum = "cdcc8dd4e2f670d309a5f0e83fe36dfdc05af317008fea29144da1a2ac858e5e" dependencies = [ "encoding_rs", "memchr", @@ -3682,9 +3701,9 @@ dependencies = [ [[package]] name = "quick_cache" -version = "0.6.21" +version = "0.6.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a70b1b8b47e31d0498ecbc3c5470bb931399a8bfed1fd79d1717a61ce7f96e3" +checksum = "d1c821816e9b928e20e92ed59bb3ac4aab321d16ca2316871c9fe7ca739cd477" dependencies = [ "equivalent", "hashbrown 0.16.1", @@ -3720,7 +3739,7 @@ dependencies = [ "bytes", "getrandom 0.3.4", "lru-slab", - "rand 0.9.3", + "rand 0.9.4", "ring", "rustc-hash", "rustls", @@ -3769,9 +3788,9 @@ checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" [[package]] name = "rand" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" dependencies = [ "libc", "rand_chacha 0.3.1", @@ -3780,9 +3799,9 @@ dependencies = [ [[package]] name = "rand" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ec095654a25171c2124e9e3393a930bddbffdc939556c914957a4c3e0a87166" +checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" dependencies = [ "rand_chacha 0.9.0", "rand_core 0.9.5", @@ -3796,7 +3815,7 @@ checksum = "d2e8e8bcc7961af1fdac401278c6a831614941f6164ee3bf4ce61b7edb162207" dependencies = [ "chacha20", "getrandom 0.4.2", - "rand_core 0.10.0", + "rand_core 0.10.1", ] [[package]] @@ -3839,9 +3858,9 @@ dependencies = [ [[package]] name = "rand_core" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c8d0fd677905edcbeedbf2edb6494d676f0e98d54d5cf9bda0b061cb8fb8aba" +checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" [[package]] name = "rand_distr" @@ -3850,7 +3869,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a8615d50dcf34fa31f7ab52692afec947c4dd0ab803cc87cb3b0b4570ff7463" dependencies = [ "num-traits", - "rand 0.9.3", + "rand 0.9.4", ] [[package]] @@ -3886,7 +3905,7 @@ dependencies = [ "num-traits", "paste", "profiling", - "rand 0.9.3", + "rand 0.9.4", "rand_chacha 0.9.0", "simd_helpers", "thiserror 2.0.18", @@ -3917,9 +3936,9 @@ checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" [[package]] name = "rayon" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" +checksum = "fb39b166781f92d482534ef4b4b1b2568f42613b53e5b6c160e24cfbfa30926d" dependencies = [ "either", "rayon-core", @@ -3995,9 +4014,9 @@ checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" [[package]] name = "reqwest" -version = "0.13.2" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab3f43e3283ab1488b624b44b0e988d0acea0b3214e694730a055cb6b2efa801" +checksum = "62e0021ea2c22aed41653bc7e1419abb2c97e038ff2c33d0e1309e49a97deec0" dependencies = [ "base64", "bytes", @@ -4091,11 +4110,11 @@ dependencies = [ [[package]] name = "retry-policies" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46a4bd6027df676bcb752d3724db0ea3c0c5fc1dd0376fec51ac7dcaf9cc69be" +checksum = "dc05fbf560421a0357a750cbe78c7ca19d4923918490daabba313d5dbc871e47" dependencies = [ - "rand 0.9.3", + "rand 0.10.1", ] [[package]] @@ -4104,11 +4123,20 @@ version = "0.8.53" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "47b34b781b31e5d73e9fbc8689c70551fd1ade9a19e3e28cfec8580a79290cc4" +[[package]] +name = "rig" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d928cf3d6427216f3df8b1f4f6c38909d1d512d3ce5e1f503ac5dd869ab0540" +dependencies = [ + "rig-core", +] + [[package]] name = "rig-core" -version = "0.33.0" +version = "0.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a529b9b72f51a46a9ea87c9ba8031e36593de7a89461983f01d8c24462f9eb06" +checksum = "a6bd308c89a90f89ce7cd6641a078c7d2a2c55fb5a4147fd48b5a0989c3430bd" dependencies = [ "as-any", "async-stream", @@ -4141,13 +4169,14 @@ dependencies = [ [[package]] name = "rig-derive" -version = "0.1.12" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7590f1ffc5cef2af569072500c3ee02836c6cfb9faee9b6f0fc140428a50891" +checksum = "5ba9149c63403a49ddfd5d373860487e6feef0021bfe5329812d1c4e72ee207c" dependencies = [ "convert_case", "deluxe", "indoc", + "proc-macro-crate 3.5.0", "proc-macro2", "quote", "serde_json", @@ -4221,9 +4250,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.38" +version = "0.23.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69f9466fb2c14ea04357e91413efb882e2a6d4a406e625449bc0a5d360d53a21" +checksum = "ef86cd5876211988985292b91c96a8f2d298df24e75989a43a3c73f2d4d8168b" dependencies = [ "aws-lc-rs", "once_cell", @@ -4247,9 +4276,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.14.0" +version = "1.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd" +checksum = "30a7197ae7eb376e574fe940d068c30fe0462554a3ddbe4eca7838e049c937a9" dependencies = [ "web-time", "zeroize", @@ -4257,11 +4286,11 @@ dependencies = [ [[package]] name = "rustls-platform-verifier" -version = "0.6.2" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d99feebc72bae7ab76ba994bb5e121b8d83d910ca40b36e0921f53becc41784" +checksum = "26d1e2536ce4f35f4846aa13bff16bd0ff40157cdb14cc056c7b14ba41233ba0" dependencies = [ - "core-foundation 0.10.1", + "core-foundation", "core-foundation-sys", "jni", "log", @@ -4284,9 +4313,9 @@ checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" [[package]] name = "rustls-webpki" -version = "0.103.11" +version = "0.103.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20a6af516fea4b20eccceaf166e8aa666ac996208e8a644ce3ef5aa783bc7cd4" +checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" dependencies = [ "aws-lc-rs", "ring", @@ -4407,7 +4436,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" dependencies = [ "bitflags", - "core-foundation 0.10.1", + "core-foundation", "core-foundation-sys", "libc", "security-framework-sys", @@ -4525,15 +4554,15 @@ dependencies = [ [[package]] name = "serde_qs" -version = "0.14.0" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b417bedc008acbdf6d6b4bc482d29859924114bbe2650b7921fb68a261d0aa6" +checksum = "67d525c8ff68aa99e5818302259bdd02d86d0303710616f39c0f44846ff6d332" dependencies = [ "axum", - "futures", + "itoa", "percent-encoding", + "ryu", "serde", - "thiserror 2.0.18", ] [[package]] @@ -4559,11 +4588,12 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.18.0" +version = "3.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd5414fad8e6907dbdd5bc441a50ae8d6e26151a03b1de04d89a5576de61d01f" +checksum = "e72c1c2cb7b223fafb600a619537a871c2818583d619401b785e7c0b746ccde2" dependencies = [ "base64", + "bs58", "chrono", "hex", "indexmap 1.9.3", @@ -4578,9 +4608,9 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.18.0" +version = "3.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3db8978e608f1fe7357e211969fd9abdcae80bac1ba7a3369bb7eb6b404eb65" +checksum = "b90c488738ecb4fb0262f41f43bc40efc5868d9fb744319ddf5f5317f417bfac" dependencies = [ "darling 0.23.0", "proc-macro2", @@ -4619,6 +4649,17 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "sha1" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aacc4cc499359472b4abe1bf11d0b12e688af9a805fa5e3016f9a386dc2d0214" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.0", + "digest 0.11.3", +] + [[package]] name = "sha2" version = "0.10.9" @@ -4638,7 +4679,7 @@ checksum = "446ba717509524cb3f22f17ecc096f10f4822d76ab5c0b9822c5f9c284e825f4" dependencies = [ "cfg-if", "cpufeatures 0.3.0", - "digest 0.11.2", + "digest 0.11.3", ] [[package]] @@ -4685,6 +4726,16 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" +[[package]] +name = "simd_cesu8" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94f90157bb87cddf702797c5dadfa0be7d266cdf49e22da2fcaa32eff75b2c33" +dependencies = [ + "rustc_version", + "simdutf8", +] + [[package]] name = "simd_helpers" version = "0.1.0" @@ -4694,11 +4745,17 @@ dependencies = [ "quote", ] +[[package]] +name = "simdutf8" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" + [[package]] name = "siphasher" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2aa850e253778c88a04c3d7323b043aeda9d3e30d5971937c1855769763678e" +checksum = "8ee5873ec9cce0195efcb7a4e9507a04cd49aec9c83d0389df45b1ef7ba2e649" [[package]] name = "slab" @@ -4848,27 +4905,6 @@ dependencies = [ "syn", ] -[[package]] -name = "system-configuration" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b" -dependencies = [ - "bitflags", - "core-foundation 0.9.4", - "system-configuration-sys", -] - -[[package]] -name = "system-configuration-sys" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" -dependencies = [ - "core-foundation-sys", - "libc", -] - [[package]] name = "target-lexicon" version = "0.13.5" @@ -5020,9 +5056,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.51.1" +version = "1.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f66bf9585cda4b724d3e78ab34b73fb2bbaba9011b9bfdf69dc836382ea13b8c" +checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe" dependencies = [ "bytes", "libc", @@ -5096,7 +5132,7 @@ dependencies = [ "toml_datetime 1.1.1+spec-1.1.0", "toml_parser", "toml_writer", - "winnow 1.0.1", + "winnow 1.0.3", ] [[package]] @@ -5125,13 +5161,25 @@ dependencies = [ "winnow 0.5.40", ] +[[package]] +name = "toml_edit" +version = "0.25.11+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b59c4d22ed448339746c59b905d24568fcbb3ab65a500494f7b8c3e97739f2b" +dependencies = [ + "indexmap 2.14.0", + "toml_datetime 1.1.1+spec-1.1.0", + "toml_parser", + "winnow 1.0.3", +] + [[package]] name = "toml_parser" version = "1.1.2+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" dependencies = [ - "winnow 1.0.1", + "winnow 1.0.3", ] [[package]] @@ -5158,9 +5206,9 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.6.8" +version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" +checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840" dependencies = [ "async-compression", "bitflags", @@ -5170,7 +5218,6 @@ dependencies = [ "http", "http-body", "http-body-util", - "iri-string", "pin-project-lite", "tokio", "tokio-util", @@ -5178,6 +5225,7 @@ dependencies = [ "tower-layer", "tower-service", "tracing", + "url", "uuid", ] @@ -5314,10 +5362,10 @@ dependencies = [ "http", "httparse", "log", - "rand 0.8.5", + "rand 0.8.6", "rustls", "rustls-pki-types", - "sha1", + "sha1 0.10.6", "thiserror 1.0.69", "utf-8", ] @@ -5336,9 +5384,9 @@ checksum = "8e28f89b80c87b8fb0cf04ab448d5dd0dd0ade2f8891bae878de66a75a28600e" [[package]] name = "typenum" -version = "1.19.0" +version = "1.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" +checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de" [[package]] name = "unicase" @@ -5448,9 +5496,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.23.0" +version = "1.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ac8b6f42ead25368cf5b098aeb3dc8a1a2c05a3eee8a9a1a68c640edbfc79d9" +checksum = "ddd74a9687298c6858e9b88ec8935ec45d22e8fd5e6394fa1bd4e99a87789c76" dependencies = [ "getrandom 0.4.2", "js-sys", @@ -5553,11 +5601,11 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasip2" -version = "1.0.2+wasi-0.2.9" +version = "1.0.3+wasi-0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" +checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6" dependencies = [ - "wit-bindgen", + "wit-bindgen 0.57.1", ] [[package]] @@ -5566,14 +5614,14 @@ version = "0.4.0+wasi-0.3.0-rc-2026-01-06" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" dependencies = [ - "wit-bindgen", + "wit-bindgen 0.51.0", ] [[package]] name = "wasm-bindgen" -version = "0.2.118" +version = "0.2.121" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf938a0bacb0469e83c1e148908bd7d5a6010354cf4fb73279b7447422e3a89" +checksum = "49ace1d07c165b0864824eee619580c4689389afa9dc9ed3a4c75040d82e6790" dependencies = [ "cfg-if", "once_cell", @@ -5584,9 +5632,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.68" +version = "0.4.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f371d383f2fb139252e0bfac3b81b265689bf45b6874af544ffa4c975ac1ebf8" +checksum = "96492d0d3ffba25305a7dc88720d250b1401d7edca02cc3bcd50633b424673b8" dependencies = [ "js-sys", "wasm-bindgen", @@ -5594,9 +5642,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.118" +version = "0.2.121" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eeff24f84126c0ec2db7a449f0c2ec963c6a49efe0698c4242929da037ca28ed" +checksum = "8e68e6f4afd367a562002c05637acb8578ff2dea1943df76afb9e83d177c8578" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -5604,9 +5652,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.118" +version = "0.2.121" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d08065faf983b2b80a79fd87d8254c409281cf7de75fc4b773019824196c904" +checksum = "d95a9ec35c64b2a7cb35d3fead40c4238d0940c86d107136999567a4703259f2" dependencies = [ "bumpalo", "proc-macro2", @@ -5617,9 +5665,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.118" +version = "0.2.121" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fd04d9e306f1907bd13c6361b5c6bfc7b3b3c095ed3f8a9246390f8dbdee129" +checksum = "c4e0100b01e9f0d03189a92b96772a1fb998639d981193d7dbab487302513441" dependencies = [ "unicode-ident", ] @@ -5687,9 +5735,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.95" +version = "0.3.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f2dfbb17949fa2088e5d39408c48368947b86f7834484e87b73de55bc14d97d" +checksum = "4b572dff8bcf38bad0fa19729c89bb5748b2b9b1d8be70cf90df697e3a8f32aa" dependencies = [ "js-sys", "wasm-bindgen", @@ -5707,9 +5755,9 @@ dependencies = [ [[package]] name = "web_atoms" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57a9779e9f04d2ac1ce317aee707aa2f6b773afba7b931222bff6983843b1576" +checksum = "d7cff6eef815df1834fd250e3a2ff436044d82a9f1bc1980ca1dbdf07effc538" dependencies = [ "phf", "phf_codegen", @@ -5719,9 +5767,9 @@ dependencies = [ [[package]] name = "webpki-root-certs" -version = "1.0.6" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "804f18a4ac2676ffb4e8b5b5fa9ae38af06df08162314f96a68d2a363e21a8ca" +checksum = "f31141ce3fc3e300ae89b78c0dd67f9708061d1d2eda54b8209346fd6be9a92c" dependencies = [ "rustls-pki-types", ] @@ -5732,14 +5780,14 @@ version = "0.26.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" dependencies = [ - "webpki-roots 1.0.6", + "webpki-roots 1.0.7", ] [[package]] name = "webpki-roots" -version = "1.0.6" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22cfaf3c063993ff62e73cb4311efde4db1efb31ab78a3e5c457939ad5cc0bed" +checksum = "52f5ee44c96cf55f1b349600768e3ece3a8f26010c05265ab73f945bb1a2eb9d" dependencies = [ "rustls-pki-types", ] @@ -5810,17 +5858,6 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" -[[package]] -name = "windows-registry" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720" -dependencies = [ - "windows-link", - "windows-result", - "windows-strings", -] - [[package]] name = "windows-result" version = "0.4.1" @@ -5839,15 +5876,6 @@ dependencies = [ "windows-link", ] -[[package]] -name = "windows-sys" -version = "0.45.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" -dependencies = [ - "windows-targets 0.42.2", -] - [[package]] name = "windows-sys" version = "0.52.0" @@ -5875,21 +5903,6 @@ dependencies = [ "windows-link", ] -[[package]] -name = "windows-targets" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" -dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", -] - [[package]] name = "windows-targets" version = "0.52.6" @@ -5923,12 +5936,6 @@ dependencies = [ "windows_x86_64_msvc 0.53.1", ] -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" @@ -5941,12 +5948,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" -[[package]] -name = "windows_aarch64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" - [[package]] name = "windows_aarch64_msvc" version = "0.52.6" @@ -5959,12 +5960,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" -[[package]] -name = "windows_i686_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" - [[package]] name = "windows_i686_gnu" version = "0.52.6" @@ -5989,12 +5984,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" -[[package]] -name = "windows_i686_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" - [[package]] name = "windows_i686_msvc" version = "0.52.6" @@ -6007,12 +5996,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" -[[package]] -name = "windows_x86_64_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" - [[package]] name = "windows_x86_64_gnu" version = "0.52.6" @@ -6025,12 +6008,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" - [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" @@ -6043,12 +6020,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" - [[package]] name = "windows_x86_64_msvc" version = "0.52.6" @@ -6072,9 +6043,12 @@ dependencies = [ [[package]] name = "winnow" -version = "1.0.1" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09dac053f1cd375980747450bfc7250c264eaae0583872e845c0c7cd578872b5" +checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1" +dependencies = [ + "memchr", +] [[package]] name = "wit-bindgen" @@ -6085,6 +6059,12 @@ dependencies = [ "wit-bindgen-rust-macro", ] +[[package]] +name = "wit-bindgen" +version = "0.57.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" + [[package]] name = "wit-bindgen-core" version = "0.51.0" @@ -6227,9 +6207,9 @@ dependencies = [ [[package]] name = "zerofrom" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69faa1f2a1ea75661980b013019ed6687ed0e83d069bc1114e2cc74c6c04c4df" +checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272" dependencies = [ "zerofrom-derive", ] @@ -6301,24 +6281,24 @@ dependencies = [ [[package]] name = "zip" -version = "8.5.1" +version = "8.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcab981e19633ebcf0b001ddd37dd802996098bc1864f90b7c5d970ce76c1d59" +checksum = "2d04a6b5381502aa6087c94c669499eb1602eb9c5e8198e534de571f7154809b" dependencies = [ - "aes", + "aes 0.9.0", "bzip2", "constant_time_eq", "crc32fast", "deflate64", "flate2", "getrandom 0.4.2", - "hmac 0.12.1", + "hmac", "indexmap 2.14.0", "lzma-rust2", "memchr", "pbkdf2", "ppmd-rust", - "sha1", + "sha1 0.11.0", "time", "typed-path", "zeroize", diff --git a/Cargo.toml b/Cargo.toml index 1bc978b8..73154764 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,7 @@ members = [ [workspace.package] version = "0.1.0" -rust-version = "1.92" +rust-version = "1.95" edition = "2024" license = "Apache-2.0" publish = false @@ -43,7 +43,7 @@ nvisy-python = { path = "./crates/nvisy-python", version = "0.1.0" } nvisy-server = { path = "./crates/nvisy-server", version = "0.1.0" } # LLM framework -rig-core = { version = "0.33", features = [] } +rig = { version = "0.37", features = [], default-features = false } # HTTP client and middleware reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls", "multipart"] } @@ -73,7 +73,7 @@ toml = { version = "1.1", features = [] } # Derive macros and error handling thiserror = { version = "2.0", features = [] } anyhow = { version = "1.0", features = [] } -derive_more = { version = "2.0", features = ["as_ref", "deref", "deref_mut", "display", "from", "into", "is_variant", "try_into"] } +derive_more = { version = "2.0", features = ["as_ref", "deref", "deref_mut", "display", "from", "from_str", "into", "into_iterator", "is_variant", "try_into"] } derive_builder = { version = "0.20", features = [] } strum = { version = "0.28", features = ["derive"] } @@ -83,6 +83,7 @@ bytes = { version = "1.0", features = ["serde"] } hipstr = { version = "0.8", features = [] } jiff = { version = "0.2", features = ["serde"] } semver = { version = "1.0", features = ["serde"] } +oxilangtag = { version = "0.1", features = ["serde"] } # Encoding and hashing base64 = { version = "0.22", features = [] } diff --git a/crates/nvisy-cli/src/config/file.rs b/crates/nvisy-cli/src/config/file.rs index 29fbc299..f58c3dbf 100644 --- a/crates/nvisy-cli/src/config/file.rs +++ b/crates/nvisy-cli/src/config/file.rs @@ -17,6 +17,7 @@ //! [ocr] / [llm] / [stt] / [tts] # ┘ → RuntimeConfig (flattened) //! ``` +use std::fs; use std::net::IpAddr; use std::path::PathBuf; @@ -124,7 +125,7 @@ impl FileConfig { if !path.exists() { return Ok(Self::default()); } - let contents = std::fs::read_to_string(path)?; + let contents = fs::read_to_string(path)?; let config = toml::from_str(&contents)?; Ok(config) } diff --git a/crates/nvisy-cli/src/config/server.rs b/crates/nvisy-cli/src/config/server.rs index ddeb705c..f3612845 100644 --- a/crates/nvisy-cli/src/config/server.rs +++ b/crates/nvisy-cli/src/config/server.rs @@ -16,6 +16,7 @@ //! | Shutdown timeout | `--shutdown-timeout` | `SHUTDOWN_TIMEOUT` | `30` s | //! | Data directory | `--data-dir` | `DATA_DIR` | `$TMPDIR/nvisy-server-data`| +use std::env; use std::net::{IpAddr, Ipv4Addr, SocketAddr}; use std::path::PathBuf; use std::time::Duration; @@ -77,7 +78,7 @@ impl ServerConfig { .data_dir .clone() .or_else(|| toml.and_then(|s| s.data_dir.clone())) - .unwrap_or_else(|| std::env::temp_dir().join("nvisy-server-data")), + .unwrap_or_else(|| env::temp_dir().join("nvisy-server-data")), log_level: obs.level, log_format: obs.format, } diff --git a/crates/nvisy-cli/src/server/listen.rs b/crates/nvisy-cli/src/server/listen.rs index 050ec34f..2e69f88f 100644 --- a/crates/nvisy-cli/src/server/listen.rs +++ b/crates/nvisy-cli/src/server/listen.rs @@ -1,5 +1,6 @@ //! TCP listener binding and graceful server lifecycle. +use std::fs; use std::path::Path; use tokio::net::TcpListener; @@ -34,7 +35,7 @@ fn cleanup_data_dir(path: &Path) { if !path.exists() { return; } - match std::fs::remove_dir_all(path) { + match fs::remove_dir_all(path) { Ok(()) => { tracing::info!(target: TARGET, path = %path.display(), "data directory cleaned up") } diff --git a/crates/nvisy-cli/src/server/shutdown.rs b/crates/nvisy-cli/src/server/shutdown.rs index d9faae65..67540efa 100644 --- a/crates/nvisy-cli/src/server/shutdown.rs +++ b/crates/nvisy-cli/src/server/shutdown.rs @@ -1,5 +1,7 @@ //! Graceful shutdown signal handling. +#[cfg(not(unix))] +use std::future; use std::time::Duration; use tokio::signal::ctrl_c; @@ -50,7 +52,7 @@ pub async fn shutdown_signal(shutdown_timeout: Duration) { }; #[cfg(not(unix))] - let terminate = std::future::pending::<()>(); + let terminate = future::pending::<()>(); tokio::select! { () = ctrl_c => {}, diff --git a/crates/nvisy-codec/src/document/mod.rs b/crates/nvisy-codec/src/document/mod.rs index 1c438ba7..b44a3912 100644 --- a/crates/nvisy-codec/src/document/mod.rs +++ b/crates/nvisy-codec/src/document/mod.rs @@ -3,6 +3,8 @@ mod span; mod stream; +use std::fmt; + use derive_more::{From, IsVariant, TryInto}; use futures::StreamExt; use nvisy_core::Error; @@ -38,8 +40,8 @@ pub enum ContentHandle { Rich(BoxedRichHandler), } -impl std::fmt::Debug for ContentHandle { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +impl fmt::Debug for ContentHandle { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_tuple("ContentHandle") .field(&self.document_type()) .finish() diff --git a/crates/nvisy-codec/src/handler/audio/audio_handler.rs b/crates/nvisy-codec/src/handler/audio/audio_handler.rs index fb0d7e9b..77b2469d 100644 --- a/crates/nvisy-codec/src/handler/audio/audio_handler.rs +++ b/crates/nvisy-codec/src/handler/audio/audio_handler.rs @@ -1,5 +1,7 @@ //! [`BoxedAudioHandler`]: type-erased wrapper over all audio handler types. +use std::fmt; + use nvisy_core::Error; use nvisy_core::content::{ContentData, ContentSource}; use nvisy_core::media::DocumentType; @@ -19,8 +21,8 @@ impl BoxedAudioHandler { } } -impl std::fmt::Debug for BoxedAudioHandler { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +impl fmt::Debug for BoxedAudioHandler { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_tuple("BoxedAudioHandler") .field(&self.0.document_type()) .finish() diff --git a/crates/nvisy-codec/src/handler/audio/audio_handler_macro.rs b/crates/nvisy-codec/src/handler/audio/audio_handler_macro.rs index 3d7e04aa..94a2fd28 100644 --- a/crates/nvisy-codec/src/handler/audio/audio_handler_macro.rs +++ b/crates/nvisy-codec/src/handler/audio/audio_handler_macro.rs @@ -2,6 +2,9 @@ /// Implement [`Handler`] + [`AudioHandler`] + inherent methods for an /// audio handler struct that holds raw bytes. +/// +/// [`Handler`]: crate::handler::Handler +/// [`AudioHandler`]: crate::handler::AudioHandler macro_rules! impl_audio_handler { ($handler:ident, $doc_type:expr, $origin:literal, $encode_name:literal) => { impl crate::handler::Handler for $handler { @@ -39,14 +42,16 @@ macro_rules! impl_audio_handler { // a placeholder. The actual time span is set by the // STT extraction operation after transcription. let location = nvisy_ontology::entity::AudioLocation { - time_span: nvisy_ontology::math::TimeSpan { + time_span: nvisy_ontology::primitive::TimeSpan { start_us: 0, end_us: 0, }, speaker_id: None, audio_id: None, }; - crate::document::SpanStream::new(futures::stream::iter(std::iter::once( + use ::std::iter; + + crate::document::SpanStream::new(futures::stream::iter(iter::once( crate::document::Span::new( location, crate::handler::AudioData::new(self.bytes.clone()), diff --git a/crates/nvisy-codec/src/handler/audio/mp3_handler.rs b/crates/nvisy-codec/src/handler/audio/mp3_handler.rs index 5e67feff..dd9311ce 100644 --- a/crates/nvisy-codec/src/handler/audio/mp3_handler.rs +++ b/crates/nvisy-codec/src/handler/audio/mp3_handler.rs @@ -48,7 +48,6 @@ mod tests { #[tokio::test] async fn edit_spans_replaces_bytes() -> Result<(), Error> { - use nvisy_ontology::entity::AudioLocation; let mut h = Mp3Handler::new(Bytes::from_static(b"original")); let spans: Vec<_> = { use futures::StreamExt; diff --git a/crates/nvisy-codec/src/handler/audio/wav_handler.rs b/crates/nvisy-codec/src/handler/audio/wav_handler.rs index 156ef3d5..1bb9f592 100644 --- a/crates/nvisy-codec/src/handler/audio/wav_handler.rs +++ b/crates/nvisy-codec/src/handler/audio/wav_handler.rs @@ -48,7 +48,6 @@ mod tests { #[tokio::test] async fn edit_spans_replaces_bytes() -> Result<(), Error> { - use nvisy_ontology::entity::AudioLocation; let mut h = WavHandler::new(Bytes::from_static(b"original")); let spans: Vec<_> = { use futures::StreamExt; diff --git a/crates/nvisy-codec/src/handler/image/image_data.rs b/crates/nvisy-codec/src/handler/image/image_data.rs index 2a597556..02516134 100644 --- a/crates/nvisy-codec/src/handler/image/image_data.rs +++ b/crates/nvisy-codec/src/handler/image/image_data.rs @@ -1,5 +1,7 @@ //! [`ImageData`]: opaque wrapper around a decoded image. +use std::io::Cursor; + use derive_more::{From, Into}; use image::DynamicImage; use nvisy_core::Error; @@ -27,7 +29,7 @@ impl ImageData { /// Encode to PNG bytes. pub fn encode_png(&self) -> Result { - let mut buf = std::io::Cursor::new(Vec::new()); + let mut buf = Cursor::new(Vec::new()); self.0 .write_to(&mut buf, image::ImageFormat::Png) .map_err(|e| Error::validation(format!("PNG encode failed: {e}"), "image-data"))?; diff --git a/crates/nvisy-codec/src/handler/image/image_handler.rs b/crates/nvisy-codec/src/handler/image/image_handler.rs index 0f01b5d2..f8027620 100644 --- a/crates/nvisy-codec/src/handler/image/image_handler.rs +++ b/crates/nvisy-codec/src/handler/image/image_handler.rs @@ -1,5 +1,7 @@ //! [`BoxedImageHandler`]: type-erased wrapper over all image handler types. +use std::fmt; + use nvisy_core::Error; use nvisy_core::content::{ContentData, ContentSource}; use nvisy_core::media::DocumentType; @@ -19,8 +21,8 @@ impl BoxedImageHandler { } } -impl std::fmt::Debug for BoxedImageHandler { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +impl fmt::Debug for BoxedImageHandler { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_tuple("BoxedImageHandler") .field(&self.0.document_type()) .finish() diff --git a/crates/nvisy-codec/src/handler/image/image_handler_macro.rs b/crates/nvisy-codec/src/handler/image/image_handler_macro.rs index 7751811c..88c84e84 100644 --- a/crates/nvisy-codec/src/handler/image/image_handler_macro.rs +++ b/crates/nvisy-codec/src/handler/image/image_handler_macro.rs @@ -2,6 +2,9 @@ /// Implement [`Handler`] + [`ImageHandler`] + inherent methods for an /// image handler struct that holds a single `DynamicImage`. +/// +/// [`Handler`]: crate::handler::Handler +/// [`ImageHandler`]: crate::handler::ImageHandler macro_rules! impl_image_handler { ($handler:ident, $doc_type:expr, $fmt:expr, $origin:literal, $encode_name:literal) => { impl crate::handler::Handler for $handler { @@ -15,7 +18,9 @@ macro_rules! impl_image_handler { #[tracing::instrument(name = $encode_name, skip_all, fields(output_bytes))] fn encode(&self) -> Result { - let mut buf = std::io::Cursor::new(Vec::new()); + use ::std::io::Cursor; + + let mut buf = Cursor::new(Vec::new()); self.image.write_to(&mut buf, $fmt).map_err(|e| { nvisy_core::Error::validation(format!("encode failed: {e}"), $origin) })?; @@ -37,7 +42,7 @@ macro_rules! impl_image_handler { > { let (w, h) = (self.image.width(), self.image.height()); let location = nvisy_ontology::entity::ImageLocation { - bounding_box: nvisy_ontology::math::BoundingBox { + bounding_box: nvisy_ontology::primitive::BoundingBox { x: 0.0, y: 0.0, width: w as f64, @@ -46,7 +51,9 @@ macro_rules! impl_image_handler { image_id: None, page_number: None, }; - crate::document::SpanStream::new(futures::stream::iter(std::iter::once( + use ::std::iter; + + crate::document::SpanStream::new(futures::stream::iter(iter::once( crate::document::Span::new( location, crate::handler::ImageData::from(self.image.clone()), diff --git a/crates/nvisy-codec/src/handler/image/jpeg_handler.rs b/crates/nvisy-codec/src/handler/image/jpeg_handler.rs index ce187809..d6951d6a 100644 --- a/crates/nvisy-codec/src/handler/image/jpeg_handler.rs +++ b/crates/nvisy-codec/src/handler/image/jpeg_handler.rs @@ -8,6 +8,8 @@ //! [`DynamicImage`](image::DynamicImage). //! [`ImageHandler::edit_images`](crate::handler::ImageHandler::edit_images) //! replaces the image in-place. +//! +//! [`Span`]: crate::document::Span use nvisy_core::content::ContentSource; diff --git a/crates/nvisy-codec/src/handler/image/png_handler.rs b/crates/nvisy-codec/src/handler/image/png_handler.rs index 7b869d71..4e302986 100644 --- a/crates/nvisy-codec/src/handler/image/png_handler.rs +++ b/crates/nvisy-codec/src/handler/image/png_handler.rs @@ -8,6 +8,8 @@ //! [`DynamicImage`](image::DynamicImage). //! [`ImageHandler::edit_images`](crate::handler::ImageHandler::edit_images) //! replaces the image in-place. +//! +//! [`Span`]: crate::document::Span use nvisy_core::content::ContentSource; diff --git a/crates/nvisy-codec/src/handler/rich/pdf_handler.rs b/crates/nvisy-codec/src/handler/rich/pdf_handler.rs index e9f82050..9c4a940a 100644 --- a/crates/nvisy-codec/src/handler/rich/pdf_handler.rs +++ b/crates/nvisy-codec/src/handler/rich/pdf_handler.rs @@ -19,7 +19,7 @@ use nvisy_core::Error; use nvisy_core::content::{ContentData, ContentSource}; use nvisy_core::media::DocumentType; use nvisy_ontology::entity::{ImageLocation, TextLocation}; -use nvisy_ontology::math::Dpi; +use nvisy_ontology::primitive::Dpi; use super::pdf_render::PdfRenderer; use crate::document::{Span, SpanStream}; @@ -270,7 +270,7 @@ impl ImageHandler for RichTextHandler { // the page requires PDF content stream parsing. For now, // use a full-page placeholder that identifies the page. let location = ImageLocation { - bounding_box: nvisy_ontology::math::BoundingBox::default(), + bounding_box: nvisy_ontology::primitive::BoundingBox::default(), image_id: None, page_number: Some((i + 1) as u32), }; diff --git a/crates/nvisy-codec/src/handler/rich/pdf_loader.rs b/crates/nvisy-codec/src/handler/rich/pdf_loader.rs index 194102e6..49e4109b 100644 --- a/crates/nvisy-codec/src/handler/rich/pdf_loader.rs +++ b/crates/nvisy-codec/src/handler/rich/pdf_loader.rs @@ -2,6 +2,8 @@ //! //! Text is extracted per page via [`lopdf`]. The raw bytes are //! preserved for encoding and rendering. +//! +//! [`lopdf`]: https://docs.rs/lopdf use nvisy_core::content::{ContentData, ContentSource}; diff --git a/crates/nvisy-codec/src/handler/rich/pdf_render.rs b/crates/nvisy-codec/src/handler/rich/pdf_render.rs index d240fd96..3b33d6eb 100644 --- a/crates/nvisy-codec/src/handler/rich/pdf_render.rs +++ b/crates/nvisy-codec/src/handler/rich/pdf_render.rs @@ -9,7 +9,7 @@ use std::cell::RefCell; use std::sync::LazyLock; use nvisy_core::Error; -use nvisy_ontology::math::Dpi; +use nvisy_ontology::primitive::Dpi; use pdfium_render::prelude::*; use crate::handler::image::ImageData; diff --git a/crates/nvisy-codec/src/handler/rich/rich_handler.rs b/crates/nvisy-codec/src/handler/rich/rich_handler.rs index 413c2b7e..f694cd8b 100644 --- a/crates/nvisy-codec/src/handler/rich/rich_handler.rs +++ b/crates/nvisy-codec/src/handler/rich/rich_handler.rs @@ -1,5 +1,7 @@ //! [`BoxedRichHandler`]: type-erased wrapper over all rich-document handler types. +use std::fmt; + use nvisy_core::Error; use nvisy_core::content::{ContentData, ContentSource}; use nvisy_core::media::DocumentType; @@ -76,8 +78,8 @@ impl BoxedRichHandler { } } -impl std::fmt::Debug for BoxedRichHandler { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +impl fmt::Debug for BoxedRichHandler { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_tuple("BoxedRichHandler") .field(&self.0.document_type()) .finish() diff --git a/crates/nvisy-codec/src/handler/text/html_loader.rs b/crates/nvisy-codec/src/handler/text/html_loader.rs index 3247f6e5..be5cb0e8 100644 --- a/crates/nvisy-codec/src/handler/text/html_loader.rs +++ b/crates/nvisy-codec/src/handler/text/html_loader.rs @@ -3,6 +3,8 @@ //! //! The loader parses the input using [`scraper`], extracts text nodes //! in document order, and produces a handler backed by those nodes. +//! +//! [`scraper`]: https://docs.rs/scraper use nvisy_core::Error; use nvisy_core::content::{ContentData, ContentSource}; diff --git a/crates/nvisy-codec/src/handler/text/markdown_loader.rs b/crates/nvisy-codec/src/handler/text/markdown_loader.rs index 4bca4b88..176add00 100644 --- a/crates/nvisy-codec/src/handler/text/markdown_loader.rs +++ b/crates/nvisy-codec/src/handler/text/markdown_loader.rs @@ -5,6 +5,8 @@ //! [`TxtHandler`] for span access and editing; the format distinction //! is carried by the [`DocumentType`] so downstream operations can //! apply Markdown-aware processing when needed. +//! +//! [`DocumentType`]: nvisy_core::media::DocumentType use nvisy_core::Error; use nvisy_core::content::{ContentData, ContentSource, TextEncoding}; diff --git a/crates/nvisy-codec/src/handler/text/text_handler.rs b/crates/nvisy-codec/src/handler/text/text_handler.rs index 02ceeaa3..1cb19b8e 100644 --- a/crates/nvisy-codec/src/handler/text/text_handler.rs +++ b/crates/nvisy-codec/src/handler/text/text_handler.rs @@ -1,5 +1,7 @@ //! [`BoxedTextHandler`]: type-erased wrapper over all text handler types. +use std::fmt; + use nvisy_core::Error; use nvisy_core::content::{ContentData, ContentSource}; use nvisy_core::media::DocumentType; @@ -23,8 +25,8 @@ impl BoxedTextHandler { } } -impl std::fmt::Debug for BoxedTextHandler { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +impl fmt::Debug for BoxedTextHandler { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_tuple("BoxedTextHandler") .field(&self.0.document_type()) .finish() diff --git a/crates/nvisy-codec/src/transform/audio/instruction.rs b/crates/nvisy-codec/src/transform/audio/instruction.rs index c97da1d2..c14e334d 100644 --- a/crates/nvisy-codec/src/transform/audio/instruction.rs +++ b/crates/nvisy-codec/src/transform/audio/instruction.rs @@ -1,6 +1,6 @@ //! Audio redaction instruction types. -use nvisy_ontology::math::TimeSpan; +use nvisy_ontology::primitive::TimeSpan; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; diff --git a/crates/nvisy-codec/src/transform/image/instruction.rs b/crates/nvisy-codec/src/transform/image/instruction.rs index 8aacc15d..b15ba5f0 100644 --- a/crates/nvisy-codec/src/transform/image/instruction.rs +++ b/crates/nvisy-codec/src/transform/image/instruction.rs @@ -1,6 +1,6 @@ //! Image redaction instruction types. -use nvisy_ontology::math::{BoundingBox, Color}; +use nvisy_ontology::primitive::{BoundingBox, Color}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; diff --git a/crates/nvisy-codec/src/transform/image/ops.rs b/crates/nvisy-codec/src/transform/image/ops.rs index af7c416f..7c5b1a07 100644 --- a/crates/nvisy-codec/src/transform/image/ops.rs +++ b/crates/nvisy-codec/src/transform/image/ops.rs @@ -6,7 +6,7 @@ use image::DynamicImage; use image::imageops::FilterType; use imageproc::filter::gaussian_blur_f32; -use nvisy_ontology::math::{BoundingBoxPixel, Color}; +use nvisy_ontology::primitive::{BoundingBoxPixel, Color}; /// Mutating image-transform operations on individual bounding-box regions. pub trait ImageOps { diff --git a/crates/nvisy-codec/src/transform/image/transform.rs b/crates/nvisy-codec/src/transform/image/transform.rs index e423b16f..fb0c2f4d 100644 --- a/crates/nvisy-codec/src/transform/image/transform.rs +++ b/crates/nvisy-codec/src/transform/image/transform.rs @@ -1,5 +1,7 @@ //! [`ImageTransform`] async trait and blanket implementation. +use std::iter; + use futures::StreamExt; use image::DynamicImage; use nvisy_core::Error; @@ -81,7 +83,7 @@ impl ImageTransform for H { } } - self.edit_images(SpanStream::new(futures::stream::iter(std::iter::once( + self.edit_images(SpanStream::new(futures::stream::iter(iter::once( Span::new(span.id, ImageData::from(img)), )))) .await?; diff --git a/crates/nvisy-codec/src/transform/tabular/transform.rs b/crates/nvisy-codec/src/transform/tabular/transform.rs index 6c0970f4..f58afdaa 100644 --- a/crates/nvisy-codec/src/transform/tabular/transform.rs +++ b/crates/nvisy-codec/src/transform/tabular/transform.rs @@ -9,6 +9,7 @@ //! replacements right-to-left, and writes results back via //! [`TextHandler::edit_text`]. +use std::cmp::Reverse; use std::collections::HashMap; use futures::StreamExt; @@ -109,7 +110,7 @@ impl TabularTransform for H { let content: &str = span.data.as_ref(); // Sort right-to-left so earlier byte offsets stay valid. - replacements.sort_by(|a, b| b.0.cmp(&a.0)); + replacements.sort_by_key(|r| Reverse(r.0)); // Check for overlapping ranges. for pair in replacements.windows(2) { diff --git a/crates/nvisy-codec/src/transform/text/transform.rs b/crates/nvisy-codec/src/transform/text/transform.rs index e9e63e5b..b3af3537 100644 --- a/crates/nvisy-codec/src/transform/text/transform.rs +++ b/crates/nvisy-codec/src/transform/text/transform.rs @@ -5,6 +5,7 @@ //! byte-offset replacements right-to-left, and writes the results back //! via [`TextHandler::edit_text`]. +use std::cmp::Reverse; use std::collections::HashMap; use futures::StreamExt; @@ -68,7 +69,7 @@ impl TextTransform for H { let content: &str = span.data.as_ref(); // Sort right-to-left so earlier byte offsets stay valid. - replacements.sort_by(|a, b| b.0.cmp(&a.0)); + replacements.sort_by_key(|r| Reverse(r.0)); // Check for overlapping ranges (sorted descending by start). for pair in replacements.windows(2) { diff --git a/crates/nvisy-core/src/content/content_data.rs b/crates/nvisy-core/src/content/content_data.rs index c4f77ad5..232e8255 100644 --- a/crates/nvisy-core/src/content/content_data.rs +++ b/crates/nvisy-core/src/content/content_data.rs @@ -5,7 +5,7 @@ //! attributes (MIME type, filename, arbitrary metadata) live on //! [`ContentMetadata`](super::ContentMetadata). -use std::fmt; +use std::{fmt, str}; use bytes::Bytes; use hipstr::HipStr; @@ -90,7 +90,7 @@ impl ContentData { /// return). #[must_use] pub fn is_likely_text(&self) -> bool { - let Ok(s) = std::str::from_utf8(&self.data) else { + let Ok(s) = str::from_utf8(&self.data) else { return false; }; s.chars() @@ -103,7 +103,7 @@ impl ContentData { /// /// Returns an error if the content data contains invalid UTF-8 sequences. pub fn as_str(&self) -> Result<&str> { - std::str::from_utf8(&self.data) + str::from_utf8(&self.data) .map_err(|e| Error::new(ErrorKind::Serialization, format!("Invalid UTF-8: {e}"))) } @@ -122,7 +122,7 @@ impl ContentData { /// /// Returns an error if the content is not valid UTF-8. pub fn as_hipstr(&self) -> Result> { - let s = std::str::from_utf8(&self.data) + let s = str::from_utf8(&self.data) .map_err(|e| Error::new(ErrorKind::Serialization, format!("Invalid UTF-8: {e}")))?; Ok(HipStr::from(s)) } @@ -267,7 +267,7 @@ mod tests { let content = ContentData::from("Hello, world!"); let hash = content.sha256().clone(); assert!(content.verify_sha256(&hash).is_ok()); - assert!(content.verify_sha256(&[0u8; 32]).is_err()); + assert!(content.verify_sha256([0u8; 32]).is_err()); } #[test] diff --git a/crates/nvisy-core/src/error.rs b/crates/nvisy-core/src/error.rs index 4d6ec164..6370748c 100644 --- a/crates/nvisy-core/src/error.rs +++ b/crates/nvisy-core/src/error.rs @@ -3,6 +3,8 @@ //! All crates in the nvisy workspace use [`Error`] as their primary error //! type and [`ErrorKind`] to classify failures. +use std::{error, io, result}; + use derive_more::Display; /// Classification of error kinds. @@ -49,7 +51,7 @@ pub struct Error { pub retryable: bool, /// The underlying cause, if any. #[source] - pub source: Option>, + pub source: Option>, } impl Error { @@ -65,7 +67,7 @@ impl Error { } /// Attach an underlying cause to this error. - pub fn with_source(mut self, source: impl std::error::Error + Send + Sync + 'static) -> Self { + pub fn with_source(mut self, source: impl error::Error + Send + Sync + 'static) -> Self { self.source = Some(Box::new(source)); self } @@ -130,8 +132,8 @@ impl Error { } } -impl From for Error { - fn from(err: std::io::Error) -> Self { +impl From for Error { + fn from(err: io::Error) -> Self { Self::new(ErrorKind::Internal, err.to_string()).with_source(err) } } @@ -149,4 +151,4 @@ impl From for Error { } /// Convenience type alias for results using the Nvisy error type. -pub type Result = std::result::Result; +pub type Result = result::Result; diff --git a/crates/nvisy-engine/src/operation/deduplication/conflict.rs b/crates/nvisy-engine/src/operation/deduplication/conflict.rs index 3fab7d2d..8e10e979 100644 --- a/crates/nvisy-engine/src/operation/deduplication/conflict.rs +++ b/crates/nvisy-engine/src/operation/deduplication/conflict.rs @@ -5,6 +5,8 @@ //! module resolves the conflict by keeping only the winner based on //! the configured [`ConflictResolution`] strategy. +use std::cmp::Ordering; + use nvisy_ontology::entity::{Entities, Entity, Overlap}; use nvisy_ontology::workflow::ConflictResolution; @@ -91,10 +93,7 @@ impl ConflictResolutionExt for ConflictResolution { (None, None) => a.confidence >= b.confidence, }, Self::LongestSpan => { - a.location - .span_cmp(&b.location) - .unwrap_or(std::cmp::Ordering::Equal) - != std::cmp::Ordering::Less + a.location.span_cmp(&b.location).unwrap_or(Ordering::Equal) != Ordering::Less } _ => true, } diff --git a/crates/nvisy-engine/src/operation/deduplication/grouping.rs b/crates/nvisy-engine/src/operation/deduplication/grouping.rs index 95a14c05..7a538805 100644 --- a/crates/nvisy-engine/src/operation/deduplication/grouping.rs +++ b/crates/nvisy-engine/src/operation/deduplication/grouping.rs @@ -12,6 +12,7 @@ //! containment across different hash keys. use std::collections::{HashMap, HashSet}; +use std::mem; use nvisy_ontology::entity::{Entities, Entity, EntityKind, Overlap}; use nvisy_ontology::workflow::GroupingCriteria; @@ -152,7 +153,7 @@ impl GroupEntities for Entities { .any(|b| a.location.overlaps(&b.location)) }); if location_ok && any_value_match { - let donor = std::mem::take(&mut groups[indices[j]]); + let donor = mem::take(&mut groups[indices[j]]); groups[indices[i]].extend(donor); merged_into.insert(indices[j]); total_merges += 1; diff --git a/crates/nvisy-engine/src/operation/deduplication/mod.rs b/crates/nvisy-engine/src/operation/deduplication/mod.rs index 9960b109..c35f48a1 100644 --- a/crates/nvisy-engine/src/operation/deduplication/mod.rs +++ b/crates/nvisy-engine/src/operation/deduplication/mod.rs @@ -27,6 +27,8 @@ mod grouping; pub(crate) mod span_size; mod strategy; +use std::mem; + use nvisy_core::Result; use nvisy_ontology::entity::Entities; use nvisy_ontology::workflow::{ @@ -124,7 +126,7 @@ impl Operation for DeduplicationOp { entities = envelope.audit.entities.len(), "running deduplication", ); - let entities = std::mem::take(&mut envelope.audit.entities); + let entities = mem::take(&mut envelope.audit.entities); envelope.audit.entities = self.deduplicate(entities, &envelope.document).await; } Ok(()) @@ -401,13 +403,13 @@ mod tests { .with_confidence(0.7) .test_build(); e1.language = None; - e2.language = Some("en".into()); + e2.language = Some("en".parse().unwrap()); let entities: Entities = vec![e1, e2].into(); let result = MaxConfidence .fuse(entities, GroupingCriteria::default(), &doc) .await; - assert_eq!(result[0].language.as_deref(), Some("en")); + assert_eq!(result[0].language.as_ref().map(|t| t.as_str()), Some("en")); } #[tokio::test] diff --git a/crates/nvisy-engine/src/operation/deduplication/span_size.rs b/crates/nvisy-engine/src/operation/deduplication/span_size.rs index 6ce2e42f..deb133a7 100644 --- a/crates/nvisy-engine/src/operation/deduplication/span_size.rs +++ b/crates/nvisy-engine/src/operation/deduplication/span_size.rs @@ -58,8 +58,8 @@ impl SpanSize for Location { #[cfg(test)] mod tests { - use nvisy_ontology::entity::{AudioLocation, ImageLocation, TabularLocation, TextLocation}; - use nvisy_ontology::math::{BoundingBox, TimeSpan}; + use nvisy_ontology::entity::{AudioLocation, ImageLocation, TextLocation}; + use nvisy_ontology::primitive::{BoundingBox, TimeSpan}; use super::*; diff --git a/crates/nvisy-engine/src/operation/deduplication/strategy.rs b/crates/nvisy-engine/src/operation/deduplication/strategy.rs index 0d65029b..c50d29a9 100644 --- a/crates/nvisy-engine/src/operation/deduplication/strategy.rs +++ b/crates/nvisy-engine/src/operation/deduplication/strategy.rs @@ -4,6 +4,7 @@ //! field merging. The strategy determines *how* confidences are combined; //! the grouping module determines *which* entities are candidates. +use std::cmp::Ordering; use std::collections::HashSet; use nvisy_ontology::entity::{Entities, Entity, RefinementMethod}; @@ -85,7 +86,7 @@ impl DeduplicationStrategyExt for DeduplicationStrategy { group.sort_by(|a, b| { b.confidence .partial_cmp(&a.confidence) - .unwrap_or(std::cmp::Ordering::Equal) + .unwrap_or(Ordering::Equal) }); let mut result = group.remove(0); let rest = group; @@ -97,8 +98,8 @@ impl DeduplicationStrategyExt for DeduplicationStrategy { for e in &rest { if e.location .span_cmp(&result.location) - .unwrap_or(std::cmp::Ordering::Less) - == std::cmp::Ordering::Greater + .unwrap_or(Ordering::Less) + == Ordering::Greater { result.location.clone_from(&e.location); } diff --git a/crates/nvisy-engine/src/operation/detection/pattern_recognition.rs b/crates/nvisy-engine/src/operation/detection/pattern_recognition.rs index 6c0788eb..ab78fa06 100644 --- a/crates/nvisy-engine/src/operation/detection/pattern_recognition.rs +++ b/crates/nvisy-engine/src/operation/detection/pattern_recognition.rs @@ -6,6 +6,8 @@ //! //! [`EntityRecognitionOp`]: crate::operation::EntityRecognitionOp +use std::ops::Deref; + use nvisy_codec::Span; use nvisy_codec::handler::TextData; use nvisy_core::Result; @@ -23,7 +25,7 @@ enum EngineRef { Owned(nvisy_pattern::PatternEngine), } -impl std::ops::Deref for EngineRef { +impl Deref for EngineRef { type Target = nvisy_pattern::PatternEngine; fn deref(&self) -> &Self::Target { diff --git a/crates/nvisy-engine/src/operation/envelope/document.rs b/crates/nvisy-engine/src/operation/envelope/document.rs index 5235878d..b2e38351 100644 --- a/crates/nvisy-engine/src/operation/envelope/document.rs +++ b/crates/nvisy-engine/src/operation/envelope/document.rs @@ -5,6 +5,8 @@ //! (MIME type, filename) and [`ContentArtifacts`] (intermediate //! processing data like OCR results or transcriptions). +use std::fmt; + use nvisy_codec::handler::{AudioData, ImageData, TextData}; use nvisy_codec::transform::{AudioRedaction, ImageRedaction, TabularRedaction, TextRedaction}; use nvisy_codec::{ContentHandle, Span, SpanStream}; @@ -146,7 +148,7 @@ impl Document { .artifacts .as_audio() .and_then(|a| a.transcription.as_ref()) - .map(|t| t.text.clone()), + .map(|t| t.text()), // Image OCR results are multi-region; location-specific // lookup is not yet implemented. _ => None, @@ -192,8 +194,8 @@ impl Document { } } -impl std::fmt::Debug for Document { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +impl fmt::Debug for Document { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Document") .field("type", &self.document_type()) .field("source", &self.source()) diff --git a/crates/nvisy-engine/src/operation/envelope/mod.rs b/crates/nvisy-engine/src/operation/envelope/mod.rs index 955a8843..51e44afe 100644 --- a/crates/nvisy-engine/src/operation/envelope/mod.rs +++ b/crates/nvisy-engine/src/operation/envelope/mod.rs @@ -21,6 +21,7 @@ //! provider) is available via the [`shared`](DocumentEnvelope::shared) //! field. +use std::fmt; use std::sync::Arc; use nvisy_codec::ContentHandle; @@ -115,8 +116,8 @@ impl DocumentEnvelope { } } -impl std::fmt::Debug for DocumentEnvelope { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +impl fmt::Debug for DocumentEnvelope { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("DocumentEnvelope") .field("document_type", &self.document.document_type()) .field("source", &self.document.source()) diff --git a/crates/nvisy-engine/src/operation/envelope/shared.rs b/crates/nvisy-engine/src/operation/envelope/shared.rs index b8ad9669..f9e8d6ec 100644 --- a/crates/nvisy-engine/src/operation/envelope/shared.rs +++ b/crates/nvisy-engine/src/operation/envelope/shared.rs @@ -4,6 +4,7 @@ //! that every envelope and operation can cheaply access the same actor //! identity, policies, and context cache. +use std::fmt; use std::sync::Arc; use nvisy_ontology::policy::{Policies, Policy}; @@ -61,8 +62,8 @@ impl SharedData { } } -impl std::fmt::Debug for SharedData { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +impl fmt::Debug for SharedData { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("SharedData") .field("run_id", &self.run_id) .field("actor_id", &self.actor_id) diff --git a/crates/nvisy-engine/src/operation/extraction/speech.rs b/crates/nvisy-engine/src/operation/extraction/speech.rs index 2f8bad07..c067269b 100644 --- a/crates/nvisy-engine/src/operation/extraction/speech.rs +++ b/crates/nvisy-engine/src/operation/extraction/speech.rs @@ -5,7 +5,8 @@ use nvisy_codec::ContentHandle; use nvisy_codec::handler::{BoxedTextHandler, Handler, TxtHandler}; use nvisy_core::{Error, ErrorKind, Result}; -use nvisy_ontology::artifacts::Transcription; +use nvisy_ontology::artifacts::{TranscriptSegment, Transcription}; +use nvisy_ontology::primitive::TimeSpan; use nvisy_ontology::workflow::AudialExtraction as AudialExtractionCfg; use nvisy_provider::audio::stt::{SttConfig, SttService}; use nvisy_provider::http::HttpClient; @@ -73,9 +74,16 @@ impl Operation for AudialExtractionOp { tracing::debug!(target: TARGET, "transcription returned empty text"); } else { // Store transcription in artifacts. + // TODO: populate real segment timestamps once the STT provider + // returns verbose_json with timestamp_granularities. if let Some(audio) = envelope.document.artifacts.as_audio_mut() { audio.transcription = Some(Transcription { - text: stt_result.text.clone(), + segments: vec![TranscriptSegment { + text: stt_result.text.clone(), + time_span: TimeSpan::new(0, 0), + speaker_id: None, + confidence: None, + }], language: None, }); } diff --git a/crates/nvisy-engine/src/operation/extraction/vision.rs b/crates/nvisy-engine/src/operation/extraction/vision.rs index 005669c4..cc7a415f 100644 --- a/crates/nvisy-engine/src/operation/extraction/vision.rs +++ b/crates/nvisy-engine/src/operation/extraction/vision.rs @@ -143,7 +143,14 @@ impl Operation for VisualExtractionOp { "running OCR extraction", ); - let _ocr_output = self.extract(&image_spans).await?; + let ocr_output = self.extract(&image_spans).await?; + + // Store OCR results in image artifacts. + if let Some(image_artifacts) = envelope.document.artifacts.as_image_mut() { + for output in &ocr_output { + image_artifacts.ocr_pages.extend(output.pages.clone()); + } + } if self.agent.has_verifier() && !envelope.audit.entities.is_empty() { let verify_spans = envelope.document.collect_image_spans().await; diff --git a/crates/nvisy-engine/src/operation/import_file.rs b/crates/nvisy-engine/src/operation/import_file.rs index 88253d99..4fd7775e 100644 --- a/crates/nvisy-engine/src/operation/import_file.rs +++ b/crates/nvisy-engine/src/operation/import_file.rs @@ -12,6 +12,7 @@ //! //! [`ContentHandle`]: nvisy_codec::ContentHandle +use std::mem; use std::sync::Arc; use nvisy_codec::ContentHandle; @@ -87,7 +88,7 @@ impl ImportFileOp { // Move persisted annotations from metadata to the envelope // and apply inclusions as entities. - let annotations = std::mem::take(&mut metadata.annotations); + let annotations = mem::take(&mut metadata.annotations); let mut envelope = DocumentEnvelope::new(doc, metadata, Arc::clone(shared)); if !annotations.is_empty() { annotations.apply_inclusions(&mut envelope.audit.entities); diff --git a/crates/nvisy-engine/src/operation/mod.rs b/crates/nvisy-engine/src/operation/mod.rs index 4448dfb8..e1cf1026 100644 --- a/crates/nvisy-engine/src/operation/mod.rs +++ b/crates/nvisy-engine/src/operation/mod.rs @@ -5,10 +5,10 @@ //! //! # Submodules //! -//! - [`extraction`]: visual (OCR), audial (STT), and text extraction. -//! - [`detection`]: NER and pattern-based entity detection. -//! - [`deduplication`]: entity deduplication and confidence scoring. -//! - [`redaction`]: policy evaluation and content redaction. +//! - `extraction`: visual (OCR), audial (STT), and text extraction. +//! - `detection`: NER and pattern-based entity detection. +//! - `deduplication`: entity deduplication and confidence scoring. +//! - `redaction`: policy evaluation and content redaction. mod deduplication; mod detection; diff --git a/crates/nvisy-engine/src/operation/redaction/apply.rs b/crates/nvisy-engine/src/operation/redaction/apply.rs index 28078547..fd17f51d 100644 --- a/crates/nvisy-engine/src/operation/redaction/apply.rs +++ b/crates/nvisy-engine/src/operation/redaction/apply.rs @@ -306,9 +306,7 @@ impl<'a> RedactionApplicator<'a> { mod tests { use nvisy_codec::ContentHandle; use nvisy_core::content::{Content, ContentData, ContentMetadata, ContentSource}; - use nvisy_ontology::entity::{ - Entities, Entity, EntityCategory, EntityKind, Location, RecognitionMethod, TextLocation, - }; + use nvisy_ontology::entity::{Entities, Entity, EntityKind}; use nvisy_ontology::policy::ImageStrategy; use nvisy_ontology::provenance::AuditEntry; diff --git a/crates/nvisy-engine/src/operation/redaction/evaluate.rs b/crates/nvisy-engine/src/operation/redaction/evaluate.rs index d4a90084..00d1b5c8 100644 --- a/crates/nvisy-engine/src/operation/redaction/evaluate.rs +++ b/crates/nvisy-engine/src/operation/redaction/evaluate.rs @@ -204,7 +204,7 @@ impl ConditionExt for Condition { #[cfg(test)] mod tests { - use nvisy_ontology::entity::{Entity, EntityCategory, EntityKind, Location, RecognitionMethod}; + use nvisy_ontology::entity::Entity; use nvisy_ontology::policy::{Strategy, TextStrategy}; use super::*; diff --git a/crates/nvisy-engine/src/operation/redaction/mod.rs b/crates/nvisy-engine/src/operation/redaction/mod.rs index f5cbc1aa..7d23b7fa 100644 --- a/crates/nvisy-engine/src/operation/redaction/mod.rs +++ b/crates/nvisy-engine/src/operation/redaction/mod.rs @@ -7,6 +7,8 @@ //! 2. **Apply**: build per-modality codec instructions (text, image, //! audio) from decisions and apply them to the document, writing //! replacement values into audit records (in [`apply`]). +//! +//! [`AuditEntry`]: nvisy_ontology::provenance::AuditEntry mod apply; mod evaluate; diff --git a/crates/nvisy-engine/src/pipeline/config/validate.rs b/crates/nvisy-engine/src/pipeline/config/validate.rs index 7818d5eb..f9bb9d51 100644 --- a/crates/nvisy-engine/src/pipeline/config/validate.rs +++ b/crates/nvisy-engine/src/pipeline/config/validate.rs @@ -1,5 +1,7 @@ //! Post-deserialization validation and environment variable resolution. +use std::env; + use nvisy_core::Error; use validator::Validate; @@ -123,7 +125,7 @@ fn resolve_ocr_provider_key( #[allow(dead_code)] pub(super) fn fill_key_from_env(key: &mut String, env_var: &str) { if key.is_empty() - && let Ok(val) = std::env::var(env_var) + && let Ok(val) = env::var(env_var) && !val.is_empty() { *key = val; diff --git a/crates/nvisy-engine/src/pipeline/default.rs b/crates/nvisy-engine/src/pipeline/default.rs index c3dc0231..edacf38e 100644 --- a/crates/nvisy-engine/src/pipeline/default.rs +++ b/crates/nvisy-engine/src/pipeline/default.rs @@ -1,4 +1,4 @@ -//! [`Engine`]: the main entry point for running redaction pipelines. +//! [`Engine`] — the main entry point for running redaction pipelines. //! //! The engine is a thin facade over the pipeline subsystem. It owns //! shared infrastructure (registry, HTTP client, run state) and @@ -8,6 +8,7 @@ use std::path::Path; use std::sync::Arc; +use std::{fmt, mem}; use nvisy_core::Error; use nvisy_ontology::provenance::Audit; @@ -87,21 +88,20 @@ pub(super) struct EngineInner { /// The redaction pipeline engine. /// /// Thin facade over shared infrastructure. Actual pipeline execution -/// is delegated to [`Pipeline`] (one per run). All state lives in +/// is delegated to `Pipeline` (one per run). All state lives in /// `Arc` and is shared across clones. /// /// Builder methods ([`with_key_provider`]) require exclusive access /// via `Arc::get_mut` and must be called before the engine is cloned. /// -/// [`Pipeline`]: super::run::Pipeline /// [`with_key_provider`]: Engine::with_key_provider #[derive(Clone)] pub struct Engine { pub(super) inner: Arc, } -impl std::fmt::Debug for Engine { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +impl fmt::Debug for Engine { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Engine") .field("runtime_config", &self.inner.runtime_config) .field("http_client", &self.inner.http_client) @@ -150,6 +150,7 @@ impl Engine { /// /// Panics if the temp directory or registry cannot be created. /// + /// [`tempfile`]: https://docs.rs/tempfile /// [`TempDir`]: tempfile::TempDir #[cfg(any(test, feature = "test-utils"))] pub fn temp() -> (Self, tempfile::TempDir) { @@ -333,7 +334,7 @@ impl Engine { /// Call during graceful shutdown to ensure in-flight runs finish /// and persist their results before the process exits. pub async fn shutdown(&self) { - let tasks = std::mem::take(&mut *self.inner.background_tasks.lock().await); + let tasks = mem::take(&mut *self.inner.background_tasks.lock().await); tasks.join_all().await; } } diff --git a/crates/nvisy-engine/src/pipeline/mod.rs b/crates/nvisy-engine/src/pipeline/mod.rs index 028c053b..b8ee9ad4 100644 --- a/crates/nvisy-engine/src/pipeline/mod.rs +++ b/crates/nvisy-engine/src/pipeline/mod.rs @@ -2,19 +2,17 @@ //! //! The pipeline processes content through a typed execution plan //! derived from a user-submitted [`Graph`]. The [`Engine`] is a thin -//! facade that delegates actual execution to [`Pipeline`] (one per run). +//! facade that delegates actual execution to `Pipeline` (one per run). //! //! # Submodules //! -//! - [`config`]: [`RuntimeConfig`] and per-subsystem sections. -//! - [`plan`]: compiles a [`Graph`] into a typed [`ExecutionPlan`]. -//! - [`run`]: per-run lifecycle ([`Pipeline`]). -//! - [`orchestrator`]: concurrent document processing through the plan. -//! - [`runs`]: in-memory run lifecycle tracking. +//! - `config`: [`RuntimeConfig`] and per-subsystem sections. +//! - `plan`: compiles a [`Graph`] into a typed `ExecutionPlan`. +//! - `run`: per-run lifecycle (`Pipeline`). +//! - `orchestrator`: concurrent document processing through the plan. +//! - `runs`: in-memory run lifecycle tracking. //! //! [`Graph`]: nvisy_ontology::workflow::Graph -//! [`Pipeline`]: run::Pipeline -//! [`ExecutionPlan`]: plan::ExecutionPlan mod config; mod default; diff --git a/crates/nvisy-engine/src/pipeline/orchestrator.rs b/crates/nvisy-engine/src/pipeline/orchestrator.rs index ccc2413c..9b1b39f6 100644 --- a/crates/nvisy-engine/src/pipeline/orchestrator.rs +++ b/crates/nvisy-engine/src/pipeline/orchestrator.rs @@ -9,6 +9,7 @@ //! phases sequentially: extraction → detection → deduplication → redaction → //! validation. +use std::future::Future; use std::sync::Arc; use nvisy_core::Error; @@ -236,7 +237,7 @@ impl DocumentPipeline { /// Wrap a phase future with an optional timeout from the phase policy. async fn run_phase(&self, policy: &PhasePolicy, future: F) -> Result<(), Error> where - F: std::future::Future> + Send, + F: Future> + Send, { match &policy.timeout { Some(tp) => tp.with_timeout(future).await, diff --git a/crates/nvisy-engine/src/pipeline/run.rs b/crates/nvisy-engine/src/pipeline/run.rs index e9ff31f8..f6776a4e 100644 --- a/crates/nvisy-engine/src/pipeline/run.rs +++ b/crates/nvisy-engine/src/pipeline/run.rs @@ -10,6 +10,7 @@ use std::collections::HashMap; use std::sync::Arc; +use std::time::Duration; use nvisy_core::Error; use nvisy_ontology::policy::{Policies, Retention, RetentionPolicy, RetentionScope}; @@ -155,7 +156,7 @@ impl Pipeline { .await; let mut policies = Policies::default(); for policy in cached_policies { - policies.push(std::sync::Arc::unwrap_or_clone(policy)); + policies.push(Arc::unwrap_or_clone(policy)); } let retention_rules = policies @@ -196,11 +197,7 @@ impl Pipeline { let limits = self.base_config.effective_limits(); let orchestrator = Orchestrator::new(ctx); let run_output = if let Some(ms) = limits.run_timeout_ms { - match tokio::time::timeout( - std::time::Duration::from_millis(ms), - orchestrator.run(&compiled), - ) - .await + match tokio::time::timeout(Duration::from_millis(ms), orchestrator.run(&compiled)).await { Ok(Ok(output)) => output, Ok(Err(e)) => { @@ -334,6 +331,8 @@ impl Pipeline { /// /// Mirrors [`acquire_resources`] — contexts are loaded into the /// cache before execution and saved back here after completion. + /// + /// [`acquire_resources`]: Self::acquire_resources async fn release_resources(&self, actor_id: Uuid, save_context_ids: &[Uuid]) { let registry = &self.registry; for &id in save_context_ids { @@ -349,7 +348,7 @@ impl Pipeline { } }; if let Err(e) = registry - .register_context(actor_id, std::sync::Arc::unwrap_or_clone(context)) + .register_context(actor_id, Arc::unwrap_or_clone(context)) .await { tracing::warn!( diff --git a/crates/nvisy-engine/src/pipeline/runs/mod.rs b/crates/nvisy-engine/src/pipeline/runs/mod.rs index 9d3abba4..66d829c5 100644 --- a/crates/nvisy-engine/src/pipeline/runs/mod.rs +++ b/crates/nvisy-engine/src/pipeline/runs/mod.rs @@ -78,6 +78,8 @@ pub struct NodeSnapshot { /// `Succeeded` and `PartialFailure` include audit trails (populated /// from the registry by [`Engine::get_run`]). `Failed` carries an /// optional error message. `Pending` and `Running` have no extra data. +/// +/// [`Engine::get_run`]: crate::pipeline::Engine::get_run #[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] #[serde(tag = "status", rename_all = "snake_case")] pub enum RunOutcome { diff --git a/crates/nvisy-engine/src/registry/cache.rs b/crates/nvisy-engine/src/registry/cache.rs index eb91ff8c..51e1079f 100644 --- a/crates/nvisy-engine/src/registry/cache.rs +++ b/crates/nvisy-engine/src/registry/cache.rs @@ -13,9 +13,9 @@ //! [`Registry`]: super::Registry use std::collections::HashMap; -use std::fmt; use std::future::Future; use std::sync::Arc; +use std::{fmt, mem}; use tokio::sync::RwLock; use uuid::Uuid; @@ -221,7 +221,7 @@ impl Drop for ResourceGuard { return; } let cache = self.cache.clone(); - let ids = std::mem::take(&mut self.ids); + let ids = mem::take(&mut self.ids); tokio::spawn(async move { cache.release(&ids).await; }); diff --git a/crates/nvisy-engine/src/registry/fjall_ext.rs b/crates/nvisy-engine/src/registry/fjall_ext.rs index 7f711481..1600e10d 100644 --- a/crates/nvisy-engine/src/registry/fjall_ext.rs +++ b/crates/nvisy-engine/src/registry/fjall_ext.rs @@ -4,6 +4,7 @@ //! operations with [`CompositeKey`] support and consistent error //! mapping to [`nvisy_core::Error`]. +use std::error; use std::path::Path; use fjall::{Database, Keyspace, KeyspaceCreateOptions, KvSeparationOptions}; @@ -144,7 +145,7 @@ pub(crate) fn not_found(kind: &str, actor_id: Uuid, resource_id: Uuid) -> Error .with_component(COMPONENT) } -fn fjall_err(msg: impl Into, err: impl std::error::Error + Send + Sync + 'static) -> Error { +fn fjall_err(msg: impl Into, err: impl error::Error + Send + Sync + 'static) -> Error { Error::new(ErrorKind::Internal, msg) .with_component(COMPONENT) .with_source(err) diff --git a/crates/nvisy-engine/src/registry/mod.rs b/crates/nvisy-engine/src/registry/mod.rs index b658629f..b3b7d593 100644 --- a/crates/nvisy-engine/src/registry/mod.rs +++ b/crates/nvisy-engine/src/registry/mod.rs @@ -2,12 +2,10 @@ //! //! The [`Registry`] stores content, contexts, policies, and audit //! trails in a fjall database. All entries are actor-scoped via -//! [`CompositeKey`]. +//! a composite key. //! //! [`ResourceCache`] provides generic ref-counted caching on top of //! the store, used for contexts and policies. -//! -//! [`CompositeKey`]: key::CompositeKey mod cache; mod content; diff --git a/crates/nvisy-engine/src/registry/store.rs b/crates/nvisy-engine/src/registry/store.rs index d575806c..740de239 100644 --- a/crates/nvisy-engine/src/registry/store.rs +++ b/crates/nvisy-engine/src/registry/store.rs @@ -1,5 +1,6 @@ //! [`Registry`]: actor-scoped content, context, and policy store. +use std::fmt; use std::path::{Path, PathBuf}; use std::sync::Arc; @@ -39,8 +40,8 @@ struct RegistryInner { policy_cache: ResourceCache, } -impl std::fmt::Debug for Registry { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +impl fmt::Debug for Registry { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Registry") .field("base_dir", &self.inner.base_dir) .finish_non_exhaustive() diff --git a/crates/nvisy-engine/src/utility/compression/mod.rs b/crates/nvisy-engine/src/utility/compression/mod.rs index ab7cdcdb..d81af7ca 100644 --- a/crates/nvisy-engine/src/utility/compression/mod.rs +++ b/crates/nvisy-engine/src/utility/compression/mod.rs @@ -1,13 +1,10 @@ //! Content compression and decompression. //! -//! Used as pre/post-processing steps within [`ImportFileOp`] and -//! [`ExportFileOp`] operations, not as standalone pipeline operations. +//! Used as pre/post-processing steps within `ImportFileOp` and +//! `ExportFileOp` operations, not as standalone pipeline operations. //! //! Gzip and Zstd are recognized but not yet implemented — selecting //! them returns a runtime error. -//! -//! [`ImportFileOp`]: crate::operation::ImportFileOp -//! [`ExportFileOp`]: crate::operation::ExportFileOp use bytes::Bytes; use nvisy_core::{Error, Result}; diff --git a/crates/nvisy-engine/src/utility/encryption/provider.rs b/crates/nvisy-engine/src/utility/encryption/provider.rs index 1147cafc..ff8395b7 100644 --- a/crates/nvisy-engine/src/utility/encryption/provider.rs +++ b/crates/nvisy-engine/src/utility/encryption/provider.rs @@ -1,6 +1,7 @@ //! Key provider abstraction for encryption key resolution. use std::collections::HashMap; +use std::fmt; use std::sync::Arc; use bytes::Bytes; @@ -35,8 +36,8 @@ impl Default for SharedKeyProvider { } } -impl std::fmt::Debug for SharedKeyProvider { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +impl fmt::Debug for SharedKeyProvider { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_tuple("SharedKeyProvider").finish() } } diff --git a/crates/nvisy-engine/src/utility/encryption/wire.rs b/crates/nvisy-engine/src/utility/encryption/wire.rs index db360f77..ed0afd7e 100644 --- a/crates/nvisy-engine/src/utility/encryption/wire.rs +++ b/crates/nvisy-engine/src/utility/encryption/wire.rs @@ -1,5 +1,7 @@ //! Self-describing wire envelope for encrypted content. +use std::str; + use bytes::Bytes; use nvisy_core::content::ContentSource; use nvisy_core::{Error, Result}; @@ -96,7 +98,7 @@ impl WireEnvelope<'_> { )); } - let key_id = std::str::from_utf8(&data[8..key_id_end]).map_err(|e| { + let key_id = str::from_utf8(&data[8..key_id_end]).map_err(|e| { Error::validation(format!("invalid key_id UTF-8: {e}"), "WireEnvelope::parse") })?; diff --git a/crates/nvisy-engine/tests/fixtures/mod.rs b/crates/nvisy-engine/tests/fixtures/mod.rs index 28276977..42709978 100644 --- a/crates/nvisy-engine/tests/fixtures/mod.rs +++ b/crates/nvisy-engine/tests/fixtures/mod.rs @@ -1,4 +1,8 @@ //! Shared test helpers and factories for integration tests. +//! +//! Each integration test compiles this module separately, so some helpers +//! may appear unused from any given test file's perspective. +#![allow(dead_code)] use nvisy_core::content::{Content, ContentData, ContentMetadata, ContentSource}; use nvisy_engine::pipeline::{Engine, EngineInput}; diff --git a/crates/nvisy-ontology/Cargo.toml b/crates/nvisy-ontology/Cargo.toml index f4e4de8e..59a6205a 100644 --- a/crates/nvisy-ontology/Cargo.toml +++ b/crates/nvisy-ontology/Cargo.toml @@ -35,7 +35,7 @@ schemars = { workspace = true, features = [] } # Derive macros and error handling thiserror = { workspace = true, features = [] } derive_builder = { workspace = true, features = [] } -derive_more = { workspace = true, features = ["deref", "deref_mut", "display", "from", "into", "into_iterator"] } +derive_more = { workspace = true, features = ["deref", "deref_mut", "display", "from", "from_str", "into", "into_iterator"] } strum = { workspace = true, features = [] } validator = { workspace = true, features = [] } @@ -43,3 +43,4 @@ validator = { workspace = true, features = [] } uuid = { workspace = true, features = [] } jiff = { workspace = true, features = [] } semver = { workspace = true, features = [] } +oxilangtag = { workspace = true, features = [] } diff --git a/crates/nvisy-ontology/src/artifacts/audio.rs b/crates/nvisy-ontology/src/artifacts/audio.rs index a0dbdc11..84029cd9 100644 --- a/crates/nvisy-ontology/src/artifacts/audio.rs +++ b/crates/nvisy-ontology/src/artifacts/audio.rs @@ -3,16 +3,57 @@ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; +use crate::primitive::{LanguageTag, TimeSpan}; + +/// A single timestamped segment within a transcription. +#[derive(Debug, Clone, PartialEq)] +#[derive(Serialize, Deserialize, JsonSchema)] +#[serde(rename_all = "camelCase")] +pub struct TranscriptSegment { + /// The transcribed text for this segment. + pub text: String, + /// Time interval of this segment within the audio stream. + pub time_span: TimeSpan, + /// Speaker identifier from diarization, if available. + #[serde(skip_serializing_if = "Option::is_none")] + pub speaker_id: Option, + /// Confidence score for this segment in the range `[0.0, 1.0]`. + #[serde(skip_serializing_if = "Option::is_none")] + pub confidence: Option, +} + /// Full transcript produced by speech-to-text extraction. #[derive(Debug, Clone, PartialEq)] #[derive(Serialize, Deserialize, JsonSchema)] #[serde(rename_all = "camelCase")] pub struct Transcription { - /// The transcribed text. - pub text: String, + /// Timestamped segments composing the full transcript. + pub segments: Vec, /// BCP-47 language tag of the detected language, if available. #[serde(skip_serializing_if = "Option::is_none")] - pub language: Option, + #[schemars(with = "Option")] + pub language: Option, +} + +impl Transcription { + /// Returns the full transcript text by joining all segments. + pub fn text(&self) -> String { + self.segments + .iter() + .map(|s| s.text.as_str()) + .collect::>() + .join(" ") + } + + /// Returns the total time span covering all segments, or `None` if empty. + pub fn time_span(&self) -> Option { + let first = self.segments.first()?; + let last = self.segments.last()?; + Some(TimeSpan::new( + first.time_span.start_us, + last.time_span.end_us, + )) + } } /// Artifacts produced during processing of audio content. @@ -24,3 +65,68 @@ pub struct AudioArtifacts { #[serde(skip_serializing_if = "Option::is_none")] pub transcription: Option, } + +#[cfg(test)] +mod tests { + use super::*; + + fn sample_transcription() -> Transcription { + Transcription { + segments: vec![ + TranscriptSegment { + text: "Hello".to_owned(), + time_span: TimeSpan::from_secs(0.0, 1.0), + speaker_id: Some("speaker_1".to_owned()), + confidence: Some(0.95), + }, + TranscriptSegment { + text: "world".to_owned(), + time_span: TimeSpan::from_secs(1.0, 2.0), + speaker_id: Some("speaker_2".to_owned()), + confidence: Some(0.90), + }, + ], + language: Some("en".parse().unwrap()), + } + } + + #[test] + fn text_joins_segments() { + let t = sample_transcription(); + assert_eq!(t.text(), "Hello world"); + } + + #[test] + fn text_empty_segments() { + let t = Transcription { + segments: vec![], + language: None, + }; + assert_eq!(t.text(), ""); + } + + #[test] + fn time_span_covers_all_segments() { + let t = sample_transcription(); + let span = t.time_span().unwrap(); + assert_eq!(span.start_us, 0); + assert_eq!(span.end_us, 2_000_000); + } + + #[test] + fn time_span_empty() { + let t = Transcription { + segments: vec![], + language: None, + }; + assert!(t.time_span().is_none()); + } + + #[test] + fn serde_roundtrip() { + let t = sample_transcription(); + let json = serde_json::to_string(&t).unwrap(); + let back: Transcription = serde_json::from_str(&json).unwrap(); + assert_eq!(t, back); + } +} diff --git a/crates/nvisy-ontology/src/artifacts/image.rs b/crates/nvisy-ontology/src/artifacts/image.rs index 99631bfd..4c7fa294 100644 --- a/crates/nvisy-ontology/src/artifacts/image.rs +++ b/crates/nvisy-ontology/src/artifacts/image.rs @@ -3,7 +3,7 @@ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use crate::math::{BoundingBox, Polygon}; +use crate::primitive::{BoundingBox, Polygon}; /// A single word detected by OCR. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)] diff --git a/crates/nvisy-ontology/src/artifacts/mod.rs b/crates/nvisy-ontology/src/artifacts/mod.rs index 3661ff59..fba52aec 100644 --- a/crates/nvisy-ontology/src/artifacts/mod.rs +++ b/crates/nvisy-ontology/src/artifacts/mod.rs @@ -10,13 +10,14 @@ mod rich; mod tabular; mod text; +use derive_more::From; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -pub use self::audio::{AudioArtifacts, Transcription}; +pub use self::audio::{AudioArtifacts, TranscriptSegment, Transcription}; pub use self::image::{Block, BlockKind, ImageArtifacts, Line, Page, Word}; pub use self::rich::RichArtifacts; -pub use self::tabular::TabularArtifacts; +pub use self::tabular::{ColumnHeader, TabularArtifacts}; pub use self::text::TextArtifacts; /// Modality-specific processing artifacts. @@ -24,7 +25,7 @@ pub use self::text::TextArtifacts; /// Each variant matches a content modality and holds only the artifact /// types that make sense for that modality (e.g. transcription is only /// available on audio, OCR only on image/rich). -#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)] +#[derive(Debug, Clone, PartialEq, From, Serialize, Deserialize, JsonSchema)] #[serde(tag = "modality", rename_all = "snake_case")] pub enum ContentArtifacts { /// Artifacts from text content processing. @@ -98,4 +99,40 @@ impl ContentArtifacts { _ => None, } } + + /// Access text artifacts, if this is a text or rich variant. + pub fn as_text(&self) -> Option<&TextArtifacts> { + match self { + Self::Text(a) => Some(a), + Self::Rich(a) => Some(&a.text), + _ => None, + } + } + + /// Access text artifacts mutably, if this is a text or rich variant. + pub fn as_text_mut(&mut self) -> Option<&mut TextArtifacts> { + match self { + Self::Text(a) => Some(a), + Self::Rich(a) => Some(&mut a.text), + _ => None, + } + } + + /// Access tabular artifacts, if this is a tabular or rich variant. + pub fn as_tabular(&self) -> Option<&TabularArtifacts> { + match self { + Self::Tabular(a) => Some(a), + Self::Rich(a) => Some(&a.tabular), + _ => None, + } + } + + /// Access tabular artifacts mutably, if this is a tabular or rich variant. + pub fn as_tabular_mut(&mut self) -> Option<&mut TabularArtifacts> { + match self { + Self::Tabular(a) => Some(a), + Self::Rich(a) => Some(&mut a.tabular), + _ => None, + } + } } diff --git a/crates/nvisy-ontology/src/artifacts/rich.rs b/crates/nvisy-ontology/src/artifacts/rich.rs index aab9b62f..fc960ff5 100644 --- a/crates/nvisy-ontology/src/artifacts/rich.rs +++ b/crates/nvisy-ontology/src/artifacts/rich.rs @@ -1,15 +1,16 @@ -//! Rich-document artifacts (text + image combined). +//! Rich-document artifacts (text + image + tabular combined). use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use super::image::ImageArtifacts; +use super::tabular::TabularArtifacts; use super::text::TextArtifacts; /// Artifacts produced during processing of rich documents (PDF, DOCX). /// -/// Rich documents contain both text and image content, so their -/// artifacts compose both modality-specific artifact types. +/// Rich documents can contain text, images, and tables, so their +/// artifacts compose all three modality-specific artifact types. #[derive(Debug, Clone, Default, PartialEq)] #[derive(Serialize, Deserialize, JsonSchema)] #[serde(rename_all = "camelCase")] @@ -18,4 +19,6 @@ pub struct RichArtifacts { pub text: TextArtifacts, /// Image-modality artifacts (embedded images, OCR results). pub image: ImageArtifacts, + /// Tabular-modality artifacts (embedded tables). + pub tabular: TabularArtifacts, } diff --git a/crates/nvisy-ontology/src/artifacts/tabular.rs b/crates/nvisy-ontology/src/artifacts/tabular.rs index 7ebc654c..c6536f3c 100644 --- a/crates/nvisy-ontology/src/artifacts/tabular.rs +++ b/crates/nvisy-ontology/src/artifacts/tabular.rs @@ -3,8 +3,69 @@ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; +/// A column header in a tabular document. +/// +/// Only columns with detected headers get an entry, so this +/// representation naturally supports gaps (headerless columns). +#[derive(Debug, Clone, PartialEq)] +#[derive(Serialize, Deserialize, JsonSchema)] +#[serde(rename_all = "camelCase")] +pub struct ColumnHeader { + /// 0-based column index. + pub column_index: u32, + /// Header text. + pub text: String, +} + /// Artifacts produced during processing of tabular content. #[derive(Debug, Clone, Default, PartialEq)] #[derive(Serialize, Deserialize, JsonSchema)] #[serde(rename_all = "camelCase")] -pub struct TabularArtifacts {} +pub struct TabularArtifacts { + /// Number of data rows (excluding headers). + #[serde(skip_serializing_if = "Option::is_none")] + pub row_count: Option, + /// Number of columns. + #[serde(skip_serializing_if = "Option::is_none")] + pub column_count: Option, + /// Detected column headers, sparse — only columns with headers are present. + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub headers: Vec, +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn sparse_headers_skip_gaps() { + // Columns 0 and 3 have headers; columns 1 and 2 do not. + let artifacts = TabularArtifacts { + row_count: Some(100), + column_count: Some(4), + headers: vec![ + ColumnHeader { + column_index: 0, + text: "id".to_owned(), + }, + ColumnHeader { + column_index: 3, + text: "name".to_owned(), + }, + ], + }; + let json = serde_json::to_string(&artifacts).unwrap(); + let back: TabularArtifacts = serde_json::from_str(&json).unwrap(); + assert_eq!(back.headers.len(), 2); + assert_eq!(back.headers[0].column_index, 0); + assert_eq!(back.headers[1].column_index, 3); + } + + #[test] + fn default_is_empty() { + let artifacts = TabularArtifacts::default(); + let json = serde_json::to_string(&artifacts).unwrap(); + // All fields skipped — JSON should be empty object. + assert_eq!(json, "{}"); + } +} diff --git a/crates/nvisy-ontology/src/artifacts/text.rs b/crates/nvisy-ontology/src/artifacts/text.rs index 80b8f64c..60458d7b 100644 --- a/crates/nvisy-ontology/src/artifacts/text.rs +++ b/crates/nvisy-ontology/src/artifacts/text.rs @@ -3,8 +3,18 @@ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; +use crate::primitive::LanguageTag; + /// Artifacts produced during processing of text content. #[derive(Debug, Clone, Default, PartialEq)] #[derive(Serialize, Deserialize, JsonSchema)] #[serde(rename_all = "camelCase")] -pub struct TextArtifacts {} +pub struct TextArtifacts { + /// BCP-47 language tag of the detected language, if available. + #[serde(skip_serializing_if = "Option::is_none")] + #[schemars(with = "Option")] + pub language: Option, + /// Total character count of the text content. + #[serde(skip_serializing_if = "Option::is_none")] + pub char_count: Option, +} diff --git a/crates/nvisy-ontology/src/context/analytic/embedding.rs b/crates/nvisy-ontology/src/context/analytic/embedding.rs index 8d3a2765..16e3066b 100644 --- a/crates/nvisy-ontology/src/context/analytic/embedding.rs +++ b/crates/nvisy-ontology/src/context/analytic/embedding.rs @@ -24,14 +24,18 @@ pub enum DistanceMetric { #[serde(rename_all = "camelCase")] pub struct EmbeddingData { /// The embedding vector values. - pub vector: Vec, - /// Dimensionality of the vector. + pub vector: Vec, + /// Identifier of the model/algorithm that produced this embedding. #[serde(skip_serializing_if = "Option::is_none")] - pub dimensions: Option, - /// Identifier of the model that produced this embedding. - #[serde(skip_serializing_if = "Option::is_none")] - pub model: Option, + pub algorithm: Option, /// Distance metric to use for comparison. #[serde(skip_serializing_if = "Option::is_none")] pub distance_metric: Option, } + +impl EmbeddingData { + /// Dimensionality of the embedding vector. + pub fn dimensions(&self) -> usize { + self.vector.len() + } +} diff --git a/crates/nvisy-ontology/src/context/analytic/mod.rs b/crates/nvisy-ontology/src/context/analytic/mod.rs index 567454fa..2487456a 100644 --- a/crates/nvisy-ontology/src/context/analytic/mod.rs +++ b/crates/nvisy-ontology/src/context/analytic/mod.rs @@ -7,7 +7,7 @@ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; pub use self::embedding::{DistanceMetric, EmbeddingData}; -pub use self::pattern::PatternData; +pub use self::pattern::{GlobPattern, PatternData, PatternExpression, RegexPattern}; /// Analytic computation variants. #[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] diff --git a/crates/nvisy-ontology/src/context/analytic/pattern.rs b/crates/nvisy-ontology/src/context/analytic/pattern.rs index 90bf583e..3beb698b 100644 --- a/crates/nvisy-ontology/src/context/analytic/pattern.rs +++ b/crates/nvisy-ontology/src/context/analytic/pattern.rs @@ -1,23 +1,34 @@ //! Pattern reference data for regex/glob matching. +use derive_more::From; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -/// A pattern expression with its type. -#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] -#[serde(tag = "kind", rename_all = "snake_case")] +/// A regular expression pattern. +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] +#[serde(rename_all = "camelCase")] +pub struct RegexPattern { + /// The regex expression string. + pub expression: String, +} + +/// A shell-style glob pattern. +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] +#[serde(rename_all = "camelCase")] +pub struct GlobPattern { + /// The glob expression string. + pub expression: String, +} + +/// A pattern expression with its syntax type. +#[derive(Debug, Clone, PartialEq, Eq, From, Serialize, Deserialize, JsonSchema)] +#[serde(tag = "syntax", rename_all = "snake_case")] #[non_exhaustive] pub enum PatternExpression { /// Regular expression pattern. - Regex { - /// The regex expression. - expression: String, - }, + Regex(RegexPattern), /// Shell-style glob pattern. - Glob { - /// The glob expression. - expression: String, - }, + Glob(GlobPattern), } /// A named pattern for detection matching. @@ -33,3 +44,55 @@ pub struct PatternData { #[serde(flatten)] pub pattern: PatternExpression, } + +#[cfg(test)] +mod tests { + use super::*; + use crate::context::analytic::AnalyticVariant; + + #[test] + fn pattern_expression_roundtrip_regex() { + let pat = PatternExpression::Regex(RegexPattern { + expression: r"^\d+$".to_owned(), + }); + let json = serde_json::to_string(&pat).unwrap(); + assert!(json.contains(r#""syntax":"regex""#)); + assert!(json.contains(r#""expression":"^\\d+$""#)); + let back: PatternExpression = serde_json::from_str(&json).unwrap(); + assert_eq!(pat, back); + } + + #[test] + fn pattern_expression_roundtrip_glob() { + let pat = PatternExpression::Glob(GlobPattern { + expression: "*.txt".to_owned(), + }); + let json = serde_json::to_string(&pat).unwrap(); + assert!(json.contains(r#""syntax":"glob""#)); + let back: PatternExpression = serde_json::from_str(&json).unwrap(); + assert_eq!(pat, back); + } + + #[test] + fn pattern_data_flatten_no_tag_collision() { + // AnalyticVariant uses `tag = "kind"` and PatternExpression uses + // `tag = "syntax"`. Flattening must keep both distinct. + let entry = AnalyticVariant::Pattern(PatternData { + label: "phone".to_owned(), + pattern: PatternExpression::Regex(RegexPattern { + expression: r"\d{3}-\d{4}".to_owned(), + }), + }); + let json = serde_json::to_string(&entry).unwrap(); + assert!(json.contains(r#""kind":"pattern""#)); + assert!(json.contains(r#""syntax":"regex""#)); + let back: AnalyticVariant = serde_json::from_str(&json).unwrap(); + match back { + AnalyticVariant::Pattern(p) => { + assert_eq!(p.label, "phone"); + assert!(matches!(p.pattern, PatternExpression::Regex(_))); + } + _ => panic!("expected Pattern variant"), + } + } +} diff --git a/crates/nvisy-ontology/src/context/biometric/face.rs b/crates/nvisy-ontology/src/context/biometric/face.rs index 90481289..9a3e33af 100644 --- a/crates/nvisy-ontology/src/context/biometric/face.rs +++ b/crates/nvisy-ontology/src/context/biometric/face.rs @@ -4,7 +4,7 @@ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use crate::entity::ContentSource; -use crate::math::BoundingBox; +use crate::primitive::BoundingBox; /// Reference face data for identity matching. #[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] @@ -15,9 +15,6 @@ pub struct FaceData { /// Bounding box of the face within the image. #[serde(skip_serializing_if = "Option::is_none")] pub region: Option, - /// Image format hint (e.g. `"jpeg"`, `"png"`). - #[serde(skip_serializing_if = "Option::is_none")] - pub format: Option, /// Base64-encoded face embedding / template. #[serde(skip_serializing_if = "Option::is_none")] pub template: Option, @@ -32,7 +29,6 @@ impl FaceData { Self { image_source, region: None, - format: None, template: None, algorithm: None, } diff --git a/crates/nvisy-ontology/src/context/biometric/voice.rs b/crates/nvisy-ontology/src/context/biometric/voice.rs index cc8122a4..e7f5b248 100644 --- a/crates/nvisy-ontology/src/context/biometric/voice.rs +++ b/crates/nvisy-ontology/src/context/biometric/voice.rs @@ -4,7 +4,7 @@ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use crate::entity::ContentSource; -use crate::math::TimeSpan; +use crate::primitive::TimeSpan; /// Reference voice data for speaker identification. #[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] @@ -15,9 +15,6 @@ pub struct VoiceData { /// Segment within the audio used for enrollment. #[serde(skip_serializing_if = "Option::is_none")] pub segment: Option, - /// Audio format hint (e.g. `"wav"`, `"flac"`). - #[serde(skip_serializing_if = "Option::is_none")] - pub format: Option, /// Base64-encoded speaker embedding / voiceprint. #[serde(skip_serializing_if = "Option::is_none")] pub template: Option, @@ -32,7 +29,6 @@ impl VoiceData { Self { audio_source, segment: None, - format: None, template: None, algorithm: None, } diff --git a/crates/nvisy-ontology/src/context/document/signature.rs b/crates/nvisy-ontology/src/context/document/signature.rs index 7f3192d6..8ed14a98 100644 --- a/crates/nvisy-ontology/src/context/document/signature.rs +++ b/crates/nvisy-ontology/src/context/document/signature.rs @@ -4,7 +4,7 @@ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use crate::entity::ContentSource; -use crate::math::BoundingBox; +use crate::primitive::BoundingBox; /// Reference handwritten signature for verification. #[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] @@ -15,12 +15,12 @@ pub struct SignatureData { /// Bounding box of the signature within the image. #[serde(skip_serializing_if = "Option::is_none")] pub region: Option, - /// Image format hint (e.g. `"png"`, `"jpeg"`). - #[serde(skip_serializing_if = "Option::is_none")] - pub format: Option, /// Identity of the signer this signature belongs to. #[serde(skip_serializing_if = "Option::is_none")] pub signer_id: Option, + /// Algorithm used for signature verification. + #[serde(skip_serializing_if = "Option::is_none")] + pub algorithm: Option, } impl SignatureData { @@ -29,8 +29,8 @@ impl SignatureData { Self { image_source, region: None, - format: None, signer_id: None, + algorithm: None, } } @@ -39,4 +39,10 @@ impl SignatureData { self.signer_id = Some(signer_id.into()); self } + + /// Set the verification algorithm. + pub fn with_algorithm(mut self, algorithm: impl Into) -> Self { + self.algorithm = Some(algorithm.into()); + self + } } diff --git a/crates/nvisy-ontology/src/context/document/template.rs b/crates/nvisy-ontology/src/context/document/template.rs index 18d88504..845560dc 100644 --- a/crates/nvisy-ontology/src/context/document/template.rs +++ b/crates/nvisy-ontology/src/context/document/template.rs @@ -4,7 +4,7 @@ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use crate::entity::ContentSource; -use crate::math::BoundingBox; +use crate::primitive::BoundingBox; /// Reference document template for layout/type classification. /// @@ -18,9 +18,6 @@ pub struct TemplateData { /// Optional sub-region of interest within the template. #[serde(skip_serializing_if = "Option::is_none")] pub region: Option, - /// Image format hint (e.g. `"jpeg"`, `"png"`). - #[serde(skip_serializing_if = "Option::is_none")] - pub format: Option, /// Document type label (e.g. `"passport"`, `"drivers_license"`, `"invoice"`). #[serde(skip_serializing_if = "Option::is_none")] pub document_type: Option, @@ -32,7 +29,6 @@ impl TemplateData { Self { image_source, region: None, - format: None, document_type: None, } } diff --git a/crates/nvisy-ontology/src/context/entry.rs b/crates/nvisy-ontology/src/context/entry.rs index d01725d8..50d74231 100644 --- a/crates/nvisy-ontology/src/context/entry.rs +++ b/crates/nvisy-ontology/src/context/entry.rs @@ -1,5 +1,6 @@ //! Context entry types for reference data. +use derive_more::From; use jiff::Timestamp; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; @@ -16,7 +17,7 @@ use super::temporal::TemporalVariant; /// /// Each domain contains a nested enum of specific variants, /// keeping modality and semantic purpose cleanly separated. -#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] +#[derive(Debug, Clone, From, Serialize, Deserialize, JsonSchema)] #[serde(tag = "domain", content = "data", rename_all = "snake_case")] #[non_exhaustive] pub enum ContextEntryData { @@ -59,7 +60,7 @@ impl ContextEntry { /// Create a new context entry with a generated UUID and current timestamp. pub fn new(data: ContextEntryData) -> Self { Self { - id: Uuid::new_v4(), + id: Uuid::now_v7(), label: None, created_at: Timestamp::now(), expires_at: None, diff --git a/crates/nvisy-ontology/src/context/geospatial/address.rs b/crates/nvisy-ontology/src/context/geospatial/address.rs index fe156d39..5b92413a 100644 --- a/crates/nvisy-ontology/src/context/geospatial/address.rs +++ b/crates/nvisy-ontology/src/context/geospatial/address.rs @@ -15,9 +15,9 @@ pub struct AddressData { /// City name. #[serde(skip_serializing_if = "Option::is_none")] pub city: Option, - /// State, province, or region. + /// State, province, or administrative area. #[serde(skip_serializing_if = "Option::is_none")] - pub region: Option, + pub state: Option, /// Postal / ZIP code. #[serde(skip_serializing_if = "Option::is_none")] pub postal_code: Option, diff --git a/crates/nvisy-ontology/src/context/geospatial/coordinates.rs b/crates/nvisy-ontology/src/context/geospatial/coordinates.rs index 0b2d5e51..dac84e1c 100644 --- a/crates/nvisy-ontology/src/context/geospatial/coordinates.rs +++ b/crates/nvisy-ontology/src/context/geospatial/coordinates.rs @@ -3,7 +3,7 @@ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use crate::math::Vertex; +use crate::primitive::Vertex; /// A geographic coordinate (latitude/longitude). #[derive(Debug, Clone, Copy, Serialize, Deserialize, JsonSchema)] diff --git a/crates/nvisy-ontology/src/context/geospatial/region.rs b/crates/nvisy-ontology/src/context/geospatial/region.rs index d23432f5..2067a262 100644 --- a/crates/nvisy-ontology/src/context/geospatial/region.rs +++ b/crates/nvisy-ontology/src/context/geospatial/region.rs @@ -4,7 +4,7 @@ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use super::coordinates::GeoCoordinate; -use crate::math::Polygon; +use crate::primitive::Polygon; /// A geographic bounding box defined by its south-west and north-east corners. #[derive(Debug, Clone, Copy, Serialize, Deserialize, JsonSchema)] @@ -41,17 +41,17 @@ impl GeoBounds { pub enum GeoShape { /// Axis-aligned bounding rectangle. Bounds(GeoBounds), - /// Circular region defined by centre and radius. + /// Circular region defined by center and radius. Circle { - /// Centre of the circle. - centre: GeoCoordinate, - /// Radius in metres. + /// Center of the circle. + center: GeoCoordinate, + /// Radius in meters. radius_m: f64, }, /// Arbitrary polygon defined by vertices. Polygon { - /// Vertices in order (x = lng, y = lat). - polygon: Polygon, + /// Boundary vertices in order (x = lng, y = lat). + boundary: Polygon, }, } @@ -77,17 +77,17 @@ impl RegionData { } /// Create a circular region. - pub fn from_circle(centre: GeoCoordinate, radius_m: f64) -> Self { + pub fn from_circle(center: GeoCoordinate, radius_m: f64) -> Self { Self { - region: GeoShape::Circle { centre, radius_m }, + region: GeoShape::Circle { center, radius_m }, name: None, } } /// Create a polygon region. - pub fn from_polygon(polygon: Polygon) -> Self { + pub fn from_polygon(boundary: Polygon) -> Self { Self { - region: GeoShape::Polygon { polygon }, + region: GeoShape::Polygon { boundary }, name: None, } } diff --git a/crates/nvisy-ontology/src/context/mod.rs b/crates/nvisy-ontology/src/context/mod.rs index 750b2dcb..be34de4e 100644 --- a/crates/nvisy-ontology/src/context/mod.rs +++ b/crates/nvisy-ontology/src/context/mod.rs @@ -13,6 +13,7 @@ pub mod reference; pub mod temporal; use derive_builder::Builder; +use derive_more::{Deref, DerefMut, From}; use schemars::JsonSchema; use semver::Version; use serde::{Deserialize, Serialize}; @@ -23,39 +24,21 @@ pub use self::entry::{ContextEntry, ContextEntryData}; /// Lightweight set of context references carried by each document envelope. /// /// Each UUID points to a [`Context`] in the engine's context cache. -#[derive(Debug, Clone, Default, Serialize, Deserialize, JsonSchema)] +#[derive(Debug, Clone, Default, Deref, DerefMut, From)] +#[derive(Serialize, Deserialize, JsonSchema)] pub struct Contexts(Vec); impl Contexts { pub fn new() -> Self { - Self(Vec::new()) - } - - pub fn from_ids(ids: Vec) -> Self { - Self(ids) + Self::default() } + /// Add a context ID, deduplicating. pub fn push(&mut self, id: Uuid) { if !self.0.contains(&id) { self.0.push(id); } } - - pub fn ids(&self) -> &[Uuid] { - &self.0 - } - - pub fn len(&self) -> usize { - self.0.len() - } - - pub fn is_empty(&self) -> bool { - self.0.is_empty() - } - - pub fn contains(&self, id: &Uuid) -> bool { - self.0.contains(id) - } } #[cfg(test)] @@ -82,9 +65,9 @@ mod tests { #[test] fn contains_and_ids() { let id = Uuid::now_v7(); - let ctx = Contexts::from_ids(vec![id]); + let ctx = Contexts::from(vec![id]); assert!(ctx.contains(&id)); - assert_eq!(ctx.ids(), &[id]); + assert_eq!(&*ctx, &[id]); } #[test] diff --git a/crates/nvisy-ontology/src/context/reference/credential.rs b/crates/nvisy-ontology/src/context/reference/credential.rs index a31d4bb5..0e0bea05 100644 --- a/crates/nvisy-ontology/src/context/reference/credential.rs +++ b/crates/nvisy-ontology/src/context/reference/credential.rs @@ -3,16 +3,83 @@ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; +/// Classification of credential secrets. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +#[derive(Serialize, Deserialize, JsonSchema)] +#[serde(rename_all = "snake_case")] +#[non_exhaustive] +pub enum CredentialKind { + /// API key. + ApiKey, + /// OAuth access or refresh token. + OauthToken, + /// Password or passphrase. + Password, + /// Private key (SSH, TLS, etc.). + PrivateKey, + /// Other credential type. + Other, +} + /// A reference credential for detecting leaked secrets. +/// +/// The `value` field is intentionally excluded from serialization to +/// prevent plaintext secrets from appearing in logs or API responses. +/// It is only accepted during deserialization (ingest). #[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] #[serde(rename_all = "camelCase")] pub struct CredentialData { /// The credential value (API key, token, etc.). + /// + /// Excluded from serialization output to prevent leaking secrets. + /// Round-trip deserialization yields an empty string. + #[serde(default, skip_serializing)] pub value: String, - /// Type of credential (e.g. `"api_key"`, `"oauth_token"`, `"password"`). + /// Classification of this credential. #[serde(skip_serializing_if = "Option::is_none")] - pub credential_type: Option, + pub credential_kind: Option, /// Service or provider this credential belongs to. #[serde(skip_serializing_if = "Option::is_none")] pub provider: Option, } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn value_is_not_serialized() { + let cred = CredentialData { + value: "sk-secret-token".to_owned(), + credential_kind: Some(CredentialKind::ApiKey), + provider: Some("openai".to_owned()), + }; + let json = serde_json::to_string(&cred).unwrap(); + assert!( + !json.contains("sk-secret-token"), + "serialized JSON must not contain plaintext value, got: {json}" + ); + assert!(json.contains(r#""credentialKind":"api_key""#)); + } + + #[test] + fn round_trip_defaults_value_to_empty() { + // Serialize without value, then deserialize: value must default to empty string. + let cred = CredentialData { + value: "secret".to_owned(), + credential_kind: None, + provider: None, + }; + let json = serde_json::to_string(&cred).unwrap(); + let back: CredentialData = serde_json::from_str(&json).unwrap(); + assert_eq!(back.value, ""); + } + + #[test] + fn deserialize_with_value_provided() { + let json = r#"{"value":"abc123","credentialKind":"password"}"#; + let cred: CredentialData = serde_json::from_str(json).unwrap(); + assert_eq!(cred.value, "abc123"); + assert_eq!(cred.credential_kind, Some(CredentialKind::Password)); + } +} diff --git a/crates/nvisy-ontology/src/context/reference/image.rs b/crates/nvisy-ontology/src/context/reference/image.rs index b86b42ba..64b80457 100644 --- a/crates/nvisy-ontology/src/context/reference/image.rs +++ b/crates/nvisy-ontology/src/context/reference/image.rs @@ -4,7 +4,7 @@ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use crate::entity::ContentSource; -use crate::math::BoundingBox; +use crate::primitive::BoundingBox; /// Reference image for object/scene matching. #[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] @@ -15,7 +15,4 @@ pub struct ImageData { /// Optional sub-region within the image. #[serde(skip_serializing_if = "Option::is_none")] pub region: Option, - /// Image format hint (e.g. `"jpeg"`, `"png"`, `"webp"`). - #[serde(skip_serializing_if = "Option::is_none")] - pub format: Option, } diff --git a/crates/nvisy-ontology/src/context/reference/mod.rs b/crates/nvisy-ontology/src/context/reference/mod.rs index d587b68e..cf265600 100644 --- a/crates/nvisy-ontology/src/context/reference/mod.rs +++ b/crates/nvisy-ontology/src/context/reference/mod.rs @@ -8,7 +8,7 @@ mod text; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -pub use self::credential::CredentialData; +pub use self::credential::{CredentialData, CredentialKind}; pub use self::image::ImageData; pub use self::tag::TagData; pub use self::text::{TextData, TextEntry}; @@ -25,5 +25,5 @@ pub enum ReferenceVariant { /// API keys, tokens, or known secret patterns. Credential(CredentialData), /// Reference image for object/scene matching. - Object(ImageData), + Image(ImageData), } diff --git a/crates/nvisy-ontology/src/context/reference/text.rs b/crates/nvisy-ontology/src/context/reference/text.rs index 7c739d4d..7221ea82 100644 --- a/crates/nvisy-ontology/src/context/reference/text.rs +++ b/crates/nvisy-ontology/src/context/reference/text.rs @@ -3,6 +3,8 @@ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; +use crate::primitive::LanguageTag; + /// A labeled text value for reference matching. /// /// The `key` is a human/LLM-readable label describing what this value @@ -21,9 +23,81 @@ pub struct TextEntry { /// /// Keys describe *what* a value represents (for humans and LLMs); /// values are the literal strings used for matching. -#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] +#[derive(Debug, Clone, Default, Serialize, Deserialize, JsonSchema)] #[serde(rename_all = "camelCase")] pub struct TextData { /// Key-value pairs for matching. + #[serde(default, skip_serializing_if = "Vec::is_empty")] pub entries: Vec, + /// BCP-47 language tag for locale-sensitive matching. + #[serde(skip_serializing_if = "Option::is_none")] + #[schemars(with = "Option")] + pub language: Option, +} + +impl TextData { + /// Create text data from a list of entries. + pub fn new(entries: Vec) -> Self { + Self { + entries, + language: None, + } + } + + /// Set the language for locale-sensitive matching. + pub fn with_language(mut self, language: LanguageTag) -> Self { + self.language = Some(language); + self + } +} + +impl TextEntry { + /// Create a labeled text entry. + pub fn new(key: impl Into, value: impl Into) -> Self { + Self { + key: key.into(), + value: value.into(), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn new_constructor_sets_no_language() { + let data = TextData::new(vec![TextEntry::new("name", "Alice")]); + assert_eq!(data.entries.len(), 1); + assert!(data.language.is_none()); + } + + #[test] + fn with_language_sets_field() { + let tag: LanguageTag = "en-US".parse().unwrap(); + let data = TextData::new(vec![]).with_language(tag.clone()); + assert_eq!(data.language, Some(tag)); + } + + #[test] + fn roundtrip_serde() { + let data = TextData::new(vec![ + TextEntry::new("full_name", "Alice Smith"), + TextEntry::new("email", "alice@example.com"), + ]) + .with_language("en".parse().unwrap()); + let json = serde_json::to_string(&data).unwrap(); + let back: TextData = serde_json::from_str(&json).unwrap(); + assert_eq!(data.entries.len(), back.entries.len()); + assert_eq!(data.language, back.language); + } + + #[test] + fn roundtrip_empty() { + let data = TextData::default(); + let json = serde_json::to_string(&data).unwrap(); + let back: TextData = serde_json::from_str(&json).unwrap(); + assert!(back.entries.is_empty()); + assert!(back.language.is_none()); + } } diff --git a/crates/nvisy-ontology/src/context/temporal/date.rs b/crates/nvisy-ontology/src/context/temporal/date.rs index 6ef72abc..0a60b87c 100644 --- a/crates/nvisy-ontology/src/context/temporal/date.rs +++ b/crates/nvisy-ontology/src/context/temporal/date.rs @@ -15,10 +15,6 @@ pub struct DateData { #[serde(skip_serializing_if = "Option::is_none")] #[schemars(with = "Option")] pub end: Option, - /// Optional format hint describing the expected representation - /// (e.g. `"MM/DD/YYYY"`, `"ISO8601"`). - #[serde(skip_serializing_if = "Option::is_none")] - pub format: Option, } impl DateData { @@ -27,7 +23,6 @@ impl DateData { Self { start: date, end: None, - format: None, } } @@ -36,13 +31,6 @@ impl DateData { Self { start, end: Some(end), - format: None, } } - - /// Set a format hint. - pub fn with_format(mut self, format: impl Into) -> Self { - self.format = Some(format.into()); - self - } } diff --git a/crates/nvisy-ontology/src/context/temporal/datetime.rs b/crates/nvisy-ontology/src/context/temporal/datetime.rs new file mode 100644 index 00000000..e57e66b0 --- /dev/null +++ b/crates/nvisy-ontology/src/context/temporal/datetime.rs @@ -0,0 +1,66 @@ +//! Date-time reference data. + +use jiff::civil::DateTime; +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; + +/// A date-time or date-time range reference for temporal matching. +/// +/// Uses naive (timezone-unaware) date-times from [`jiff::civil::DateTime`]. +/// For timezone-aware timestamps, use the entry-level `created_at` / +/// `expires_at` fields on [`ContextEntry`](crate::context::ContextEntry). +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] +#[serde(rename_all = "camelCase")] +pub struct DateTimeData { + /// Start (or only) date-time. + #[schemars(with = "String")] + pub start: DateTime, + /// End date-time for a range. When `None` this represents a single instant. + #[serde(skip_serializing_if = "Option::is_none")] + #[schemars(with = "Option")] + pub end: Option, +} + +impl DateTimeData { + /// Create a single date-time reference. + pub fn single(dt: DateTime) -> Self { + Self { + start: dt, + end: None, + } + } + + /// Create a date-time range reference. + pub fn range(start: DateTime, end: DateTime) -> Self { + Self { + start, + end: Some(end), + } + } +} + +#[cfg(test)] +mod tests { + use jiff::civil::datetime; + + use super::*; + + #[test] + fn roundtrip_serde() { + let dt = DateTimeData::range( + datetime(2026, 1, 1, 9, 0, 0, 0), + datetime(2026, 1, 1, 17, 0, 0, 0), + ); + let json = serde_json::to_string(&dt).unwrap(); + let back: DateTimeData = serde_json::from_str(&json).unwrap(); + assert_eq!(dt.start, back.start); + assert_eq!(dt.end, back.end); + } + + #[test] + fn single_skips_end_in_json() { + let dt = DateTimeData::single(datetime(2026, 1, 1, 9, 0, 0, 0)); + let json = serde_json::to_string(&dt).unwrap(); + assert!(!json.contains("\"end\""), "end should be skipped: {json}"); + } +} diff --git a/crates/nvisy-ontology/src/context/temporal/mod.rs b/crates/nvisy-ontology/src/context/temporal/mod.rs index 95613630..274551bb 100644 --- a/crates/nvisy-ontology/src/context/temporal/mod.rs +++ b/crates/nvisy-ontology/src/context/temporal/mod.rs @@ -1,17 +1,29 @@ -//! Date and time-based reference data. +//! Date, time, and datetime reference data. mod date; +mod datetime; +mod time; +mod timespan; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; pub use self::date::DateData; +pub use self::datetime::DateTimeData; +pub use self::time::TimeData; +pub use self::timespan::TimeSpanData; /// Temporal matching variants. #[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] #[serde(tag = "kind", rename_all = "snake_case")] #[non_exhaustive] pub enum TemporalVariant { - /// Date or date-range to match. + /// Date or date-range to match (e.g. birthdays, expiry dates). Date(DateData), + /// Time-of-day or time range (e.g. business hours). + Time(TimeData), + /// Date-time or date-time range (e.g. appointment timestamps). + DateTime(DateTimeData), + /// Time span reference for matching audio/video segments. + TimeSpan(TimeSpanData), } diff --git a/crates/nvisy-ontology/src/context/temporal/time.rs b/crates/nvisy-ontology/src/context/temporal/time.rs new file mode 100644 index 00000000..11a40e6e --- /dev/null +++ b/crates/nvisy-ontology/src/context/temporal/time.rs @@ -0,0 +1,67 @@ +//! Time-of-day reference data. + +use jiff::civil::Time; +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; + +/// A time-of-day or time range reference for temporal matching. +/// +/// Uses naive (timezone-unaware) times from [`jiff::civil::Time`]. +/// Useful for matching recurring time patterns (e.g. business hours). +#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)] +#[serde(rename_all = "camelCase")] +pub struct TimeData { + /// Start (or only) time. + #[schemars(with = "String")] + pub start: Time, + /// End time for a range. When `None` this represents a single time. + #[serde(skip_serializing_if = "Option::is_none")] + #[schemars(with = "Option")] + pub end: Option