cargo-util-schemas-0.10.0/.cargo_vcs_info.json0000644000000001670000000000100146050ustar { "git": { "sha1": "840b83a10fb0e039a83f4d70ad032892c287570a" }, "path_in_vcs": "crates/cargo-util-schemas" }cargo-util-schemas-0.10.0/Cargo.lock0000644000000714020000000000100125600ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 4 [[package]] name = "aho-corasick" version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] [[package]] name = "anstream" version = "0.6.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "301af1932e46185686725e0fad2f8f2aa7da69dd70bf6ecc44d6b703844a3933" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", "is_terminal_polyfill", "utf8parse", ] [[package]] name = "anstyle" version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" [[package]] name = "anstyle-lossy" version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04d3a5dc826f84d0ea11882bb8054ff7f3d482602e11bb181101303a279ea01f" dependencies = [ "anstyle", ] [[package]] name = "anstyle-parse" version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c8bdeb6047d8983be085bab0ba1472e6dc604e7041dbf6fcd5e71523014fae9" dependencies = [ "windows-sys 0.59.0", ] [[package]] name = "anstyle-svg" version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c681338396641f4e32a29f045d0c70950da7207b4376685b51396c481ee36f1a" dependencies = [ "anstyle", "anstyle-lossy", "anstyle-parse", "html-escape", "unicode-width", ] [[package]] name = "anstyle-wincon" version = "3.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "403f75924867bb1033c59fbf0797484329750cfbe3c4325cd33127941fabc882" dependencies = [ "anstyle", "once_cell_polyfill", "windows-sys 0.59.0", ] [[package]] name = "autocfg" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "bitflags" version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" [[package]] name = "cargo-util-schemas" version = "0.10.0" dependencies = [ "schemars", "semver", "serde", "serde-untagged", "serde-value", "serde_json", "snapbox", "thiserror", "toml", "unicode-xid", "url", ] [[package]] name = "cfg-if" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" [[package]] name = "colorchoice" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" [[package]] name = "content_inspector" version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7bda66e858c683005a53a9a60c69a4aca7eeaa45d124526e389f7aec8e62f38" dependencies = [ "memchr", ] [[package]] name = "displaydoc" version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "dunce" version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" [[package]] name = "dyn-clone" version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c7a8fb8a9fbf66c1f703fe16184d10ca0ee9d23be5b4436400408ba54a95005" [[package]] name = "equivalent" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "erased-serde" version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e004d887f51fcb9fef17317a2f3525c887d8aa3f4f50fed920816a688284a5b7" dependencies = [ "serde", "typeid", ] [[package]] name = "errno" version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" dependencies = [ "libc", "windows-sys 0.60.2", ] [[package]] name = "fastrand" version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "filetime" version = "0.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "35c0522e981e68cbfa8c3f978441a5f34b30b96e146b33cd3359176b50fe8586" dependencies = [ "cfg-if", "libc", "libredox", "windows-sys 0.59.0", ] [[package]] name = "form_urlencoded" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" dependencies = [ "percent-encoding", ] [[package]] name = "getrandom" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" dependencies = [ "cfg-if", "libc", "r-efi", "wasi", ] [[package]] name = "hashbrown" version = "0.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" [[package]] name = "html-escape" version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d1ad449764d627e22bfd7cd5e8868264fc9236e07c752972b4080cd351cb476" dependencies = [ "utf8-width", ] [[package]] name = "icu_collections" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" dependencies = [ "displaydoc", "potential_utf", "yoke", "zerofrom", "zerovec", ] [[package]] name = "icu_locale_core" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" dependencies = [ "displaydoc", "litemap", "tinystr", "writeable", "zerovec", ] [[package]] name = "icu_normalizer" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" dependencies = [ "displaydoc", "icu_collections", "icu_normalizer_data", "icu_properties", "icu_provider", "smallvec", "zerovec", ] [[package]] name = "icu_normalizer_data" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" [[package]] name = "icu_properties" version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" dependencies = [ "displaydoc", "icu_collections", "icu_locale_core", "icu_properties_data", "icu_provider", "potential_utf", "zerotrie", "zerovec", ] [[package]] name = "icu_properties_data" version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" [[package]] name = "icu_provider" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" dependencies = [ "displaydoc", "icu_locale_core", "stable_deref_trait", "tinystr", "writeable", "yoke", "zerofrom", "zerotrie", "zerovec", ] [[package]] name = "idna" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" dependencies = [ "idna_adapter", "smallvec", "utf8_iter", ] [[package]] name = "idna_adapter" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" dependencies = [ "icu_normalizer", "icu_properties", ] [[package]] name = "indexmap" version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" dependencies = [ "equivalent", "hashbrown", ] [[package]] name = "is_terminal_polyfill" version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" [[package]] name = "itoa" version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "libc" version = "0.2.174" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" [[package]] name = "libredox" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ "bitflags", "libc", "redox_syscall", ] [[package]] name = "linux-raw-sys" version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" [[package]] name = "litemap" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" [[package]] name = "memchr" version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" [[package]] name = "normalize-line-endings" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" [[package]] name = "num-traits" version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", ] [[package]] name = "once_cell" version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "once_cell_polyfill" version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" [[package]] name = "ordered-float" version = "2.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c" dependencies = [ "num-traits", ] [[package]] name = "percent-encoding" version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "potential_utf" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585" dependencies = [ "zerovec", ] [[package]] name = "proc-macro2" version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" dependencies = [ "proc-macro2", ] [[package]] name = "r-efi" version = "5.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" [[package]] name = "redox_syscall" version = "0.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d04b7d0ee6b4a0207a0a7adb104d23ecb0b47d6beae7152d0fa34b692b29fd6" dependencies = [ "bitflags", ] [[package]] name = "ref-cast" version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a0ae411dbe946a674d89546582cea4ba2bb8defac896622d6496f14c23ba5cf" dependencies = [ "ref-cast-impl", ] [[package]] name = "ref-cast-impl" version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1165225c21bff1f3bbce98f5a1f889949bc902d3575308cc7b0de30b4f6d27c7" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "regex" version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", "regex-automata", "regex-syntax", ] [[package]] name = "regex-automata" version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", "regex-syntax", ] [[package]] name = "regex-syntax" version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "rustix" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" dependencies = [ "bitflags", "errno", "libc", "linux-raw-sys", "windows-sys 0.59.0", ] [[package]] name = "ryu" version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" [[package]] name = "same-file" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" dependencies = [ "winapi-util", ] [[package]] name = "schemars" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1375ba8ef45a6f15d83fa8748f1079428295d403d6ea991d09ab100155fbc06d" dependencies = [ "dyn-clone", "ref-cast", "schemars_derive", "semver", "serde", "serde_json", ] [[package]] name = "schemars_derive" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b13ed22d6d49fe23712e068770b5c4df4a693a2b02eeff8e7ca3135627a24f6" dependencies = [ "proc-macro2", "quote", "serde_derive_internals", "syn", ] [[package]] name = "semver" version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" dependencies = [ "serde", ] [[package]] name = "serde" version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" dependencies = [ "serde_derive", ] [[package]] name = "serde-untagged" version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "299d9c19d7d466db4ab10addd5703e4c615dec2a5a16dbbafe191045e87ee66e" dependencies = [ "erased-serde", "serde", "typeid", ] [[package]] name = "serde-value" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c" dependencies = [ "ordered-float", "serde", ] [[package]] name = "serde_derive" version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "serde_derive_internals" version = "0.29.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "serde_json" version = "1.0.140" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" dependencies = [ "indexmap", "itoa", "memchr", "ryu", "serde", ] [[package]] name = "serde_spanned" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40734c41988f7306bb04f0ecf60ec0f3f1caa34290e4e8ea471dcd3346483b83" dependencies = [ "serde", ] [[package]] name = "similar" version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa" [[package]] name = "smallvec" version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] name = "snapbox" version = "0.6.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96dcfc4581e3355d70ac2ee14cfdf81dce3d85c85f1ed9e2c1d3013f53b3436b" dependencies = [ "anstream", "anstyle", "anstyle-svg", "content_inspector", "dunce", "filetime", "normalize-line-endings", "regex", "serde", "serde_json", "similar", "snapbox-macros", "tempfile", "walkdir", ] [[package]] name = "snapbox-macros" version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "16569f53ca23a41bb6f62e0a5084aa1661f4814a67fa33696a79073e03a664af" dependencies = [ "anstream", ] [[package]] name = "stable_deref_trait" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" [[package]] name = "syn" version = "2.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "synstructure" version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "tempfile" version = "3.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" dependencies = [ "fastrand", "getrandom", "once_cell", "rustix", "windows-sys 0.59.0", ] [[package]] name = "thiserror" version = "2.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" version = "2.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "tinystr" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" dependencies = [ "displaydoc", "zerovec", ] [[package]] name = "toml" version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed0aee96c12fa71097902e0bb061a5e1ebd766a6636bb605ba401c45c1650eac" dependencies = [ "serde", "serde_spanned", "toml_datetime", ] [[package]] name = "toml_datetime" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bade1c3e902f58d73d3f294cd7f20391c1cb2fbcb643b73566bc773971df91e3" dependencies = [ "serde", ] [[package]] name = "typeid" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" [[package]] name = "unicode-ident" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" [[package]] name = "unicode-width" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a1a07cc7db3810833284e8d372ccdc6da29741639ecc70c9ec107df0fa6154c" [[package]] name = "unicode-xid" version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" [[package]] name = "url" version = "2.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" dependencies = [ "form_urlencoded", "idna", "percent-encoding", ] [[package]] name = "utf8-width" version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "86bd8d4e895da8537e5315b8254664e6b769c4ff3db18321b297a1e7004392e3" [[package]] name = "utf8_iter" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] name = "utf8parse" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "walkdir" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" dependencies = [ "same-file", "winapi-util", ] [[package]] name = "wasi" version = "0.14.2+wasi-0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" dependencies = [ "wit-bindgen-rt", ] [[package]] name = "winapi-util" version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ "windows-sys 0.59.0", ] [[package]] name = "windows-sys" version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ "windows-targets 0.52.6", ] [[package]] name = "windows-sys" version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" dependencies = [ "windows-targets 0.53.2", ] [[package]] name = "windows-targets" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ "windows_aarch64_gnullvm 0.52.6", "windows_aarch64_msvc 0.52.6", "windows_i686_gnu 0.52.6", "windows_i686_gnullvm 0.52.6", "windows_i686_msvc 0.52.6", "windows_x86_64_gnu 0.52.6", "windows_x86_64_gnullvm 0.52.6", "windows_x86_64_msvc 0.52.6", ] [[package]] name = "windows-targets" version = "0.53.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c66f69fcc9ce11da9966ddb31a40968cad001c5bedeb5c2b82ede4253ab48aef" dependencies = [ "windows_aarch64_gnullvm 0.53.0", "windows_aarch64_msvc 0.53.0", "windows_i686_gnu 0.53.0", "windows_i686_gnullvm 0.53.0", "windows_i686_msvc 0.53.0", "windows_x86_64_gnu 0.53.0", "windows_x86_64_gnullvm 0.53.0", "windows_x86_64_msvc 0.53.0", ] [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_gnullvm" version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_aarch64_msvc" version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" [[package]] name = "windows_i686_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnu" version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_gnullvm" version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_i686_msvc" version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnu" version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_gnullvm" version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "windows_x86_64_msvc" version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" [[package]] name = "wit-bindgen-rt" version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" dependencies = [ "bitflags", ] [[package]] name = "writeable" version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" [[package]] name = "yoke" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" dependencies = [ "serde", "stable_deref_trait", "yoke-derive", "zerofrom", ] [[package]] name = "yoke-derive" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" dependencies = [ "proc-macro2", "quote", "syn", "synstructure", ] [[package]] name = "zerofrom" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" dependencies = [ "zerofrom-derive", ] [[package]] name = "zerofrom-derive" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", "syn", "synstructure", ] [[package]] name = "zerotrie" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" dependencies = [ "displaydoc", "yoke", "zerofrom", ] [[package]] name = "zerovec" version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428" dependencies = [ "yoke", "zerofrom", "zerovec-derive", ] [[package]] name = "zerovec-derive" version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" dependencies = [ "proc-macro2", "quote", "syn", ] cargo-util-schemas-0.10.0/Cargo.toml0000644000000041330000000000100126000ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2024" rust-version = "1.88" name = "cargo-util-schemas" version = "0.10.0" build = false autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = "Deserialization schemas for Cargo" homepage = "https://github.com/rust-lang/cargo" readme = "README.md" license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/cargo" resolver = "2" [features] unstable-schema = [ "dep:schemars", "dep:serde_json", ] [lib] name = "cargo_util_schemas" path = "src/lib.rs" [dependencies.schemars] version = "1.0.3" features = [ "preserve_order", "semver1", ] optional = true [dependencies.semver] version = "1.0.26" features = ["serde"] [dependencies.serde] version = "1.0.219" features = ["derive"] [dependencies.serde-untagged] version = "0.1.7" [dependencies.serde-value] version = "0.7.0" [dependencies.serde_json] version = "1.0.140" optional = true [dependencies.thiserror] version = "2.0.12" [dependencies.toml] version = "0.9.0" features = ["serde"] default-features = false [dependencies.unicode-xid] version = "0.2.6" [dependencies.url] version = "2.5.4" [dev-dependencies.serde_json] version = "1.0.140" [dev-dependencies.snapbox] version = "0.6.21" features = [ "diff", "dir", "term-svg", "regex", "json", ] [lints.clippy] dbg_macro = "warn" disallowed_methods = "warn" print_stderr = "warn" print_stdout = "warn" self_named_module_files = "warn" [lints.clippy.all] level = "allow" priority = -2 [lints.clippy.correctness] level = "warn" priority = -1 [lints.rust] rust_2018_idioms = "warn" [lints.rustdoc] private_intra_doc_links = "allow" cargo-util-schemas-0.10.0/Cargo.toml.orig000064400000000000000000000014751046102023000162670ustar 00000000000000[package] name = "cargo-util-schemas" version = "0.10.0" rust-version = "1.88" # MSRV:1 edition.workspace = true license.workspace = true homepage.workspace = true repository.workspace = true description = "Deserialization schemas for Cargo" [dependencies] schemars = { workspace = true, features = ["preserve_order", "semver1"], optional = true } semver.workspace = true serde = { workspace = true, features = ["derive"] } serde_json = { workspace = true, optional = true } serde-untagged.workspace = true serde-value.workspace = true thiserror.workspace = true toml = { workspace = true, features = ["serde"] } unicode-xid.workspace = true url.workspace = true [lints] workspace = true [dev-dependencies] snapbox.workspace = true serde_json.workspace = true [features] unstable-schema = ["dep:schemars", "dep:serde_json"] cargo-util-schemas-0.10.0/LICENSE-APACHE000064400000000000000000000251541046102023000153240ustar 00000000000000 Apache License Version 2.0, January 2004 https://www.apache.org/licenses/LICENSE-2.0 TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. cargo-util-schemas-0.10.0/LICENSE-MIT000064400000000000000000000017771046102023000150410ustar 00000000000000Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. cargo-util-schemas-0.10.0/README.md000064400000000000000000000002051046102023000146450ustar 00000000000000> This crate is maintained by the Cargo team for use by the wider > ecosystem. This crate follows semver compatibility for its APIs. cargo-util-schemas-0.10.0/index.schema.json000064400000000000000000000150111046102023000166300ustar 00000000000000{ "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "IndexPackage", "description": "A single line in the index representing a single version of a package.", "type": "object", "properties": { "name": { "description": "Name of the package.", "type": "string" }, "vers": { "description": "The version of this dependency.", "$ref": "#/$defs/SemVer" }, "deps": { "description": "All kinds of direct dependencies of the package, including dev and\nbuild dependencies.", "type": "array", "items": { "$ref": "#/$defs/RegistryDependency" } }, "features": { "description": "Set of features defined for the package, i.e., `[features]` table.", "type": "object", "additionalProperties": { "type": "array", "items": { "type": "string" } }, "default": {} }, "features2": { "description": "This field contains features with new, extended syntax. Specifically,\nnamespaced features (`dep:`) and weak dependencies (`pkg?/feat`).\n\nThis is separated from `features` because versions older than 1.19\nwill fail to load due to not being able to parse the new syntax, even\nwith a `Cargo.lock` file.", "type": [ "object", "null" ], "additionalProperties": { "type": "array", "items": { "type": "string" } } }, "cksum": { "description": "Checksum for verifying the integrity of the corresponding downloaded package.", "type": "string" }, "yanked": { "description": "If `true`, Cargo will skip this version when resolving.\n\nThis was added in 2014. Everything in the crates.io index has this set\nnow, so this probably doesn't need to be an option anymore.", "type": [ "boolean", "null" ] }, "links": { "description": "Native library name this package links to.\n\nAdded early 2018 (see ),\ncan be `None` if published before then.", "type": [ "string", "null" ] }, "rust_version": { "description": "Required version of rust\n\nCorresponds to `package.rust-version`.\n\nAdded in 2023 (see ),\ncan be `None` if published before then or if not set in the manifest.", "type": [ "string", "null" ] }, "v": { "description": "The schema version for this entry.\n\nIf this is None, it defaults to version `1`. Entries with unknown\nversions are ignored.\n\nVersion `2` schema adds the `features2` field.\n\nVersion `3` schema adds `artifact`, `bindep_targes`, and `lib` for\nartifact dependencies support.\n\nThis provides a method to safely introduce changes to index entries\nand allow older versions of cargo to ignore newer entries it doesn't\nunderstand. This is honored as of 1.51, so unfortunately older\nversions will ignore it, and potentially misinterpret version 2 and\nnewer entries.\n\nThe intent is that versions older than 1.51 will work with a\npre-existing `Cargo.lock`, but they may not correctly process `cargo\nupdate` or build a lock from scratch. In that case, cargo may\nincorrectly select a new package that uses a new index schema. A\nworkaround is to downgrade any packages that are incompatible with the\n`--precise` flag of `cargo update`.", "type": [ "integer", "null" ], "format": "uint32", "minimum": 0 } }, "required": [ "name", "vers", "deps", "cksum" ], "$defs": { "SemVer": { "type": "string", "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$" }, "RegistryDependency": { "description": "A dependency as encoded in the [`IndexPackage`] index JSON.", "type": "object", "properties": { "name": { "description": "Name of the dependency. If the dependency is renamed, the original\nwould be stored in [`RegistryDependency::package`].", "type": "string" }, "req": { "description": "The SemVer requirement for this dependency.", "type": "string" }, "features": { "description": "Set of features enabled for this dependency.", "type": "array", "items": { "type": "string" }, "default": [] }, "optional": { "description": "Whether or not this is an optional dependency.", "type": "boolean", "default": false }, "default_features": { "description": "Whether or not default features are enabled.", "type": "boolean", "default": true }, "target": { "description": "The target platform for this dependency.", "type": [ "string", "null" ] }, "kind": { "description": "The dependency kind. \"dev\", \"build\", and \"normal\".", "type": [ "string", "null" ] }, "registry": { "description": "The URL of the index of the registry where this dependency is from.\n`None` if it is from the same index.", "type": [ "string", "null" ] }, "package": { "description": "The original name if the dependency is renamed.", "type": [ "string", "null" ] }, "public": { "description": "Whether or not this is a public dependency. Unstable. See [RFC 1977].\n\n[RFC 1977]: https://rust-lang.github.io/rfcs/1977-public-private-dependencies.html", "type": [ "boolean", "null" ] }, "artifact": { "description": "The artifacts to build from this dependency.", "type": [ "array", "null" ], "items": { "type": "string" } }, "bindep_target": { "description": "The target for bindep.", "type": [ "string", "null" ] }, "lib": { "description": "Whether or not this is a library dependency.", "type": "boolean", "default": false } }, "required": [ "name", "req" ] } } }cargo-util-schemas-0.10.0/manifest.schema.json000064400000000000000000000741061046102023000173410ustar 00000000000000{ "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "TomlManifest", "description": "This type is used to deserialize `Cargo.toml` files.", "type": "object", "properties": { "cargo-features": { "type": [ "array", "null" ], "items": { "type": "string" } }, "package": { "anyOf": [ { "$ref": "#/$defs/TomlPackage" }, { "type": "null" } ] }, "project": { "anyOf": [ { "$ref": "#/$defs/TomlPackage" }, { "type": "null" } ] }, "badges": { "type": [ "object", "null" ], "additionalProperties": { "type": "object", "additionalProperties": { "type": "string" } } }, "features": { "type": [ "object", "null" ], "additionalProperties": { "type": "array", "items": { "type": "string" } } }, "lib": { "anyOf": [ { "$ref": "#/$defs/TomlTarget" }, { "type": "null" } ] }, "bin": { "type": [ "array", "null" ], "items": { "$ref": "#/$defs/TomlTarget" } }, "example": { "type": [ "array", "null" ], "items": { "$ref": "#/$defs/TomlTarget" } }, "test": { "type": [ "array", "null" ], "items": { "$ref": "#/$defs/TomlTarget" } }, "bench": { "type": [ "array", "null" ], "items": { "$ref": "#/$defs/TomlTarget" } }, "dependencies": { "type": [ "object", "null" ], "additionalProperties": { "$ref": "#/$defs/InheritableDependency" } }, "dev-dependencies": { "type": [ "object", "null" ], "additionalProperties": { "$ref": "#/$defs/InheritableDependency" } }, "dev_dependencies": { "type": [ "object", "null" ], "additionalProperties": { "$ref": "#/$defs/InheritableDependency" } }, "build-dependencies": { "type": [ "object", "null" ], "additionalProperties": { "$ref": "#/$defs/InheritableDependency" } }, "build_dependencies": { "type": [ "object", "null" ], "additionalProperties": { "$ref": "#/$defs/InheritableDependency" } }, "target": { "type": [ "object", "null" ], "additionalProperties": { "$ref": "#/$defs/TomlPlatform" } }, "lints": { "anyOf": [ { "$ref": "#/$defs/InheritableLints" }, { "type": "null" } ] }, "hints": { "anyOf": [ { "$ref": "#/$defs/Hints" }, { "type": "null" } ] }, "workspace": { "anyOf": [ { "$ref": "#/$defs/TomlWorkspace" }, { "type": "null" } ] }, "profile": { "anyOf": [ { "$ref": "#/$defs/TomlProfiles" }, { "type": "null" } ] }, "patch": { "type": [ "object", "null" ], "additionalProperties": { "type": "object", "additionalProperties": { "$ref": "#/$defs/TomlDependency" } } }, "replace": { "type": [ "object", "null" ], "additionalProperties": { "$ref": "#/$defs/TomlDependency" } } }, "$defs": { "TomlPackage": { "description": "Represents the `package`/`project` sections of a `Cargo.toml`.\n\nNote that the order of the fields matters, since this is the order they\nare serialized to a TOML file. For example, you cannot have values after\nthe field `metadata`, since it is a table and values cannot appear after\ntables.", "type": "object", "properties": { "edition": { "anyOf": [ { "$ref": "#/$defs/InheritableField" }, { "type": "null" } ] }, "rust-version": { "type": [ "string", "null" ] }, "name": { "type": [ "string", "null" ] }, "version": { "anyOf": [ { "$ref": "#/$defs/InheritableField2" }, { "type": "null" } ] }, "authors": { "anyOf": [ { "$ref": "#/$defs/InheritableField3" }, { "type": "null" } ] }, "build": { "anyOf": [ { "$ref": "#/$defs/TomlPackageBuild" }, { "type": "null" } ] }, "metabuild": { "anyOf": [ { "$ref": "#/$defs/StringOrVec" }, { "type": "null" } ] }, "default-target": { "type": [ "string", "null" ] }, "forced-target": { "type": [ "string", "null" ] }, "links": { "type": [ "string", "null" ] }, "exclude": { "anyOf": [ { "$ref": "#/$defs/InheritableField3" }, { "type": "null" } ] }, "include": { "anyOf": [ { "$ref": "#/$defs/InheritableField3" }, { "type": "null" } ] }, "publish": { "anyOf": [ { "$ref": "#/$defs/InheritableField4" }, { "type": "null" } ] }, "workspace": { "type": [ "string", "null" ] }, "im-a-teapot": { "type": [ "boolean", "null" ] }, "autolib": { "type": [ "boolean", "null" ] }, "autobins": { "type": [ "boolean", "null" ] }, "autoexamples": { "type": [ "boolean", "null" ] }, "autotests": { "type": [ "boolean", "null" ] }, "autobenches": { "type": [ "boolean", "null" ] }, "default-run": { "type": [ "string", "null" ] }, "description": { "anyOf": [ { "$ref": "#/$defs/InheritableField" }, { "type": "null" } ] }, "homepage": { "anyOf": [ { "$ref": "#/$defs/InheritableField" }, { "type": "null" } ] }, "documentation": { "anyOf": [ { "$ref": "#/$defs/InheritableField" }, { "type": "null" } ] }, "readme": { "anyOf": [ { "$ref": "#/$defs/InheritableField5" }, { "type": "null" } ] }, "keywords": { "anyOf": [ { "$ref": "#/$defs/InheritableField3" }, { "type": "null" } ] }, "categories": { "anyOf": [ { "$ref": "#/$defs/InheritableField3" }, { "type": "null" } ] }, "license": { "anyOf": [ { "$ref": "#/$defs/InheritableField" }, { "type": "null" } ] }, "license-file": { "anyOf": [ { "$ref": "#/$defs/InheritableField" }, { "type": "null" } ] }, "repository": { "anyOf": [ { "$ref": "#/$defs/InheritableField" }, { "type": "null" } ] }, "resolver": { "type": [ "string", "null" ] }, "metadata": { "anyOf": [ { "$ref": "#/$defs/TomlValue" }, { "type": "null" } ] } } }, "InheritableField": { "description": "An enum that allows for inheriting keys from a workspace in a Cargo.toml.", "anyOf": [ { "description": "The type that is used when not inheriting from a workspace.", "type": "string" }, { "description": "The type when inheriting from a workspace.", "$ref": "#/$defs/TomlInheritedField" } ] }, "TomlInheritedField": { "type": "object", "properties": { "workspace": { "$ref": "#/$defs/WorkspaceValue" } }, "required": [ "workspace" ] }, "WorkspaceValue": { "type": "boolean" }, "InheritableField2": { "description": "An enum that allows for inheriting keys from a workspace in a Cargo.toml.", "anyOf": [ { "description": "The type that is used when not inheriting from a workspace.", "$ref": "#/$defs/SemVer" }, { "description": "The type when inheriting from a workspace.", "$ref": "#/$defs/TomlInheritedField" } ] }, "SemVer": { "type": "string", "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$" }, "InheritableField3": { "description": "An enum that allows for inheriting keys from a workspace in a Cargo.toml.", "anyOf": [ { "description": "The type that is used when not inheriting from a workspace.", "type": "array", "items": { "type": "string" } }, { "description": "The type when inheriting from a workspace.", "$ref": "#/$defs/TomlInheritedField" } ] }, "TomlPackageBuild": { "anyOf": [ { "description": "If build scripts are disabled or enabled.\nIf true, `build.rs` in the root folder will be the build script.", "type": "boolean" }, { "description": "Path of Build Script if there's just one script.", "type": "string" }, { "description": "Vector of paths if multiple build script are to be used.", "type": "array", "items": { "type": "string" } } ] }, "StringOrVec": { "description": "This can be parsed from either a TOML string or array,\nbut is always stored as a vector.", "type": "array", "items": { "type": "string" } }, "InheritableField4": { "description": "An enum that allows for inheriting keys from a workspace in a Cargo.toml.", "anyOf": [ { "description": "The type that is used when not inheriting from a workspace.", "$ref": "#/$defs/VecStringOrBool" }, { "description": "The type when inheriting from a workspace.", "$ref": "#/$defs/TomlInheritedField" } ] }, "VecStringOrBool": { "anyOf": [ { "type": "array", "items": { "type": "string" } }, { "type": "boolean" } ] }, "InheritableField5": { "description": "An enum that allows for inheriting keys from a workspace in a Cargo.toml.", "anyOf": [ { "description": "The type that is used when not inheriting from a workspace.", "$ref": "#/$defs/StringOrBool" }, { "description": "The type when inheriting from a workspace.", "$ref": "#/$defs/TomlInheritedField" } ] }, "StringOrBool": { "anyOf": [ { "type": "string" }, { "type": "boolean" } ] }, "TomlValue": true, "TomlTarget": { "type": "object", "properties": { "name": { "type": [ "string", "null" ] }, "crate-type": { "type": [ "array", "null" ], "items": { "type": "string" } }, "crate_type": { "type": [ "array", "null" ], "items": { "type": "string" } }, "path": { "type": [ "string", "null" ] }, "filename": { "type": [ "string", "null" ] }, "test": { "type": [ "boolean", "null" ] }, "doctest": { "type": [ "boolean", "null" ] }, "bench": { "type": [ "boolean", "null" ] }, "doc": { "type": [ "boolean", "null" ] }, "doc-scrape-examples": { "type": [ "boolean", "null" ] }, "proc-macro": { "type": [ "boolean", "null" ] }, "proc_macro": { "type": [ "boolean", "null" ] }, "harness": { "type": [ "boolean", "null" ] }, "required-features": { "type": [ "array", "null" ], "items": { "type": "string" } }, "edition": { "type": [ "string", "null" ] } } }, "InheritableDependency": { "anyOf": [ { "description": "The type that is used when not inheriting from a workspace.", "$ref": "#/$defs/TomlDependency" }, { "description": "The type when inheriting from a workspace.", "$ref": "#/$defs/TomlInheritedDependency" } ] }, "TomlDependency": { "anyOf": [ { "description": "In the simple format, only a version is specified, eg.\n`package = \"\"`", "type": "string" }, { "description": "The simple format is equivalent to a detailed dependency\nspecifying only a version, eg.\n`package = { version = \"\" }`", "$ref": "#/$defs/TomlDetailedDependency" } ] }, "TomlDetailedDependency": { "type": "object", "properties": { "version": { "type": [ "string", "null" ] }, "registry": { "type": [ "string", "null" ] }, "registry-index": { "description": "The URL of the `registry` field.\nThis is an internal implementation detail. When Cargo creates a\npackage, it replaces `registry` with `registry-index` so that the\nmanifest contains the correct URL. All users won't have the same\nregistry names configured, so Cargo can't rely on just the name for\ncrates published by other users.", "type": [ "string", "null" ] }, "path": { "type": [ "string", "null" ] }, "base": { "type": [ "string", "null" ] }, "git": { "type": [ "string", "null" ] }, "branch": { "type": [ "string", "null" ] }, "tag": { "type": [ "string", "null" ] }, "rev": { "type": [ "string", "null" ] }, "features": { "type": [ "array", "null" ], "items": { "type": "string" } }, "optional": { "type": [ "boolean", "null" ] }, "default-features": { "type": [ "boolean", "null" ] }, "default_features": { "type": [ "boolean", "null" ] }, "package": { "type": [ "string", "null" ] }, "public": { "type": [ "boolean", "null" ] }, "artifact": { "description": "One or more of `bin`, `cdylib`, `staticlib`, `bin:`.", "anyOf": [ { "$ref": "#/$defs/StringOrVec" }, { "type": "null" } ] }, "lib": { "description": "If set, the artifact should also be a dependency", "type": [ "boolean", "null" ] }, "target": { "description": "A platform name, like `x86_64-apple-darwin`", "type": [ "string", "null" ] } } }, "TomlInheritedDependency": { "type": "object", "properties": { "workspace": { "type": "boolean" }, "features": { "type": [ "array", "null" ], "items": { "type": "string" } }, "default-features": { "type": [ "boolean", "null" ] }, "default_features": { "type": [ "boolean", "null" ] }, "optional": { "type": [ "boolean", "null" ] }, "public": { "type": [ "boolean", "null" ] } }, "required": [ "workspace" ] }, "TomlPlatform": { "description": "Corresponds to a `target` entry, but `TomlTarget` is already used.", "type": "object", "properties": { "dependencies": { "type": [ "object", "null" ], "additionalProperties": { "$ref": "#/$defs/InheritableDependency" } }, "build-dependencies": { "type": [ "object", "null" ], "additionalProperties": { "$ref": "#/$defs/InheritableDependency" } }, "build_dependencies": { "type": [ "object", "null" ], "additionalProperties": { "$ref": "#/$defs/InheritableDependency" } }, "dev-dependencies": { "type": [ "object", "null" ], "additionalProperties": { "$ref": "#/$defs/InheritableDependency" } }, "dev_dependencies": { "type": [ "object", "null" ], "additionalProperties": { "$ref": "#/$defs/InheritableDependency" } } } }, "InheritableLints": { "type": "object", "properties": { "workspace": { "type": "boolean" } }, "additionalProperties": { "type": "object", "additionalProperties": { "$ref": "#/$defs/TomlLint" } } }, "TomlLint": { "anyOf": [ { "$ref": "#/$defs/TomlLintLevel" }, { "$ref": "#/$defs/TomlLintConfig" } ] }, "TomlLintLevel": { "type": "string", "enum": [ "forbid", "deny", "warn", "allow" ] }, "TomlLintConfig": { "type": "object", "properties": { "level": { "$ref": "#/$defs/TomlLintLevel" }, "priority": { "type": "integer", "format": "int8", "minimum": -128, "maximum": 127, "default": 0 } }, "required": [ "level" ], "additionalProperties": { "$ref": "#/$defs/TomlValue" } }, "Hints": { "type": "object", "properties": { "mostly-unused": { "anyOf": [ { "$ref": "#/$defs/TomlValue" }, { "type": "null" } ] } } }, "TomlWorkspace": { "type": "object", "properties": { "members": { "type": [ "array", "null" ], "items": { "type": "string" } }, "exclude": { "type": [ "array", "null" ], "items": { "type": "string" } }, "default-members": { "type": [ "array", "null" ], "items": { "type": "string" } }, "resolver": { "type": [ "string", "null" ] }, "metadata": { "anyOf": [ { "$ref": "#/$defs/TomlValue" }, { "type": "null" } ] }, "package": { "anyOf": [ { "$ref": "#/$defs/InheritablePackage" }, { "type": "null" } ] }, "dependencies": { "type": [ "object", "null" ], "additionalProperties": { "$ref": "#/$defs/TomlDependency" } }, "lints": { "type": [ "object", "null" ], "additionalProperties": { "type": "object", "additionalProperties": { "$ref": "#/$defs/TomlLint" } } } } }, "InheritablePackage": { "description": "A group of fields that are inheritable by members of the workspace", "type": "object", "properties": { "version": { "anyOf": [ { "$ref": "#/$defs/SemVer" }, { "type": "null" } ] }, "authors": { "type": [ "array", "null" ], "items": { "type": "string" } }, "description": { "type": [ "string", "null" ] }, "homepage": { "type": [ "string", "null" ] }, "documentation": { "type": [ "string", "null" ] }, "readme": { "anyOf": [ { "$ref": "#/$defs/StringOrBool" }, { "type": "null" } ] }, "keywords": { "type": [ "array", "null" ], "items": { "type": "string" } }, "categories": { "type": [ "array", "null" ], "items": { "type": "string" } }, "license": { "type": [ "string", "null" ] }, "license-file": { "type": [ "string", "null" ] }, "repository": { "type": [ "string", "null" ] }, "publish": { "anyOf": [ { "$ref": "#/$defs/VecStringOrBool" }, { "type": "null" } ] }, "edition": { "type": [ "string", "null" ] }, "badges": { "type": [ "object", "null" ], "additionalProperties": { "type": "object", "additionalProperties": { "type": "string" } } }, "exclude": { "type": [ "array", "null" ], "items": { "type": "string" } }, "include": { "type": [ "array", "null" ], "items": { "type": "string" } }, "rust-version": { "type": [ "string", "null" ] } } }, "TomlProfiles": { "type": "object", "additionalProperties": { "$ref": "#/$defs/TomlProfile" } }, "TomlProfile": { "type": "object", "properties": { "opt-level": { "anyOf": [ { "$ref": "#/$defs/TomlOptLevel" }, { "type": "null" } ], "default": null }, "lto": { "anyOf": [ { "$ref": "#/$defs/StringOrBool" }, { "type": "null" } ], "default": null }, "codegen-backend": { "type": [ "string", "null" ], "default": null }, "codegen-units": { "type": [ "integer", "null" ], "format": "uint32", "minimum": 0, "default": null }, "debug": { "anyOf": [ { "$ref": "#/$defs/TomlDebugInfo" }, { "type": "null" } ], "default": null }, "split-debuginfo": { "type": [ "string", "null" ], "default": null }, "debug-assertions": { "type": [ "boolean", "null" ], "default": null }, "rpath": { "type": [ "boolean", "null" ], "default": null }, "panic": { "type": [ "string", "null" ], "default": null }, "overflow-checks": { "type": [ "boolean", "null" ], "default": null }, "incremental": { "type": [ "boolean", "null" ], "default": null }, "dir-name": { "type": [ "string", "null" ], "default": null }, "inherits": { "type": [ "string", "null" ], "default": null }, "strip": { "anyOf": [ { "$ref": "#/$defs/StringOrBool" }, { "type": "null" } ], "default": null }, "rustflags": { "type": [ "array", "null" ], "items": { "type": "string" }, "default": null }, "package": { "type": [ "object", "null" ], "additionalProperties": { "$ref": "#/$defs/TomlProfile" }, "default": null }, "build-override": { "anyOf": [ { "$ref": "#/$defs/TomlProfile" }, { "type": "null" } ], "default": null }, "trim-paths": { "description": "Unstable feature `-Ztrim-paths`.", "anyOf": [ { "$ref": "#/$defs/TomlTrimPaths" }, { "type": "null" } ], "default": null }, "hint-mostly-unused": { "description": "Unstable feature `hint-mostly-unused`", "type": [ "boolean", "null" ], "default": null } } }, "TomlOptLevel": { "type": "string" }, "TomlDebugInfo": { "type": "string", "enum": [ "None", "LineDirectivesOnly", "LineTablesOnly", "Limited", "Full" ] }, "PackageIdSpec": { "type": "string" }, "TomlTrimPaths": { "anyOf": [ { "type": "array", "items": { "$ref": "#/$defs/TomlTrimPathsValue" } }, { "type": "null" } ] }, "TomlTrimPathsValue": { "type": "string", "enum": [ "diagnostics", "macro", "object" ] } } }cargo-util-schemas-0.10.0/src/core/mod.rs000064400000000000000000000004541046102023000162400ustar 00000000000000mod package_id_spec; mod partial_version; mod source_kind; pub use package_id_spec::PackageIdSpec; pub use package_id_spec::PackageIdSpecError; pub use partial_version::PartialVersion; pub use partial_version::PartialVersionError; pub use source_kind::GitReference; pub use source_kind::SourceKind; cargo-util-schemas-0.10.0/src/core/package_id_spec.rs000064400000000000000000000633731046102023000205530ustar 00000000000000use std::fmt; use semver::Version; use serde::{de, ser}; use url::Url; use crate::core::GitReference; use crate::core::PartialVersion; use crate::core::PartialVersionError; use crate::core::SourceKind; use crate::manifest::PackageName; use crate::restricted_names::NameValidationError; type Result = std::result::Result; /// Some or all of the data required to identify a package: /// /// 1. the package name (a `String`, required) /// 2. the package version (a `Version`, optional) /// 3. the package source (a `Url`, optional) /// /// If any of the optional fields are omitted, then the package ID may be ambiguous, there may be /// more than one package/version/url combo that will match. However, often just the name is /// sufficient to uniquely define a package ID. #[derive(Clone, PartialEq, Eq, Debug, Hash, Ord, PartialOrd)] pub struct PackageIdSpec { name: String, version: Option, url: Option, kind: Option, } impl PackageIdSpec { pub fn new(name: String) -> Self { Self { name, version: None, url: None, kind: None, } } pub fn with_version(mut self, version: PartialVersion) -> Self { self.version = Some(version); self } pub fn with_url(mut self, url: Url) -> Self { self.url = Some(url); self } pub fn with_kind(mut self, kind: SourceKind) -> Self { self.kind = Some(kind); self } /// Parses a spec string and returns a `PackageIdSpec` if the string was valid. /// /// # Examples /// Some examples of valid strings /// /// ``` /// use cargo_util_schemas::core::PackageIdSpec; /// /// let specs = vec![ /// "foo", /// "foo@1.4", /// "foo@1.4.3", /// "foo:1.2.3", /// "https://github.com/rust-lang/crates.io-index#foo", /// "https://github.com/rust-lang/crates.io-index#foo@1.4.3", /// "ssh://git@github.com/rust-lang/foo.git#foo@1.4.3", /// "file:///path/to/my/project/foo", /// "file:///path/to/my/project/foo#1.1.8" /// ]; /// for spec in specs { /// assert!(PackageIdSpec::parse(spec).is_ok()); /// } pub fn parse(spec: &str) -> Result { if spec.contains("://") { if let Ok(url) = Url::parse(spec) { return PackageIdSpec::from_url(url); } } else if spec.contains('/') || spec.contains('\\') { let abs = std::env::current_dir().unwrap_or_default().join(spec); if abs.exists() { let maybe_url = Url::from_file_path(abs) .map_or_else(|_| "a file:// URL".to_string(), |url| url.to_string()); return Err(ErrorKind::MaybeFilePath { spec: spec.into(), maybe_url, } .into()); } } let (name, version) = parse_spec(spec)?.unwrap_or_else(|| (spec.to_owned(), None)); PackageName::new(&name)?; Ok(PackageIdSpec { name: String::from(name), version, url: None, kind: None, }) } /// Tries to convert a valid `Url` to a `PackageIdSpec`. fn from_url(mut url: Url) -> Result { let mut kind = None; if let Some((kind_str, scheme)) = url.scheme().split_once('+') { match kind_str { "git" => { let git_ref = GitReference::from_query(url.query_pairs()); url.set_query(None); kind = Some(SourceKind::Git(git_ref)); url = strip_url_protocol(&url); } "registry" => { if url.query().is_some() { return Err(ErrorKind::UnexpectedQueryString(url).into()); } kind = Some(SourceKind::Registry); url = strip_url_protocol(&url); } "sparse" => { if url.query().is_some() { return Err(ErrorKind::UnexpectedQueryString(url).into()); } kind = Some(SourceKind::SparseRegistry); // Leave `sparse` as part of URL, see `SourceId::new` // url = strip_url_protocol(&url); } "path" => { if url.query().is_some() { return Err(ErrorKind::UnexpectedQueryString(url).into()); } if scheme != "file" { return Err(ErrorKind::UnsupportedPathPlusScheme(scheme.into()).into()); } kind = Some(SourceKind::Path); url = strip_url_protocol(&url); } kind => return Err(ErrorKind::UnsupportedProtocol(kind.into()).into()), } } else { if url.query().is_some() { return Err(ErrorKind::UnexpectedQueryString(url).into()); } } let frag = url.fragment().map(|s| s.to_owned()); url.set_fragment(None); let (name, version) = { let Some(path_name) = url.path_segments().and_then(|mut p| p.next_back()) else { return Err(ErrorKind::MissingUrlPath(url).into()); }; match frag { Some(fragment) => match parse_spec(&fragment)? { Some((name, ver)) => (name, ver), None => { if fragment.chars().next().unwrap().is_alphabetic() { (String::from(fragment.as_str()), None) } else { let version = fragment.parse::()?; (String::from(path_name), Some(version)) } } }, None => (String::from(path_name), None), } }; PackageName::new(&name)?; Ok(PackageIdSpec { name, version, url: Some(url), kind, }) } pub fn name(&self) -> &str { self.name.as_str() } /// Full `semver::Version`, if present pub fn version(&self) -> Option { self.version.as_ref().and_then(|v| v.to_version()) } pub fn partial_version(&self) -> Option<&PartialVersion> { self.version.as_ref() } pub fn url(&self) -> Option<&Url> { self.url.as_ref() } pub fn set_url(&mut self, url: Url) { self.url = Some(url); } pub fn kind(&self) -> Option<&SourceKind> { self.kind.as_ref() } pub fn set_kind(&mut self, kind: SourceKind) { self.kind = Some(kind); } } fn parse_spec(spec: &str) -> Result)>> { let Some((name, ver)) = spec .rsplit_once('@') .or_else(|| spec.rsplit_once(':').filter(|(n, _)| !n.ends_with(':'))) else { return Ok(None); }; let name = name.to_owned(); let ver = ver.parse::()?; Ok(Some((name, Some(ver)))) } fn strip_url_protocol(url: &Url) -> Url { // Ridiculous hoop because `Url::set_scheme` errors when changing to http/https let raw = url.to_string(); raw.split_once('+').unwrap().1.parse().unwrap() } impl fmt::Display for PackageIdSpec { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut printed_name = false; match self.url { Some(ref url) => { if let Some(protocol) = self.kind.as_ref().and_then(|k| k.protocol()) { write!(f, "{protocol}+")?; } write!(f, "{}", url)?; if let Some(SourceKind::Git(git_ref)) = self.kind.as_ref() { if let Some(pretty) = git_ref.pretty_ref(true) { write!(f, "?{}", pretty)?; } } if url.path_segments().unwrap().next_back().unwrap() != &*self.name { printed_name = true; write!(f, "#{}", self.name)?; } } None => { printed_name = true; write!(f, "{}", self.name)?; } } if let Some(ref v) = self.version { write!(f, "{}{}", if printed_name { "@" } else { "#" }, v)?; } Ok(()) } } impl ser::Serialize for PackageIdSpec { fn serialize(&self, s: S) -> std::result::Result where S: ser::Serializer, { self.to_string().serialize(s) } } impl<'de> de::Deserialize<'de> for PackageIdSpec { fn deserialize(d: D) -> std::result::Result where D: de::Deserializer<'de>, { let string = String::deserialize(d)?; PackageIdSpec::parse(&string).map_err(de::Error::custom) } } #[cfg(feature = "unstable-schema")] impl schemars::JsonSchema for PackageIdSpec { fn schema_name() -> std::borrow::Cow<'static, str> { "PackageIdSpec".into() } fn json_schema(generator: &mut schemars::SchemaGenerator) -> schemars::Schema { ::json_schema(generator) } } #[derive(Debug, thiserror::Error)] #[error(transparent)] pub struct PackageIdSpecError(#[from] ErrorKind); impl From for PackageIdSpecError { fn from(value: PartialVersionError) -> Self { ErrorKind::PartialVersion(value).into() } } impl From for PackageIdSpecError { fn from(value: NameValidationError) -> Self { ErrorKind::NameValidation(value).into() } } /// Non-public error kind for [`PackageIdSpecError`]. #[non_exhaustive] #[derive(Debug, thiserror::Error)] enum ErrorKind { #[error("unsupported source protocol: {0}")] UnsupportedProtocol(String), #[error("`path+{0}` is unsupported; `path+file` and `file` schemes are supported")] UnsupportedPathPlusScheme(String), #[error("cannot have a query string in a pkgid: {0}")] UnexpectedQueryString(Url), #[error("pkgid urls must have at least one path component: {0}")] MissingUrlPath(Url), #[error("package ID specification `{spec}` looks like a file path, maybe try {maybe_url}")] MaybeFilePath { spec: String, maybe_url: String }, #[error(transparent)] NameValidation(#[from] crate::restricted_names::NameValidationError), #[error(transparent)] PartialVersion(#[from] crate::core::PartialVersionError), } #[cfg(test)] mod tests { use super::ErrorKind; use super::PackageIdSpec; use crate::core::{GitReference, SourceKind}; use url::Url; #[track_caller] fn ok(spec: &str, expected: PackageIdSpec, expected_rendered: &str) { let parsed = PackageIdSpec::parse(spec).unwrap(); assert_eq!(parsed, expected); let rendered = parsed.to_string(); assert_eq!(rendered, expected_rendered); let reparsed = PackageIdSpec::parse(&rendered).unwrap(); assert_eq!(reparsed, expected); } macro_rules! err { ($spec:expr, $expected:pat) => { let err = PackageIdSpec::parse($spec).unwrap_err(); let kind = err.0; assert!( matches!(kind, $expected), "`{}` parse error mismatch, got {kind:?}", $spec ); }; } #[test] fn good_parsing() { ok( "https://crates.io/foo", PackageIdSpec { name: String::from("foo"), version: None, url: Some(Url::parse("https://crates.io/foo").unwrap()), kind: None, }, "https://crates.io/foo", ); ok( "https://crates.io/foo#1.2.3", PackageIdSpec { name: String::from("foo"), version: Some("1.2.3".parse().unwrap()), url: Some(Url::parse("https://crates.io/foo").unwrap()), kind: None, }, "https://crates.io/foo#1.2.3", ); ok( "https://crates.io/foo#1.2", PackageIdSpec { name: String::from("foo"), version: Some("1.2".parse().unwrap()), url: Some(Url::parse("https://crates.io/foo").unwrap()), kind: None, }, "https://crates.io/foo#1.2", ); ok( "https://crates.io/foo#bar:1.2.3", PackageIdSpec { name: String::from("bar"), version: Some("1.2.3".parse().unwrap()), url: Some(Url::parse("https://crates.io/foo").unwrap()), kind: None, }, "https://crates.io/foo#bar@1.2.3", ); ok( "https://crates.io/foo#bar@1.2.3", PackageIdSpec { name: String::from("bar"), version: Some("1.2.3".parse().unwrap()), url: Some(Url::parse("https://crates.io/foo").unwrap()), kind: None, }, "https://crates.io/foo#bar@1.2.3", ); ok( "https://crates.io/foo#bar@1.2", PackageIdSpec { name: String::from("bar"), version: Some("1.2".parse().unwrap()), url: Some(Url::parse("https://crates.io/foo").unwrap()), kind: None, }, "https://crates.io/foo#bar@1.2", ); ok( "registry+https://crates.io/foo#bar@1.2", PackageIdSpec { name: String::from("bar"), version: Some("1.2".parse().unwrap()), url: Some(Url::parse("https://crates.io/foo").unwrap()), kind: Some(SourceKind::Registry), }, "registry+https://crates.io/foo#bar@1.2", ); ok( "sparse+https://crates.io/foo#bar@1.2", PackageIdSpec { name: String::from("bar"), version: Some("1.2".parse().unwrap()), url: Some(Url::parse("sparse+https://crates.io/foo").unwrap()), kind: Some(SourceKind::SparseRegistry), }, "sparse+https://crates.io/foo#bar@1.2", ); ok( "foo", PackageIdSpec { name: String::from("foo"), version: None, url: None, kind: None, }, "foo", ); ok( "foo::bar", PackageIdSpec { name: String::from("foo::bar"), version: None, url: None, kind: None, }, "foo::bar", ); ok( "foo:1.2.3", PackageIdSpec { name: String::from("foo"), version: Some("1.2.3".parse().unwrap()), url: None, kind: None, }, "foo@1.2.3", ); ok( "foo::bar:1.2.3", PackageIdSpec { name: String::from("foo::bar"), version: Some("1.2.3".parse().unwrap()), url: None, kind: None, }, "foo::bar@1.2.3", ); ok( "foo@1.2.3", PackageIdSpec { name: String::from("foo"), version: Some("1.2.3".parse().unwrap()), url: None, kind: None, }, "foo@1.2.3", ); ok( "foo::bar@1.2.3", PackageIdSpec { name: String::from("foo::bar"), version: Some("1.2.3".parse().unwrap()), url: None, kind: None, }, "foo::bar@1.2.3", ); ok( "foo@1.2", PackageIdSpec { name: String::from("foo"), version: Some("1.2".parse().unwrap()), url: None, kind: None, }, "foo@1.2", ); // pkgid-spec.md ok( "regex", PackageIdSpec { name: String::from("regex"), version: None, url: None, kind: None, }, "regex", ); ok( "regex@1.4", PackageIdSpec { name: String::from("regex"), version: Some("1.4".parse().unwrap()), url: None, kind: None, }, "regex@1.4", ); ok( "regex@1.4.3", PackageIdSpec { name: String::from("regex"), version: Some("1.4.3".parse().unwrap()), url: None, kind: None, }, "regex@1.4.3", ); ok( "https://github.com/rust-lang/crates.io-index#regex", PackageIdSpec { name: String::from("regex"), version: None, url: Some(Url::parse("https://github.com/rust-lang/crates.io-index").unwrap()), kind: None, }, "https://github.com/rust-lang/crates.io-index#regex", ); ok( "https://github.com/rust-lang/crates.io-index#regex@1.4.3", PackageIdSpec { name: String::from("regex"), version: Some("1.4.3".parse().unwrap()), url: Some(Url::parse("https://github.com/rust-lang/crates.io-index").unwrap()), kind: None, }, "https://github.com/rust-lang/crates.io-index#regex@1.4.3", ); ok( "sparse+https://github.com/rust-lang/crates.io-index#regex@1.4.3", PackageIdSpec { name: String::from("regex"), version: Some("1.4.3".parse().unwrap()), url: Some( Url::parse("sparse+https://github.com/rust-lang/crates.io-index").unwrap(), ), kind: Some(SourceKind::SparseRegistry), }, "sparse+https://github.com/rust-lang/crates.io-index#regex@1.4.3", ); ok( "https://github.com/rust-lang/cargo#0.52.0", PackageIdSpec { name: String::from("cargo"), version: Some("0.52.0".parse().unwrap()), url: Some(Url::parse("https://github.com/rust-lang/cargo").unwrap()), kind: None, }, "https://github.com/rust-lang/cargo#0.52.0", ); ok( "https://github.com/rust-lang/cargo#cargo-platform@0.1.2", PackageIdSpec { name: String::from("cargo-platform"), version: Some("0.1.2".parse().unwrap()), url: Some(Url::parse("https://github.com/rust-lang/cargo").unwrap()), kind: None, }, "https://github.com/rust-lang/cargo#cargo-platform@0.1.2", ); ok( "ssh://git@github.com/rust-lang/regex.git#regex@1.4.3", PackageIdSpec { name: String::from("regex"), version: Some("1.4.3".parse().unwrap()), url: Some(Url::parse("ssh://git@github.com/rust-lang/regex.git").unwrap()), kind: None, }, "ssh://git@github.com/rust-lang/regex.git#regex@1.4.3", ); ok( "git+ssh://git@github.com/rust-lang/regex.git#regex@1.4.3", PackageIdSpec { name: String::from("regex"), version: Some("1.4.3".parse().unwrap()), url: Some(Url::parse("ssh://git@github.com/rust-lang/regex.git").unwrap()), kind: Some(SourceKind::Git(GitReference::DefaultBranch)), }, "git+ssh://git@github.com/rust-lang/regex.git#regex@1.4.3", ); ok( "git+ssh://git@github.com/rust-lang/regex.git?branch=dev#regex@1.4.3", PackageIdSpec { name: String::from("regex"), version: Some("1.4.3".parse().unwrap()), url: Some(Url::parse("ssh://git@github.com/rust-lang/regex.git").unwrap()), kind: Some(SourceKind::Git(GitReference::Branch("dev".to_owned()))), }, "git+ssh://git@github.com/rust-lang/regex.git?branch=dev#regex@1.4.3", ); ok( "file:///path/to/my/project/foo", PackageIdSpec { name: String::from("foo"), version: None, url: Some(Url::parse("file:///path/to/my/project/foo").unwrap()), kind: None, }, "file:///path/to/my/project/foo", ); ok( "file:///path/to/my/project/foo::bar", PackageIdSpec { name: String::from("foo::bar"), version: None, url: Some(Url::parse("file:///path/to/my/project/foo::bar").unwrap()), kind: None, }, "file:///path/to/my/project/foo::bar", ); ok( "file:///path/to/my/project/foo#1.1.8", PackageIdSpec { name: String::from("foo"), version: Some("1.1.8".parse().unwrap()), url: Some(Url::parse("file:///path/to/my/project/foo").unwrap()), kind: None, }, "file:///path/to/my/project/foo#1.1.8", ); ok( "path+file:///path/to/my/project/foo#1.1.8", PackageIdSpec { name: String::from("foo"), version: Some("1.1.8".parse().unwrap()), url: Some(Url::parse("file:///path/to/my/project/foo").unwrap()), kind: Some(SourceKind::Path), }, "path+file:///path/to/my/project/foo#1.1.8", ); ok( "path+file:///path/to/my/project/foo#bar", PackageIdSpec { name: String::from("bar"), version: None, url: Some(Url::parse("file:///path/to/my/project/foo").unwrap()), kind: Some(SourceKind::Path), }, "path+file:///path/to/my/project/foo#bar", ); ok( "path+file:///path/to/my/project/foo#foo::bar", PackageIdSpec { name: String::from("foo::bar"), version: None, url: Some(Url::parse("file:///path/to/my/project/foo").unwrap()), kind: Some(SourceKind::Path), }, "path+file:///path/to/my/project/foo#foo::bar", ); ok( "path+file:///path/to/my/project/foo#bar:1.1.8", PackageIdSpec { name: String::from("bar"), version: Some("1.1.8".parse().unwrap()), url: Some(Url::parse("file:///path/to/my/project/foo").unwrap()), kind: Some(SourceKind::Path), }, "path+file:///path/to/my/project/foo#bar@1.1.8", ); ok( "path+file:///path/to/my/project/foo#foo::bar:1.1.8", PackageIdSpec { name: String::from("foo::bar"), version: Some("1.1.8".parse().unwrap()), url: Some(Url::parse("file:///path/to/my/project/foo").unwrap()), kind: Some(SourceKind::Path), }, "path+file:///path/to/my/project/foo#foo::bar@1.1.8", ); ok( "path+file:///path/to/my/project/foo#bar@1.1.8", PackageIdSpec { name: String::from("bar"), version: Some("1.1.8".parse().unwrap()), url: Some(Url::parse("file:///path/to/my/project/foo").unwrap()), kind: Some(SourceKind::Path), }, "path+file:///path/to/my/project/foo#bar@1.1.8", ); ok( "path+file:///path/to/my/project/foo#foo::bar@1.1.8", PackageIdSpec { name: String::from("foo::bar"), version: Some("1.1.8".parse().unwrap()), url: Some(Url::parse("file:///path/to/my/project/foo").unwrap()), kind: Some(SourceKind::Path), }, "path+file:///path/to/my/project/foo#foo::bar@1.1.8", ); } #[test] fn bad_parsing() { err!("baz:", ErrorKind::PartialVersion(_)); err!("baz:*", ErrorKind::PartialVersion(_)); err!("baz@", ErrorKind::PartialVersion(_)); err!("baz@*", ErrorKind::PartialVersion(_)); err!("baz@^1.0", ErrorKind::PartialVersion(_)); err!("https://baz:1.0", ErrorKind::NameValidation(_)); err!("https://#baz:1.0", ErrorKind::NameValidation(_)); err!( "foobar+https://github.com/rust-lang/crates.io-index", ErrorKind::UnsupportedProtocol(_) ); err!( "path+https://github.com/rust-lang/crates.io-index", ErrorKind::UnsupportedPathPlusScheme(_) ); // Only `git+` can use `?` err!( "file:///path/to/my/project/foo?branch=dev", ErrorKind::UnexpectedQueryString(_) ); err!( "path+file:///path/to/my/project/foo?branch=dev", ErrorKind::UnexpectedQueryString(_) ); err!( "registry+https://github.com/rust-lang/cargo?branch=dev#0.52.0", ErrorKind::UnexpectedQueryString(_) ); err!( "sparse+https://github.com/rust-lang/cargo?branch=dev#0.52.0", ErrorKind::UnexpectedQueryString(_) ); err!("@1.2.3", ErrorKind::NameValidation(_)); err!("registry+https://github.com", ErrorKind::NameValidation(_)); err!("https://crates.io/1foo#1.2.3", ErrorKind::NameValidation(_)); } } cargo-util-schemas-0.10.0/src/core/partial_version.rs000064400000000000000000000174031046102023000206640ustar 00000000000000use std::fmt::{self, Display}; use semver::{Comparator, Version, VersionReq}; use serde_untagged::UntaggedEnumVisitor; #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Debug)] pub struct PartialVersion { pub major: u64, pub minor: Option, pub patch: Option, pub pre: Option, pub build: Option, } impl PartialVersion { pub fn to_version(&self) -> Option { Some(Version { major: self.major, minor: self.minor?, patch: self.patch?, pre: self.pre.clone().unwrap_or_default(), build: self.build.clone().unwrap_or_default(), }) } pub fn to_caret_req(&self) -> VersionReq { VersionReq { comparators: vec![Comparator { op: semver::Op::Caret, major: self.major, minor: self.minor, patch: self.patch, pre: self.pre.as_ref().cloned().unwrap_or_default(), }], } } /// Check if this matches a version, including build metadata /// /// Build metadata does not affect version precedence but may be necessary for uniquely /// identifying a package. pub fn matches(&self, version: &Version) -> bool { if !version.pre.is_empty() && self.pre.is_none() { // Pre-release versions must be explicitly opted into, if for no other reason than to // give us room to figure out and define the semantics return false; } self.major == version.major && self.minor.map(|f| f == version.minor).unwrap_or(true) && self.patch.map(|f| f == version.patch).unwrap_or(true) && self.pre.as_ref().map(|f| f == &version.pre).unwrap_or(true) && self .build .as_ref() .map(|f| f == &version.build) .unwrap_or(true) } } impl From for PartialVersion { fn from(ver: semver::Version) -> Self { let pre = if ver.pre.is_empty() { None } else { Some(ver.pre) }; let build = if ver.build.is_empty() { None } else { Some(ver.build) }; Self { major: ver.major, minor: Some(ver.minor), patch: Some(ver.patch), pre, build, } } } impl std::str::FromStr for PartialVersion { type Err = PartialVersionError; fn from_str(value: &str) -> Result { match semver::Version::parse(value) { Ok(ver) => Ok(ver.into()), Err(_) => { // HACK: Leverage `VersionReq` for partial version parsing let mut version_req = match semver::VersionReq::parse(value) { Ok(req) => req, Err(_) if value.contains('-') => return Err(ErrorKind::Prerelease.into()), Err(_) if value.contains('+') => return Err(ErrorKind::BuildMetadata.into()), Err(_) => return Err(ErrorKind::Unexpected.into()), }; if version_req.comparators.len() != 1 { return Err(ErrorKind::VersionReq.into()); } let comp = version_req.comparators.pop().unwrap(); if comp.op != semver::Op::Caret { return Err(ErrorKind::VersionReq.into()); } else if value.starts_with('^') { // Can't distinguish between `^` present or not return Err(ErrorKind::VersionReq.into()); } let pre = if comp.pre.is_empty() { None } else { Some(comp.pre) }; Ok(Self { major: comp.major, minor: comp.minor, patch: comp.patch, pre, build: None, }) } } } } impl Display for PartialVersion { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let major = self.major; write!(f, "{major}")?; if let Some(minor) = self.minor { write!(f, ".{minor}")?; } if let Some(patch) = self.patch { write!(f, ".{patch}")?; } if let Some(pre) = self.pre.as_ref() { write!(f, "-{pre}")?; } if let Some(build) = self.build.as_ref() { write!(f, "+{build}")?; } Ok(()) } } impl serde::Serialize for PartialVersion { fn serialize(&self, serializer: S) -> Result where S: serde::Serializer, { serializer.collect_str(self) } } impl<'de> serde::Deserialize<'de> for PartialVersion { fn deserialize(deserializer: D) -> Result where D: serde::Deserializer<'de>, { UntaggedEnumVisitor::new() .expecting("SemVer version") .string(|value| value.parse().map_err(serde::de::Error::custom)) .deserialize(deserializer) } } /// Error parsing a [`PartialVersion`]. #[derive(Debug, thiserror::Error)] #[error(transparent)] pub struct PartialVersionError(#[from] ErrorKind); /// Non-public error kind for [`PartialVersionError`]. #[non_exhaustive] #[derive(Debug, thiserror::Error)] enum ErrorKind { #[error("unexpected version requirement, expected a version like \"1.32\"")] VersionReq, #[error("unexpected prerelease field, expected a version like \"1.32\"")] Prerelease, #[error("unexpected build field, expected a version like \"1.32\"")] BuildMetadata, #[error("expected a version like \"1.32\"")] Unexpected, } #[cfg(test)] mod test { use super::*; use snapbox::prelude::*; use snapbox::str; #[test] fn parse_success() { let cases = &[ // Valid pre-release ("1.43.0-beta.1", str!["1.43.0-beta.1"]), // Valid pre-release with wildcard ("1.43.0-beta.1.x", str!["1.43.0-beta.1.x"]), ]; for (input, expected) in cases { let actual: Result = input.parse(); let actual = match actual { Ok(result) => result.to_string(), Err(err) => format!("didn't pass: {err}"), }; snapbox::assert_data_eq!(actual, expected.clone().raw()); } } #[test] fn parse_errors() { let cases = &[ // Disallow caret ( "^1.43", str![[r#"unexpected version requirement, expected a version like "1.32""#]], ), // Bad pre-release ( "1.43-beta.1", str![[r#"unexpected prerelease field, expected a version like "1.32""#]], ), // Weird wildcard ( "x", str![[r#"unexpected version requirement, expected a version like "1.32""#]], ), ( "1.x", str![[r#"unexpected version requirement, expected a version like "1.32""#]], ), ( "1.1.x", str![[r#"unexpected version requirement, expected a version like "1.32""#]], ), // Non-sense ("foodaddle", str![[r#"expected a version like "1.32""#]]), ]; for (input, expected) in cases { let actual: Result = input.parse(); let actual = match actual { Ok(result) => format!("didn't fail: {result:?}"), Err(err) => err.to_string(), }; snapbox::assert_data_eq!(actual, expected.clone().raw()); } } } cargo-util-schemas-0.10.0/src/core/source_kind.rs000064400000000000000000000125701046102023000177700ustar 00000000000000use std::cmp::Ordering; /// The possible kinds of code source. #[derive(Debug, Clone, PartialEq, Eq)] pub enum SourceKind { /// A git repository. Git(GitReference), /// A local path. Path, /// A remote registry. Registry, /// A sparse registry. SparseRegistry, /// A local filesystem-based registry. LocalRegistry, /// A directory-based registry. Directory, } // The hash here is important for what folder packages get downloaded into. // Changes trigger all users to download another copy of their crates. // So the `stable_hash` test checks that we only change it intentionally. // We implement hash manually to callout the stability impact. impl std::hash::Hash for SourceKind { fn hash(&self, state: &mut H) { core::mem::discriminant(self).hash(state); if let SourceKind::Git(git) = self { git.hash(state); } } } impl SourceKind { pub fn protocol(&self) -> Option<&str> { match self { SourceKind::Path => Some("path"), SourceKind::Git(_) => Some("git"), SourceKind::Registry => Some("registry"), // Sparse registry URL already includes the `sparse+` prefix, see `SourceId::new` SourceKind::SparseRegistry => None, SourceKind::LocalRegistry => Some("local-registry"), SourceKind::Directory => Some("directory"), } } } // The ordering here is important for how packages are serialized into lock files. // We implement it manually to callout the stability guarantee. // See https://github.com/rust-lang/cargo/pull/9397 for the history. impl Ord for SourceKind { fn cmp(&self, other: &SourceKind) -> Ordering { match (self, other) { (SourceKind::Path, SourceKind::Path) => Ordering::Equal, (SourceKind::Path, _) => Ordering::Less, (_, SourceKind::Path) => Ordering::Greater, (SourceKind::Registry, SourceKind::Registry) => Ordering::Equal, (SourceKind::Registry, _) => Ordering::Less, (_, SourceKind::Registry) => Ordering::Greater, (SourceKind::SparseRegistry, SourceKind::SparseRegistry) => Ordering::Equal, (SourceKind::SparseRegistry, _) => Ordering::Less, (_, SourceKind::SparseRegistry) => Ordering::Greater, (SourceKind::LocalRegistry, SourceKind::LocalRegistry) => Ordering::Equal, (SourceKind::LocalRegistry, _) => Ordering::Less, (_, SourceKind::LocalRegistry) => Ordering::Greater, (SourceKind::Directory, SourceKind::Directory) => Ordering::Equal, (SourceKind::Directory, _) => Ordering::Less, (_, SourceKind::Directory) => Ordering::Greater, (SourceKind::Git(a), SourceKind::Git(b)) => a.cmp(b), } } } /// Forwards to `Ord` impl PartialOrd for SourceKind { fn partial_cmp(&self, other: &SourceKind) -> Option { Some(self.cmp(other)) } } /// Information to find a specific commit in a Git repository. #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum GitReference { /// From a tag. Tag(String), /// From a branch. Branch(String), /// From a specific revision. Can be a commit hash (either short or full), /// or a named reference like `refs/pull/493/head`. Rev(String), /// The default branch of the repository, the reference named `HEAD`. DefaultBranch, } impl GitReference { pub fn from_query( query_pairs: impl Iterator, impl AsRef)>, ) -> Self { let mut reference = GitReference::DefaultBranch; for (k, v) in query_pairs { let v = v.as_ref(); match k.as_ref() { // Map older 'ref' to branch. "branch" | "ref" => reference = GitReference::Branch(v.to_owned()), "rev" => reference = GitReference::Rev(v.to_owned()), "tag" => reference = GitReference::Tag(v.to_owned()), _ => {} } } reference } /// Returns a `Display`able view of this git reference, or None if using /// the head of the default branch pub fn pretty_ref(&self, url_encoded: bool) -> Option> { match self { GitReference::DefaultBranch => None, _ => Some(PrettyRef { inner: self, url_encoded, }), } } } /// A git reference that can be `Display`ed pub struct PrettyRef<'a> { inner: &'a GitReference, url_encoded: bool, } impl<'a> std::fmt::Display for PrettyRef<'a> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let value: &str; match self.inner { GitReference::Branch(s) => { write!(f, "branch=")?; value = s; } GitReference::Tag(s) => { write!(f, "tag=")?; value = s; } GitReference::Rev(s) => { write!(f, "rev=")?; value = s; } GitReference::DefaultBranch => unreachable!(), } if self.url_encoded { for value in url::form_urlencoded::byte_serialize(value.as_bytes()) { write!(f, "{value}")?; } } else { write!(f, "{value}")?; } Ok(()) } } cargo-util-schemas-0.10.0/src/index.rs000064400000000000000000000145321046102023000156420ustar 00000000000000use crate::manifest::RustVersion; use semver::Version; use serde::{Deserialize, Serialize}; use std::{borrow::Cow, collections::BTreeMap}; /// A single line in the index representing a single version of a package. #[derive(Deserialize, Serialize)] #[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))] pub struct IndexPackage<'a> { /// Name of the package. #[serde(borrow)] pub name: Cow<'a, str>, /// The version of this dependency. pub vers: Version, /// All kinds of direct dependencies of the package, including dev and /// build dependencies. #[serde(borrow)] pub deps: Vec>, /// Set of features defined for the package, i.e., `[features]` table. #[serde(default)] pub features: BTreeMap, Vec>>, /// This field contains features with new, extended syntax. Specifically, /// namespaced features (`dep:`) and weak dependencies (`pkg?/feat`). /// /// This is separated from `features` because versions older than 1.19 /// will fail to load due to not being able to parse the new syntax, even /// with a `Cargo.lock` file. pub features2: Option, Vec>>>, /// Checksum for verifying the integrity of the corresponding downloaded package. pub cksum: String, /// If `true`, Cargo will skip this version when resolving. /// /// This was added in 2014. Everything in the crates.io index has this set /// now, so this probably doesn't need to be an option anymore. pub yanked: Option, /// Native library name this package links to. /// /// Added early 2018 (see ), /// can be `None` if published before then. pub links: Option>, /// Required version of rust /// /// Corresponds to `package.rust-version`. /// /// Added in 2023 (see ), /// can be `None` if published before then or if not set in the manifest. #[cfg_attr(feature = "unstable-schema", schemars(with = "Option"))] pub rust_version: Option, /// The schema version for this entry. /// /// If this is None, it defaults to version `1`. Entries with unknown /// versions are ignored. /// /// Version `2` schema adds the `features2` field. /// /// Version `3` schema adds `artifact`, `bindep_targes`, and `lib` for /// artifact dependencies support. /// /// This provides a method to safely introduce changes to index entries /// and allow older versions of cargo to ignore newer entries it doesn't /// understand. This is honored as of 1.51, so unfortunately older /// versions will ignore it, and potentially misinterpret version 2 and /// newer entries. /// /// The intent is that versions older than 1.51 will work with a /// pre-existing `Cargo.lock`, but they may not correctly process `cargo /// update` or build a lock from scratch. In that case, cargo may /// incorrectly select a new package that uses a new index schema. A /// workaround is to downgrade any packages that are incompatible with the /// `--precise` flag of `cargo update`. pub v: Option, } /// A dependency as encoded in the [`IndexPackage`] index JSON. #[derive(Deserialize, Serialize, Clone)] #[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))] pub struct RegistryDependency<'a> { /// Name of the dependency. If the dependency is renamed, the original /// would be stored in [`RegistryDependency::package`]. #[serde(borrow)] pub name: Cow<'a, str>, /// The SemVer requirement for this dependency. #[serde(borrow)] pub req: Cow<'a, str>, /// Set of features enabled for this dependency. #[serde(default)] pub features: Vec>, /// Whether or not this is an optional dependency. #[serde(default)] pub optional: bool, /// Whether or not default features are enabled. #[serde(default = "default_true")] pub default_features: bool, /// The target platform for this dependency. pub target: Option>, /// The dependency kind. "dev", "build", and "normal". pub kind: Option>, /// The URL of the index of the registry where this dependency is from. /// `None` if it is from the same index. pub registry: Option>, /// The original name if the dependency is renamed. pub package: Option>, /// Whether or not this is a public dependency. Unstable. See [RFC 1977]. /// /// [RFC 1977]: https://rust-lang.github.io/rfcs/1977-public-private-dependencies.html pub public: Option, /// The artifacts to build from this dependency. pub artifact: Option>>, /// The target for bindep. pub bindep_target: Option>, /// Whether or not this is a library dependency. #[serde(default)] pub lib: bool, } fn default_true() -> bool { true } #[test] fn escaped_char_in_index_json_blob() { let _: IndexPackage<'_> = serde_json::from_str( r#"{"name":"a","vers":"0.0.1","deps":[],"cksum":"bae3","features":{}}"#, ) .unwrap(); let _: IndexPackage<'_> = serde_json::from_str( r#"{"name":"a","vers":"0.0.1","deps":[],"cksum":"bae3","features":{"test":["k","q"]},"links":"a-sys"}"# ).unwrap(); // Now we add escaped cher all the places they can go // these are not valid, but it should error later than json parsing let _: IndexPackage<'_> = serde_json::from_str( r#"{ "name":"This name has a escaped cher in it \n\t\" ", "vers":"0.0.1", "deps":[{ "name": " \n\t\" ", "req": " \n\t\" ", "features": [" \n\t\" "], "optional": true, "default_features": true, "target": " \n\t\" ", "kind": " \n\t\" ", "registry": " \n\t\" " }], "cksum":"bae3", "features":{"test \n\t\" ":["k \n\t\" ","q \n\t\" "]}, "links":" \n\t\" "}"#, ) .unwrap(); } #[cfg(feature = "unstable-schema")] #[test] fn dump_index_schema() { let schema = schemars::schema_for!(crate::index::IndexPackage<'_>); let dump = serde_json::to_string_pretty(&schema).unwrap(); snapbox::assert_data_eq!(dump, snapbox::file!("../index.schema.json").raw()); } cargo-util-schemas-0.10.0/src/lib.rs000064400000000000000000000010731046102023000152750ustar 00000000000000//! Low-level Cargo format schemas //! //! This is types with logic mostly focused on `serde` and `FromStr` for use in reading files and //! parsing command-lines. //! Any logic for getting final semantics from these will likely need other tools to process, like //! `cargo metadata`. //! //! > This crate is maintained by the Cargo team for use by the wider //! > ecosystem. This crate follows semver compatibility for its APIs. pub mod core; pub mod index; pub mod manifest; pub mod messages; #[cfg(feature = "unstable-schema")] pub mod schema; mod restricted_names; cargo-util-schemas-0.10.0/src/manifest/mod.rs000064400000000000000000001644101046102023000171210ustar 00000000000000//! `Cargo.toml` / Manifest schema definition //! //! ## Style //! //! - Fields duplicated for an alias will have an accessor with the primary field's name //! - Keys that exist for bookkeeping but don't correspond to the schema have a `_` prefix use std::collections::BTreeMap; use std::collections::BTreeSet; #[cfg(feature = "unstable-schema")] use std::collections::HashMap; use std::fmt::{self, Display, Write}; use std::path::PathBuf; use std::str; use serde::de::{self, IntoDeserializer as _, Unexpected}; use serde::ser; use serde::{Deserialize, Serialize}; use serde_untagged::UntaggedEnumVisitor; use crate::core::PackageIdSpec; use crate::restricted_names; mod rust_version; pub use crate::restricted_names::NameValidationError; pub use rust_version::RustVersion; pub use rust_version::RustVersionError; #[cfg(feature = "unstable-schema")] use crate::schema::TomlValueWrapper; /// This type is used to deserialize `Cargo.toml` files. #[derive(Default, Clone, Debug, Deserialize, Serialize)] #[serde(rename_all = "kebab-case")] #[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))] pub struct TomlManifest { pub cargo_features: Option>, // Update `requires_package` when adding new package-specific fields pub package: Option>, pub project: Option>, pub badges: Option>>, pub features: Option>>, pub lib: Option, pub bin: Option>, pub example: Option>, pub test: Option>, pub bench: Option>, pub dependencies: Option>, pub dev_dependencies: Option>, #[serde(rename = "dev_dependencies")] pub dev_dependencies2: Option>, pub build_dependencies: Option>, #[serde(rename = "build_dependencies")] pub build_dependencies2: Option>, pub target: Option>, pub lints: Option, pub hints: Option, pub workspace: Option, pub profile: Option, pub patch: Option>>, pub replace: Option>, /// Report unused keys (see also nested `_unused_keys`) /// Note: this is populated by the caller, rather than automatically #[serde(skip)] pub _unused_keys: BTreeSet, } impl TomlManifest { pub fn requires_package(&self) -> impl Iterator { [ self.badges.as_ref().map(|_| "badges"), self.features.as_ref().map(|_| "features"), self.lib.as_ref().map(|_| "lib"), self.bin.as_ref().map(|_| "bin"), self.example.as_ref().map(|_| "example"), self.test.as_ref().map(|_| "test"), self.bench.as_ref().map(|_| "bench"), self.dependencies.as_ref().map(|_| "dependencies"), self.dev_dependencies().as_ref().map(|_| "dev-dependencies"), self.build_dependencies() .as_ref() .map(|_| "build-dependencies"), self.target.as_ref().map(|_| "target"), self.lints.as_ref().map(|_| "lints"), self.hints.as_ref().map(|_| "hints"), ] .into_iter() .flatten() } pub fn has_profiles(&self) -> bool { self.profile.is_some() } pub fn package(&self) -> Option<&Box> { self.package.as_ref().or(self.project.as_ref()) } pub fn dev_dependencies(&self) -> Option<&BTreeMap> { self.dev_dependencies .as_ref() .or(self.dev_dependencies2.as_ref()) } pub fn build_dependencies(&self) -> Option<&BTreeMap> { self.build_dependencies .as_ref() .or(self.build_dependencies2.as_ref()) } pub fn features(&self) -> Option<&BTreeMap>> { self.features.as_ref() } pub fn normalized_lints(&self) -> Result, UnresolvedError> { self.lints.as_ref().map(|l| l.normalized()).transpose() } } #[derive(Debug, Default, Deserialize, Serialize, Clone)] #[serde(rename_all = "kebab-case")] #[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))] pub struct TomlWorkspace { pub members: Option>, pub exclude: Option>, pub default_members: Option>, pub resolver: Option, #[cfg_attr( feature = "unstable-schema", schemars(with = "Option") )] pub metadata: Option, // Properties that can be inherited by members. pub package: Option, pub dependencies: Option>, pub lints: Option, } /// A group of fields that are inheritable by members of the workspace #[derive(Clone, Debug, Default, Deserialize, Serialize)] #[serde(rename_all = "kebab-case")] #[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))] pub struct InheritablePackage { pub version: Option, pub authors: Option>, pub description: Option, pub homepage: Option, pub documentation: Option, pub readme: Option, pub keywords: Option>, pub categories: Option>, pub license: Option, pub license_file: Option, pub repository: Option, pub publish: Option, pub edition: Option, pub badges: Option>>, pub exclude: Option>, pub include: Option>, #[cfg_attr(feature = "unstable-schema", schemars(with = "Option"))] pub rust_version: Option, } /// Represents the `package`/`project` sections of a `Cargo.toml`. /// /// Note that the order of the fields matters, since this is the order they /// are serialized to a TOML file. For example, you cannot have values after /// the field `metadata`, since it is a table and values cannot appear after /// tables. #[derive(Deserialize, Serialize, Clone, Debug, Default)] #[serde(rename_all = "kebab-case")] #[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))] pub struct TomlPackage { pub edition: Option, #[cfg_attr(feature = "unstable-schema", schemars(with = "Option"))] pub rust_version: Option, #[cfg_attr(feature = "unstable-schema", schemars(with = "Option"))] pub name: Option, pub version: Option, pub authors: Option, pub build: Option, pub metabuild: Option, pub default_target: Option, pub forced_target: Option, pub links: Option, pub exclude: Option, pub include: Option, pub publish: Option, pub workspace: Option, pub im_a_teapot: Option, pub autolib: Option, pub autobins: Option, pub autoexamples: Option, pub autotests: Option, pub autobenches: Option, pub default_run: Option, // Package metadata. pub description: Option, pub homepage: Option, pub documentation: Option, pub readme: Option, pub keywords: Option, pub categories: Option, pub license: Option, pub license_file: Option, pub repository: Option, pub resolver: Option, #[cfg_attr( feature = "unstable-schema", schemars(with = "Option") )] pub metadata: Option, /// Provide a helpful error message for a common user error. #[serde(rename = "cargo-features", skip_serializing)] #[cfg_attr(feature = "unstable-schema", schemars(skip))] pub _invalid_cargo_features: Option, } impl TomlPackage { pub fn new(name: PackageName) -> Self { Self { name: Some(name), ..Default::default() } } pub fn normalized_name(&self) -> Result<&PackageName, UnresolvedError> { self.name.as_ref().ok_or(UnresolvedError) } pub fn normalized_edition(&self) -> Result, UnresolvedError> { self.edition.as_ref().map(|v| v.normalized()).transpose() } pub fn normalized_rust_version(&self) -> Result, UnresolvedError> { self.rust_version .as_ref() .map(|v| v.normalized()) .transpose() } pub fn normalized_version(&self) -> Result, UnresolvedError> { self.version.as_ref().map(|v| v.normalized()).transpose() } pub fn normalized_authors(&self) -> Result>, UnresolvedError> { self.authors.as_ref().map(|v| v.normalized()).transpose() } pub fn normalized_build(&self) -> Result, UnresolvedError> { let build = self.build.as_ref().ok_or(UnresolvedError)?; match build { TomlPackageBuild::Auto(false) => Ok(None), TomlPackageBuild::Auto(true) => Err(UnresolvedError), TomlPackageBuild::SingleScript(value) => Ok(Some(std::slice::from_ref(value))), TomlPackageBuild::MultipleScript(scripts) => Ok(Some(scripts)), } } pub fn normalized_exclude(&self) -> Result>, UnresolvedError> { self.exclude.as_ref().map(|v| v.normalized()).transpose() } pub fn normalized_include(&self) -> Result>, UnresolvedError> { self.include.as_ref().map(|v| v.normalized()).transpose() } pub fn normalized_publish(&self) -> Result, UnresolvedError> { self.publish.as_ref().map(|v| v.normalized()).transpose() } pub fn normalized_description(&self) -> Result, UnresolvedError> { self.description .as_ref() .map(|v| v.normalized()) .transpose() } pub fn normalized_homepage(&self) -> Result, UnresolvedError> { self.homepage.as_ref().map(|v| v.normalized()).transpose() } pub fn normalized_documentation(&self) -> Result, UnresolvedError> { self.documentation .as_ref() .map(|v| v.normalized()) .transpose() } pub fn normalized_readme(&self) -> Result, UnresolvedError> { let readme = self.readme.as_ref().ok_or(UnresolvedError)?; readme.normalized().and_then(|sb| match sb { StringOrBool::Bool(false) => Ok(None), StringOrBool::Bool(true) => Err(UnresolvedError), StringOrBool::String(value) => Ok(Some(value)), }) } pub fn normalized_keywords(&self) -> Result>, UnresolvedError> { self.keywords.as_ref().map(|v| v.normalized()).transpose() } pub fn normalized_categories(&self) -> Result>, UnresolvedError> { self.categories.as_ref().map(|v| v.normalized()).transpose() } pub fn normalized_license(&self) -> Result, UnresolvedError> { self.license.as_ref().map(|v| v.normalized()).transpose() } pub fn normalized_license_file(&self) -> Result, UnresolvedError> { self.license_file .as_ref() .map(|v| v.normalized()) .transpose() } pub fn normalized_repository(&self) -> Result, UnresolvedError> { self.repository.as_ref().map(|v| v.normalized()).transpose() } } /// An enum that allows for inheriting keys from a workspace in a Cargo.toml. #[derive(Serialize, Copy, Clone, Debug)] #[serde(untagged)] #[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))] pub enum InheritableField { /// The type that is used when not inheriting from a workspace. Value(T), /// The type when inheriting from a workspace. Inherit(TomlInheritedField), } impl InheritableField { pub fn normalized(&self) -> Result<&T, UnresolvedError> { self.as_value().ok_or(UnresolvedError) } pub fn as_value(&self) -> Option<&T> { match self { InheritableField::Inherit(_) => None, InheritableField::Value(defined) => Some(defined), } } } //. This already has a `Deserialize` impl from version_trim_whitespace pub type InheritableSemverVersion = InheritableField; impl<'de> de::Deserialize<'de> for InheritableSemverVersion { fn deserialize(d: D) -> Result where D: de::Deserializer<'de>, { UntaggedEnumVisitor::new() .expecting("SemVer version") .string( |value| match value.trim().parse().map_err(de::Error::custom) { Ok(parsed) => Ok(InheritableField::Value(parsed)), Err(e) => Err(e), }, ) .map(|value| value.deserialize().map(InheritableField::Inherit)) .deserialize(d) } } pub type InheritableString = InheritableField; impl<'de> de::Deserialize<'de> for InheritableString { fn deserialize(d: D) -> Result where D: de::Deserializer<'de>, { struct Visitor; impl<'de> de::Visitor<'de> for Visitor { type Value = InheritableString; fn expecting(&self, f: &mut fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { f.write_str("a string or workspace") } fn visit_string(self, value: String) -> Result where E: de::Error, { Ok(InheritableString::Value(value)) } fn visit_str(self, value: &str) -> Result where E: de::Error, { self.visit_string(value.to_owned()) } fn visit_map(self, map: V) -> Result where V: de::MapAccess<'de>, { let mvd = de::value::MapAccessDeserializer::new(map); TomlInheritedField::deserialize(mvd).map(InheritableField::Inherit) } } d.deserialize_any(Visitor) } } pub type InheritableRustVersion = InheritableField; impl<'de> de::Deserialize<'de> for InheritableRustVersion { fn deserialize(d: D) -> Result where D: de::Deserializer<'de>, { struct Visitor; impl<'de> de::Visitor<'de> for Visitor { type Value = InheritableRustVersion; fn expecting(&self, f: &mut fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { f.write_str("a semver or workspace") } fn visit_string(self, value: String) -> Result where E: de::Error, { let value = value.parse::().map_err(|e| E::custom(e))?; Ok(InheritableRustVersion::Value(value)) } fn visit_str(self, value: &str) -> Result where E: de::Error, { self.visit_string(value.to_owned()) } fn visit_map(self, map: V) -> Result where V: de::MapAccess<'de>, { let mvd = de::value::MapAccessDeserializer::new(map); TomlInheritedField::deserialize(mvd).map(InheritableField::Inherit) } } d.deserialize_any(Visitor) } } pub type InheritableVecString = InheritableField>; impl<'de> de::Deserialize<'de> for InheritableVecString { fn deserialize(d: D) -> Result where D: de::Deserializer<'de>, { struct Visitor; impl<'de> de::Visitor<'de> for Visitor { type Value = InheritableVecString; fn expecting(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { f.write_str("a vector of strings or workspace") } fn visit_seq(self, v: A) -> Result where A: de::SeqAccess<'de>, { let seq = de::value::SeqAccessDeserializer::new(v); Vec::deserialize(seq).map(InheritableField::Value) } fn visit_map(self, map: V) -> Result where V: de::MapAccess<'de>, { let mvd = de::value::MapAccessDeserializer::new(map); TomlInheritedField::deserialize(mvd).map(InheritableField::Inherit) } } d.deserialize_any(Visitor) } } pub type InheritableStringOrBool = InheritableField; impl<'de> de::Deserialize<'de> for InheritableStringOrBool { fn deserialize(d: D) -> Result where D: de::Deserializer<'de>, { struct Visitor; impl<'de> de::Visitor<'de> for Visitor { type Value = InheritableStringOrBool; fn expecting(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { f.write_str("a string, a bool, or workspace") } fn visit_bool(self, v: bool) -> Result where E: de::Error, { let b = de::value::BoolDeserializer::new(v); StringOrBool::deserialize(b).map(InheritableField::Value) } fn visit_string(self, v: String) -> Result where E: de::Error, { let string = de::value::StringDeserializer::new(v); StringOrBool::deserialize(string).map(InheritableField::Value) } fn visit_str(self, value: &str) -> Result where E: de::Error, { self.visit_string(value.to_owned()) } fn visit_map(self, map: V) -> Result where V: de::MapAccess<'de>, { let mvd = de::value::MapAccessDeserializer::new(map); TomlInheritedField::deserialize(mvd).map(InheritableField::Inherit) } } d.deserialize_any(Visitor) } } pub type InheritableVecStringOrBool = InheritableField; impl<'de> de::Deserialize<'de> for InheritableVecStringOrBool { fn deserialize(d: D) -> Result where D: de::Deserializer<'de>, { struct Visitor; impl<'de> de::Visitor<'de> for Visitor { type Value = InheritableVecStringOrBool; fn expecting(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { f.write_str("a boolean, a vector of strings, or workspace") } fn visit_bool(self, v: bool) -> Result where E: de::Error, { let b = de::value::BoolDeserializer::new(v); VecStringOrBool::deserialize(b).map(InheritableField::Value) } fn visit_seq(self, v: A) -> Result where A: de::SeqAccess<'de>, { let seq = de::value::SeqAccessDeserializer::new(v); VecStringOrBool::deserialize(seq).map(InheritableField::Value) } fn visit_map(self, map: V) -> Result where V: de::MapAccess<'de>, { let mvd = de::value::MapAccessDeserializer::new(map); TomlInheritedField::deserialize(mvd).map(InheritableField::Inherit) } } d.deserialize_any(Visitor) } } pub type InheritableBtreeMap = InheritableField>>; impl<'de> de::Deserialize<'de> for InheritableBtreeMap { fn deserialize(deserializer: D) -> Result where D: de::Deserializer<'de>, { let value = serde_value::Value::deserialize(deserializer)?; if let Ok(w) = TomlInheritedField::deserialize( serde_value::ValueDeserializer::::new(value.clone()), ) { return Ok(InheritableField::Inherit(w)); } BTreeMap::deserialize(serde_value::ValueDeserializer::::new(value)) .map(InheritableField::Value) } } #[derive(Deserialize, Serialize, Copy, Clone, Debug)] #[serde(rename_all = "kebab-case")] #[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))] pub struct TomlInheritedField { workspace: WorkspaceValue, } impl TomlInheritedField { pub fn new() -> Self { TomlInheritedField { workspace: WorkspaceValue, } } } impl Default for TomlInheritedField { fn default() -> Self { Self::new() } } #[derive(Deserialize, Serialize, Copy, Clone, Debug)] #[serde(try_from = "bool")] #[serde(into = "bool")] #[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))] struct WorkspaceValue; impl TryFrom for WorkspaceValue { type Error = String; fn try_from(other: bool) -> Result { if other { Ok(WorkspaceValue) } else { Err("`workspace` cannot be false".to_owned()) } } } impl From for bool { fn from(_: WorkspaceValue) -> bool { true } } #[derive(Serialize, Clone, Debug)] #[serde(untagged)] #[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))] pub enum InheritableDependency { /// The type that is used when not inheriting from a workspace. Value(TomlDependency), /// The type when inheriting from a workspace. Inherit(TomlInheritedDependency), } impl InheritableDependency { pub fn unused_keys(&self) -> Vec { match self { InheritableDependency::Value(d) => d.unused_keys(), InheritableDependency::Inherit(w) => w._unused_keys.keys().cloned().collect(), } } pub fn normalized(&self) -> Result<&TomlDependency, UnresolvedError> { match self { InheritableDependency::Value(d) => Ok(d), InheritableDependency::Inherit(_) => Err(UnresolvedError), } } } impl<'de> de::Deserialize<'de> for InheritableDependency { fn deserialize(deserializer: D) -> Result where D: de::Deserializer<'de>, { let value = serde_value::Value::deserialize(deserializer)?; if let Ok(w) = TomlInheritedDependency::deserialize(serde_value::ValueDeserializer::< D::Error, >::new(value.clone())) { return if w.workspace { Ok(InheritableDependency::Inherit(w)) } else { Err(de::Error::custom("`workspace` cannot be false")) }; } TomlDependency::deserialize(serde_value::ValueDeserializer::::new(value)) .map(InheritableDependency::Value) } } #[derive(Deserialize, Serialize, Clone, Debug)] #[serde(rename_all = "kebab-case")] #[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))] pub struct TomlInheritedDependency { pub workspace: bool, pub features: Option>, pub default_features: Option, #[serde(rename = "default_features")] pub default_features2: Option, pub optional: Option, pub public: Option, /// This is here to provide a way to see the "unused manifest keys" when deserializing #[serde(skip_serializing)] #[serde(flatten)] #[cfg_attr(feature = "unstable-schema", schemars(skip))] pub _unused_keys: BTreeMap, } impl TomlInheritedDependency { pub fn default_features(&self) -> Option { self.default_features.or(self.default_features2) } } #[derive(Clone, Debug, Serialize)] #[serde(untagged)] #[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))] pub enum TomlDependency { /// In the simple format, only a version is specified, eg. /// `package = ""` Simple(String), /// The simple format is equivalent to a detailed dependency /// specifying only a version, eg. /// `package = { version = "" }` Detailed(TomlDetailedDependency

), } impl TomlDependency { pub fn is_version_specified(&self) -> bool { match self { TomlDependency::Detailed(d) => d.version.is_some(), TomlDependency::Simple(..) => true, } } pub fn is_optional(&self) -> bool { match self { TomlDependency::Detailed(d) => d.optional.unwrap_or(false), TomlDependency::Simple(..) => false, } } pub fn is_public(&self) -> bool { match self { TomlDependency::Detailed(d) => d.public.unwrap_or(false), TomlDependency::Simple(..) => false, } } pub fn default_features(&self) -> Option { match self { TomlDependency::Detailed(d) => d.default_features(), TomlDependency::Simple(..) => None, } } pub fn unused_keys(&self) -> Vec { match self { TomlDependency::Simple(_) => vec![], TomlDependency::Detailed(detailed) => detailed._unused_keys.keys().cloned().collect(), } } } impl<'de, P: Deserialize<'de> + Clone> de::Deserialize<'de> for TomlDependency

{ fn deserialize(deserializer: D) -> Result where D: de::Deserializer<'de>, { UntaggedEnumVisitor::new() .expecting( "a version string like \"0.9.8\" or a \ detailed dependency like { version = \"0.9.8\" }", ) .string(|value| Ok(TomlDependency::Simple(value.to_owned()))) .map(|value| value.deserialize().map(TomlDependency::Detailed)) .deserialize(deserializer) } } #[derive(Deserialize, Serialize, Clone, Debug)] #[serde(rename_all = "kebab-case")] #[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))] pub struct TomlDetailedDependency { pub version: Option, #[cfg_attr(feature = "unstable-schema", schemars(with = "Option"))] pub registry: Option, /// The URL of the `registry` field. /// This is an internal implementation detail. When Cargo creates a /// package, it replaces `registry` with `registry-index` so that the /// manifest contains the correct URL. All users won't have the same /// registry names configured, so Cargo can't rely on just the name for /// crates published by other users. pub registry_index: Option, // `path` is relative to the file it appears in. If that's a `Cargo.toml`, it'll be relative to // that TOML file, and if it's a `.cargo/config` file, it'll be relative to that file. pub path: Option

, #[cfg_attr(feature = "unstable-schema", schemars(with = "Option"))] pub base: Option, pub git: Option, pub branch: Option, pub tag: Option, pub rev: Option, pub features: Option>, pub optional: Option, pub default_features: Option, #[serde(rename = "default_features")] pub default_features2: Option, #[cfg_attr(feature = "unstable-schema", schemars(with = "Option"))] pub package: Option, pub public: Option, /// One or more of `bin`, `cdylib`, `staticlib`, `bin:`. pub artifact: Option, /// If set, the artifact should also be a dependency pub lib: Option, /// A platform name, like `x86_64-apple-darwin` pub target: Option, /// This is here to provide a way to see the "unused manifest keys" when deserializing #[serde(skip_serializing)] #[serde(flatten)] #[cfg_attr(feature = "unstable-schema", schemars(skip))] pub _unused_keys: BTreeMap, } impl TomlDetailedDependency

{ pub fn default_features(&self) -> Option { self.default_features.or(self.default_features2) } } // Explicit implementation so we avoid pulling in P: Default impl Default for TomlDetailedDependency

{ fn default() -> Self { Self { version: Default::default(), registry: Default::default(), registry_index: Default::default(), path: Default::default(), base: Default::default(), git: Default::default(), branch: Default::default(), tag: Default::default(), rev: Default::default(), features: Default::default(), optional: Default::default(), default_features: Default::default(), default_features2: Default::default(), package: Default::default(), public: Default::default(), artifact: Default::default(), lib: Default::default(), target: Default::default(), _unused_keys: Default::default(), } } } #[derive(Deserialize, Serialize, Clone, Debug, Default)] #[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))] pub struct TomlProfiles(pub BTreeMap); impl TomlProfiles { pub fn get_all(&self) -> &BTreeMap { &self.0 } pub fn get(&self, name: &str) -> Option<&TomlProfile> { self.0.get(name) } } #[derive(Deserialize, Serialize, Clone, Debug, Default, Eq, PartialEq)] #[serde(default, rename_all = "kebab-case")] #[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))] pub struct TomlProfile { pub opt_level: Option, pub lto: Option, pub codegen_backend: Option, pub codegen_units: Option, pub debug: Option, pub split_debuginfo: Option, pub debug_assertions: Option, pub rpath: Option, pub panic: Option, pub overflow_checks: Option, pub incremental: Option, pub dir_name: Option, pub inherits: Option, pub strip: Option, // Note that `rustflags` is used for the cargo-feature `profile_rustflags` pub rustflags: Option>, // These two fields must be last because they are sub-tables, and TOML // requires all non-tables to be listed first. pub package: Option>, pub build_override: Option>, /// Unstable feature `-Ztrim-paths`. pub trim_paths: Option, /// Unstable feature `hint-mostly-unused` pub hint_mostly_unused: Option, } impl TomlProfile { /// Overwrite self's values with the given profile. pub fn merge(&mut self, profile: &Self) { if let Some(v) = &profile.opt_level { self.opt_level = Some(v.clone()); } if let Some(v) = &profile.lto { self.lto = Some(v.clone()); } if let Some(v) = &profile.codegen_backend { self.codegen_backend = Some(v.clone()); } if let Some(v) = profile.codegen_units { self.codegen_units = Some(v); } if let Some(v) = profile.debug { self.debug = Some(v); } if let Some(v) = profile.debug_assertions { self.debug_assertions = Some(v); } if let Some(v) = &profile.split_debuginfo { self.split_debuginfo = Some(v.clone()); } if let Some(v) = profile.rpath { self.rpath = Some(v); } if let Some(v) = &profile.panic { self.panic = Some(v.clone()); } if let Some(v) = profile.overflow_checks { self.overflow_checks = Some(v); } if let Some(v) = profile.incremental { self.incremental = Some(v); } if let Some(v) = &profile.rustflags { self.rustflags = Some(v.clone()); } if let Some(other_package) = &profile.package { match &mut self.package { Some(self_package) => { for (spec, other_pkg_profile) in other_package { match self_package.get_mut(spec) { Some(p) => p.merge(other_pkg_profile), None => { self_package.insert(spec.clone(), other_pkg_profile.clone()); } } } } None => self.package = Some(other_package.clone()), } } if let Some(other_bo) = &profile.build_override { match &mut self.build_override { Some(self_bo) => self_bo.merge(other_bo), None => self.build_override = Some(other_bo.clone()), } } if let Some(v) = &profile.inherits { self.inherits = Some(v.clone()); } if let Some(v) = &profile.dir_name { self.dir_name = Some(v.clone()); } if let Some(v) = &profile.strip { self.strip = Some(v.clone()); } if let Some(v) = &profile.trim_paths { self.trim_paths = Some(v.clone()) } if let Some(v) = profile.hint_mostly_unused { self.hint_mostly_unused = Some(v); } } } #[derive(Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)] #[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))] pub enum ProfilePackageSpec { Spec(PackageIdSpec), All, } impl fmt::Display for ProfilePackageSpec { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { ProfilePackageSpec::Spec(spec) => spec.fmt(f), ProfilePackageSpec::All => f.write_str("*"), } } } impl ser::Serialize for ProfilePackageSpec { fn serialize(&self, s: S) -> Result where S: ser::Serializer, { self.to_string().serialize(s) } } impl<'de> de::Deserialize<'de> for ProfilePackageSpec { fn deserialize(d: D) -> Result where D: de::Deserializer<'de>, { let string = String::deserialize(d)?; if string == "*" { Ok(ProfilePackageSpec::All) } else { PackageIdSpec::parse(&string) .map_err(de::Error::custom) .map(ProfilePackageSpec::Spec) } } } #[derive(Clone, Debug, Eq, PartialEq)] #[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))] pub struct TomlOptLevel(pub String); impl ser::Serialize for TomlOptLevel { fn serialize(&self, serializer: S) -> Result where S: ser::Serializer, { match self.0.parse::() { Ok(n) => n.serialize(serializer), Err(_) => self.0.serialize(serializer), } } } impl<'de> de::Deserialize<'de> for TomlOptLevel { fn deserialize(d: D) -> Result where D: de::Deserializer<'de>, { use serde::de::Error as _; UntaggedEnumVisitor::new() .expecting("an optimization level") .i64(|value| Ok(TomlOptLevel(value.to_string()))) .string(|value| { if value == "s" || value == "z" { Ok(TomlOptLevel(value.to_string())) } else { Err(serde_untagged::de::Error::custom(format!( "must be `0`, `1`, `2`, `3`, `s` or `z`, \ but found the string: \"{}\"", value ))) } }) .deserialize(d) } } #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, PartialOrd, Ord)] #[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))] pub enum TomlDebugInfo { None, LineDirectivesOnly, LineTablesOnly, Limited, Full, } impl Display for TomlDebugInfo { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { TomlDebugInfo::None => f.write_char('0'), TomlDebugInfo::Limited => f.write_char('1'), TomlDebugInfo::Full => f.write_char('2'), TomlDebugInfo::LineDirectivesOnly => f.write_str("line-directives-only"), TomlDebugInfo::LineTablesOnly => f.write_str("line-tables-only"), } } } impl ser::Serialize for TomlDebugInfo { fn serialize(&self, serializer: S) -> Result where S: ser::Serializer, { match self { Self::None => 0.serialize(serializer), Self::LineDirectivesOnly => "line-directives-only".serialize(serializer), Self::LineTablesOnly => "line-tables-only".serialize(serializer), Self::Limited => 1.serialize(serializer), Self::Full => 2.serialize(serializer), } } } impl<'de> de::Deserialize<'de> for TomlDebugInfo { fn deserialize(d: D) -> Result where D: de::Deserializer<'de>, { use serde::de::Error as _; let expecting = "a boolean, 0, 1, 2, \"none\", \"limited\", \"full\", \"line-tables-only\", or \"line-directives-only\""; UntaggedEnumVisitor::new() .expecting(expecting) .bool(|value| { Ok(if value { TomlDebugInfo::Full } else { TomlDebugInfo::None }) }) .i64(|value| { let debuginfo = match value { 0 => TomlDebugInfo::None, 1 => TomlDebugInfo::Limited, 2 => TomlDebugInfo::Full, _ => { return Err(serde_untagged::de::Error::invalid_value( Unexpected::Signed(value), &expecting, )); } }; Ok(debuginfo) }) .string(|value| { let debuginfo = match value { "none" => TomlDebugInfo::None, "limited" => TomlDebugInfo::Limited, "full" => TomlDebugInfo::Full, "line-directives-only" => TomlDebugInfo::LineDirectivesOnly, "line-tables-only" => TomlDebugInfo::LineTablesOnly, _ => { return Err(serde_untagged::de::Error::invalid_value( Unexpected::Str(value), &expecting, )); } }; Ok(debuginfo) }) .deserialize(d) } } #[derive(Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash, Serialize)] #[serde(untagged, rename_all = "kebab-case")] #[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))] pub enum TomlTrimPaths { Values(Vec), All, } impl TomlTrimPaths { pub fn none() -> Self { TomlTrimPaths::Values(Vec::new()) } pub fn is_none(&self) -> bool { match self { TomlTrimPaths::Values(v) => v.is_empty(), TomlTrimPaths::All => false, } } } impl<'de> de::Deserialize<'de> for TomlTrimPaths { fn deserialize(d: D) -> Result where D: de::Deserializer<'de>, { use serde::de::Error as _; let expecting = r#"a boolean, "none", "diagnostics", "macro", "object", "all", or an array with these options"#; UntaggedEnumVisitor::new() .expecting(expecting) .bool(|value| { Ok(if value { TomlTrimPaths::All } else { TomlTrimPaths::none() }) }) .string(|v| match v { "none" => Ok(TomlTrimPaths::none()), "all" => Ok(TomlTrimPaths::All), v => { let d = v.into_deserializer(); let err = |_: D::Error| { serde_untagged::de::Error::custom(format!("expected {expecting}")) }; TomlTrimPathsValue::deserialize(d) .map_err(err) .map(|v| v.into()) } }) .seq(|seq| { let seq: Vec = seq.deserialize()?; let seq: Vec<_> = seq .into_iter() .map(|s| TomlTrimPathsValue::deserialize(s.into_deserializer())) .collect::>()?; Ok(seq.into()) }) .deserialize(d) } } impl fmt::Display for TomlTrimPaths { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { TomlTrimPaths::All => write!(f, "all"), TomlTrimPaths::Values(v) if v.is_empty() => write!(f, "none"), TomlTrimPaths::Values(v) => { let mut iter = v.iter(); if let Some(value) = iter.next() { write!(f, "{value}")?; } for value in iter { write!(f, ",{value}")?; } Ok(()) } } } } impl From for TomlTrimPaths { fn from(value: TomlTrimPathsValue) -> Self { TomlTrimPaths::Values(vec![value]) } } impl From> for TomlTrimPaths { fn from(value: Vec) -> Self { TomlTrimPaths::Values(value) } } #[derive(Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash, Serialize, Deserialize)] #[serde(rename_all = "kebab-case")] #[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))] pub enum TomlTrimPathsValue { Diagnostics, Macro, Object, } impl TomlTrimPathsValue { pub fn as_str(&self) -> &'static str { match self { TomlTrimPathsValue::Diagnostics => "diagnostics", TomlTrimPathsValue::Macro => "macro", TomlTrimPathsValue::Object => "object", } } } impl fmt::Display for TomlTrimPathsValue { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.as_str()) } } pub type TomlLibTarget = TomlTarget; pub type TomlBinTarget = TomlTarget; pub type TomlExampleTarget = TomlTarget; pub type TomlTestTarget = TomlTarget; pub type TomlBenchTarget = TomlTarget; #[derive(Default, Serialize, Deserialize, Debug, Clone)] #[serde(rename_all = "kebab-case")] #[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))] pub struct TomlTarget { pub name: Option, // The intention was to only accept `crate-type` here but historical // versions of Cargo also accepted `crate_type`, so look for both. pub crate_type: Option>, #[serde(rename = "crate_type")] pub crate_type2: Option>, #[cfg_attr(feature = "unstable-schema", schemars(with = "Option"))] pub path: Option, // Note that `filename` is used for the cargo-feature `different_binary_name` pub filename: Option, pub test: Option, pub doctest: Option, pub bench: Option, pub doc: Option, pub doc_scrape_examples: Option, pub proc_macro: Option, #[serde(rename = "proc_macro")] pub proc_macro2: Option, pub harness: Option, pub required_features: Option>, pub edition: Option, } impl TomlTarget { pub fn new() -> TomlTarget { TomlTarget::default() } pub fn proc_macro(&self) -> Option { self.proc_macro.or(self.proc_macro2).or_else(|| { if let Some(types) = self.crate_types() { if types.contains(&"proc-macro".to_string()) { return Some(true); } } None }) } pub fn crate_types(&self) -> Option<&Vec> { self.crate_type .as_ref() .or_else(|| self.crate_type2.as_ref()) } } macro_rules! str_newtype { ($name:ident) => { /// Verified string newtype #[derive(Serialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[serde(transparent)] #[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))] pub struct $name = String>(T); impl> $name { pub fn into_inner(self) -> T { self.0 } } impl> AsRef for $name { fn as_ref(&self) -> &str { self.0.as_ref() } } impl> std::ops::Deref for $name { type Target = T; fn deref(&self) -> &Self::Target { &self.0 } } impl> std::borrow::Borrow for $name { fn borrow(&self) -> &str { self.0.as_ref() } } impl<'a> std::str::FromStr for $name { type Err = restricted_names::NameValidationError; fn from_str(value: &str) -> Result { Self::new(value.to_owned()) } } impl<'de, T: AsRef + serde::Deserialize<'de>> serde::Deserialize<'de> for $name { fn deserialize(deserializer: D) -> Result where D: serde::Deserializer<'de>, { let inner = T::deserialize(deserializer)?; Self::new(inner).map_err(serde::de::Error::custom) } } impl> Display for $name { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.0.as_ref().fmt(f) } } }; } str_newtype!(PackageName); impl> PackageName { /// Validated package name pub fn new(name: T) -> Result { restricted_names::validate_package_name(name.as_ref())?; Ok(Self(name)) } } impl PackageName { /// Coerce a value to be a validate package name /// /// Replaces invalid values with `placeholder` pub fn sanitize(name: impl AsRef, placeholder: char) -> Self { PackageName(restricted_names::sanitize_package_name( name.as_ref(), placeholder, )) } } str_newtype!(RegistryName); impl> RegistryName { /// Validated registry name pub fn new(name: T) -> Result { restricted_names::validate_registry_name(name.as_ref())?; Ok(Self(name)) } } str_newtype!(ProfileName); impl> ProfileName { /// Validated profile name pub fn new(name: T) -> Result { restricted_names::validate_profile_name(name.as_ref())?; Ok(Self(name)) } } str_newtype!(FeatureName); impl> FeatureName { /// Validated feature name pub fn new(name: T) -> Result { restricted_names::validate_feature_name(name.as_ref())?; Ok(Self(name)) } } str_newtype!(PathBaseName); impl> PathBaseName { /// Validated path base name pub fn new(name: T) -> Result { restricted_names::validate_path_base_name(name.as_ref())?; Ok(Self(name)) } } /// Corresponds to a `target` entry, but `TomlTarget` is already used. #[derive(Serialize, Deserialize, Debug, Clone)] #[serde(rename_all = "kebab-case")] #[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))] pub struct TomlPlatform { pub dependencies: Option>, pub build_dependencies: Option>, #[serde(rename = "build_dependencies")] pub build_dependencies2: Option>, pub dev_dependencies: Option>, #[serde(rename = "dev_dependencies")] pub dev_dependencies2: Option>, } impl TomlPlatform { pub fn dev_dependencies(&self) -> Option<&BTreeMap> { self.dev_dependencies .as_ref() .or(self.dev_dependencies2.as_ref()) } pub fn build_dependencies(&self) -> Option<&BTreeMap> { self.build_dependencies .as_ref() .or(self.build_dependencies2.as_ref()) } } #[derive(Serialize, Debug, Clone)] #[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))] pub struct InheritableLints { #[serde(skip_serializing_if = "std::ops::Not::not")] #[cfg_attr(feature = "unstable-schema", schemars(default))] pub workspace: bool, #[serde(flatten)] pub lints: TomlLints, } impl InheritableLints { pub fn normalized(&self) -> Result<&TomlLints, UnresolvedError> { if self.workspace { Err(UnresolvedError) } else { Ok(&self.lints) } } } impl<'de> Deserialize<'de> for InheritableLints { fn deserialize(deserializer: D) -> Result where D: de::Deserializer<'de>, { struct InheritableLintsVisitor; impl<'de> de::Visitor<'de> for InheritableLintsVisitor { // The type that our Visitor is going to produce. type Value = InheritableLints; // Format a message stating what data this Visitor expects to receive. fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { formatter.write_str("a lints table") } // Deserialize MyMap from an abstract "map" provided by the // Deserializer. The MapAccess input is a callback provided by // the Deserializer to let us see each entry in the map. fn visit_map(self, mut access: M) -> Result where M: de::MapAccess<'de>, { let mut lints = TomlLints::new(); let mut workspace = false; // While there are entries remaining in the input, add them // into our map. while let Some(key) = access.next_key()? { if key == "workspace" { workspace = match access.next_value()? { Some(WorkspaceValue) => true, None => false, }; } else { let value = access.next_value()?; lints.insert(key, value); } } Ok(InheritableLints { workspace, lints }) } } deserializer.deserialize_map(InheritableLintsVisitor) } } pub type TomlLints = BTreeMap; pub type TomlToolLints = BTreeMap; #[derive(Serialize, Debug, Clone)] #[serde(untagged)] #[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))] pub enum TomlLint { Level(TomlLintLevel), Config(TomlLintConfig), } impl<'de> Deserialize<'de> for TomlLint { fn deserialize(deserializer: D) -> Result where D: de::Deserializer<'de>, { UntaggedEnumVisitor::new() .string(|string| { TomlLintLevel::deserialize(string.into_deserializer()).map(TomlLint::Level) }) .map(|map| map.deserialize().map(TomlLint::Config)) .deserialize(deserializer) } } impl TomlLint { pub fn level(&self) -> TomlLintLevel { match self { Self::Level(level) => *level, Self::Config(config) => config.level, } } pub fn priority(&self) -> i8 { match self { Self::Level(_) => 0, Self::Config(config) => config.priority, } } pub fn config(&self) -> Option<&toml::Table> { match self { Self::Level(_) => None, Self::Config(config) => Some(&config.config), } } } #[derive(Serialize, Deserialize, Debug, Clone)] #[serde(rename_all = "kebab-case")] #[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))] pub struct TomlLintConfig { pub level: TomlLintLevel, #[serde(default)] pub priority: i8, #[serde(flatten)] #[cfg_attr( feature = "unstable-schema", schemars(with = "HashMap") )] pub config: toml::Table, } #[derive(Serialize, Deserialize, Debug, Copy, Clone, Eq, PartialEq)] #[serde(rename_all = "kebab-case")] #[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))] pub enum TomlLintLevel { Forbid, Deny, Warn, Allow, } #[derive(Serialize, Deserialize, Debug, Default, Clone)] #[serde(rename_all = "kebab-case")] #[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))] pub struct Hints { #[cfg_attr( feature = "unstable-schema", schemars(with = "Option") )] pub mostly_unused: Option, } #[derive(Copy, Clone, Debug)] pub struct InvalidCargoFeatures {} impl<'de> de::Deserialize<'de> for InvalidCargoFeatures { fn deserialize(_d: D) -> Result where D: de::Deserializer<'de>, { use serde::de::Error as _; Err(D::Error::custom( "the field `cargo-features` should be set at the top of Cargo.toml before any tables", )) } } /// This can be parsed from either a TOML string or array, /// but is always stored as a vector. #[derive(Clone, Debug, Serialize, Eq, PartialEq, PartialOrd, Ord)] #[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))] pub struct StringOrVec(pub Vec); impl StringOrVec { pub fn iter<'a>(&'a self) -> std::slice::Iter<'a, String> { self.0.iter() } } impl<'de> de::Deserialize<'de> for StringOrVec { fn deserialize(deserializer: D) -> Result where D: de::Deserializer<'de>, { UntaggedEnumVisitor::new() .expecting("string or list of strings") .string(|value| Ok(StringOrVec(vec![value.to_owned()]))) .seq(|value| value.deserialize().map(StringOrVec)) .deserialize(deserializer) } } #[derive(Clone, Debug, Serialize, Eq, PartialEq)] #[serde(untagged)] #[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))] pub enum StringOrBool { String(String), Bool(bool), } impl<'de> Deserialize<'de> for StringOrBool { fn deserialize(deserializer: D) -> Result where D: de::Deserializer<'de>, { UntaggedEnumVisitor::new() .bool(|b| Ok(StringOrBool::Bool(b))) .string(|s| Ok(StringOrBool::String(s.to_owned()))) .deserialize(deserializer) } } #[derive(Clone, Debug, Serialize, Eq, PartialEq)] #[serde(untagged)] #[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))] pub enum TomlPackageBuild { /// If build scripts are disabled or enabled. /// If true, `build.rs` in the root folder will be the build script. Auto(bool), /// Path of Build Script if there's just one script. SingleScript(String), /// Vector of paths if multiple build script are to be used. MultipleScript(Vec), } impl<'de> Deserialize<'de> for TomlPackageBuild { fn deserialize(deserializer: D) -> Result where D: de::Deserializer<'de>, { UntaggedEnumVisitor::new() .bool(|b| Ok(TomlPackageBuild::Auto(b))) .string(|s| Ok(TomlPackageBuild::SingleScript(s.to_owned()))) .seq(|value| value.deserialize().map(TomlPackageBuild::MultipleScript)) .deserialize(deserializer) } } #[derive(PartialEq, Clone, Debug, Serialize)] #[serde(untagged)] #[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))] pub enum VecStringOrBool { VecString(Vec), Bool(bool), } impl<'de> de::Deserialize<'de> for VecStringOrBool { fn deserialize(deserializer: D) -> Result where D: de::Deserializer<'de>, { UntaggedEnumVisitor::new() .expecting("a boolean or vector of strings") .bool(|value| Ok(VecStringOrBool::Bool(value))) .seq(|value| value.deserialize().map(VecStringOrBool::VecString)) .deserialize(deserializer) } } #[derive(Clone)] pub struct PathValue(pub PathBuf); impl fmt::Debug for PathValue { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.0.fmt(f) } } impl ser::Serialize for PathValue { fn serialize(&self, serializer: S) -> Result where S: ser::Serializer, { self.0.serialize(serializer) } } impl<'de> de::Deserialize<'de> for PathValue { fn deserialize(deserializer: D) -> Result where D: de::Deserializer<'de>, { Ok(PathValue(String::deserialize(deserializer)?.into())) } } /// Error validating names in Cargo. #[derive(Debug, thiserror::Error)] #[error("manifest field was not resolved")] #[non_exhaustive] #[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))] pub struct UnresolvedError; #[cfg(feature = "unstable-schema")] #[test] fn dump_manifest_schema() { let schema = schemars::schema_for!(crate::manifest::TomlManifest); let dump = serde_json::to_string_pretty(&schema).unwrap(); snapbox::assert_data_eq!(dump, snapbox::file!("../../manifest.schema.json").raw()); } cargo-util-schemas-0.10.0/src/manifest/rust_version.rs000064400000000000000000000153701046102023000211040ustar 00000000000000use std::fmt; use std::fmt::Display; use serde_untagged::UntaggedEnumVisitor; use crate::core::PartialVersion; use crate::core::PartialVersionError; #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Debug, serde::Serialize)] #[serde(transparent)] pub struct RustVersion(PartialVersion); impl RustVersion { pub fn is_compatible_with(&self, rustc: &PartialVersion) -> bool { let msrv = self.0.to_caret_req(); // Remove any pre-release identifiers for easier comparison let rustc = semver::Version { major: rustc.major, minor: rustc.minor.unwrap_or_default(), patch: rustc.patch.unwrap_or_default(), pre: Default::default(), build: Default::default(), }; msrv.matches(&rustc) } pub fn into_partial(self) -> PartialVersion { self.0 } pub fn as_partial(&self) -> &PartialVersion { &self.0 } } impl std::str::FromStr for RustVersion { type Err = RustVersionError; fn from_str(value: &str) -> Result { let partial = value.parse::(); let partial = partial.map_err(RustVersionErrorKind::PartialVersion)?; partial.try_into() } } impl TryFrom for RustVersion { type Error = RustVersionError; fn try_from(version: semver::Version) -> Result { let version = PartialVersion::from(version); Self::try_from(version) } } impl TryFrom for RustVersion { type Error = RustVersionError; fn try_from(partial: PartialVersion) -> Result { if partial.pre.is_some() { return Err(RustVersionErrorKind::Prerelease.into()); } if partial.build.is_some() { return Err(RustVersionErrorKind::BuildMetadata.into()); } Ok(Self(partial)) } } impl<'de> serde::Deserialize<'de> for RustVersion { fn deserialize(deserializer: D) -> Result where D: serde::Deserializer<'de>, { UntaggedEnumVisitor::new() .expecting("SemVer version") .string(|value| value.parse().map_err(serde::de::Error::custom)) .deserialize(deserializer) } } impl Display for RustVersion { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.0.fmt(f) } } /// Error parsing a [`RustVersion`]. #[derive(Debug, thiserror::Error)] #[error(transparent)] pub struct RustVersionError(#[from] RustVersionErrorKind); /// Non-public error kind for [`RustVersionError`]. #[non_exhaustive] #[derive(Debug, thiserror::Error)] enum RustVersionErrorKind { #[error("unexpected prerelease field, expected a version like \"1.32\"")] Prerelease, #[error("unexpected build field, expected a version like \"1.32\"")] BuildMetadata, #[error(transparent)] PartialVersion(#[from] PartialVersionError), } #[cfg(test)] mod test { use super::*; use snapbox::prelude::*; use snapbox::str; #[test] fn is_compatible_with_rustc() { let cases = &[ ("1", "1.70.0", true), ("1.30", "1.70.0", true), ("1.30.10", "1.70.0", true), ("1.70", "1.70.0", true), ("1.70.0", "1.70.0", true), ("1.70.1", "1.70.0", false), ("1.70", "1.70.0-nightly", true), ("1.70.0", "1.70.0-nightly", true), ("1.71", "1.70.0", false), ("2", "1.70.0", false), ]; let mut passed = true; for (msrv, rustc, expected) in cases { let msrv: RustVersion = msrv.parse().unwrap(); let rustc = PartialVersion::from(semver::Version::parse(rustc).unwrap()); if msrv.is_compatible_with(&rustc) != *expected { println!("failed: {msrv} is_compatible_with {rustc} == {expected}"); passed = false; } } assert!(passed); } #[test] fn is_compatible_with_workspace_msrv() { let cases = &[ ("1", "1", true), ("1", "1.70", true), ("1", "1.70.0", true), ("1.30", "1", false), ("1.30", "1.70", true), ("1.30", "1.70.0", true), ("1.30.10", "1", false), ("1.30.10", "1.70", true), ("1.30.10", "1.70.0", true), ("1.70", "1", false), ("1.70", "1.70", true), ("1.70", "1.70.0", true), ("1.70.0", "1", false), ("1.70.0", "1.70", true), ("1.70.0", "1.70.0", true), ("1.70.1", "1", false), ("1.70.1", "1.70", false), ("1.70.1", "1.70.0", false), ("1.71", "1", false), ("1.71", "1.70", false), ("1.71", "1.70.0", false), ("2", "1.70.0", false), ]; let mut passed = true; for (dep_msrv, ws_msrv, expected) in cases { let dep_msrv: RustVersion = dep_msrv.parse().unwrap(); let ws_msrv = ws_msrv.parse::().unwrap().into_partial(); if dep_msrv.is_compatible_with(&ws_msrv) != *expected { println!("failed: {dep_msrv} is_compatible_with {ws_msrv} == {expected}"); passed = false; } } assert!(passed); } #[test] fn parse_errors() { let cases = &[ // Disallow caret ( "^1.43", str![[r#"unexpected version requirement, expected a version like "1.32""#]], ), // Valid pre-release ( "1.43.0-beta.1", str![[r#"unexpected prerelease field, expected a version like "1.32""#]], ), // Bad pre-release ( "1.43-beta.1", str![[r#"unexpected prerelease field, expected a version like "1.32""#]], ), // Weird wildcard ( "x", str![[r#"unexpected version requirement, expected a version like "1.32""#]], ), ( "1.x", str![[r#"unexpected version requirement, expected a version like "1.32""#]], ), ( "1.1.x", str![[r#"unexpected version requirement, expected a version like "1.32""#]], ), // Non-sense ("foodaddle", str![[r#"expected a version like "1.32""#]]), ]; for (input, expected) in cases { let actual: Result = input.parse(); let actual = match actual { Ok(result) => format!("didn't fail: {result:?}"), Err(err) => err.to_string(), }; snapbox::assert_data_eq!(actual, expected.clone().raw()); } } } cargo-util-schemas-0.10.0/src/messages.rs000064400000000000000000000020201046102023000163270ustar 00000000000000//! Schemas for JSON messages emitted by Cargo. use std::collections::BTreeMap; use std::path::PathBuf; /// File information of a package archive generated by `cargo package --list`. #[derive(Debug, serde::Serialize)] #[serde(rename_all = "snake_case")] pub struct PackageList { /// The Package ID Spec of the package. pub id: crate::core::PackageIdSpec, /// A map of relative paths in the archive to their detailed file information. pub files: BTreeMap, } /// Where the file is from. #[derive(Debug, serde::Serialize)] #[serde(rename_all = "snake_case", tag = "kind")] pub enum PackageFile { /// File being copied from another location. Copy { /// An absolute path to the actual file content path: PathBuf, }, /// File being generated during packaging Generate { /// An absolute path to the original file the generated one is based on. /// if any. #[serde(skip_serializing_if = "Option::is_none")] path: Option, }, } cargo-util-schemas-0.10.0/src/restricted_names.rs000064400000000000000000000205661046102023000200720ustar 00000000000000//! Helpers for validating and checking names like package and crate names. type Result = std::result::Result; /// Error validating names in Cargo. #[derive(Debug, thiserror::Error)] #[error(transparent)] pub struct NameValidationError(#[from] ErrorKind); /// Non-public error kind for [`NameValidationError`]. #[non_exhaustive] #[derive(Debug, thiserror::Error)] enum ErrorKind { #[error("{0} cannot be empty")] Empty(&'static str), #[error("invalid character `{ch}` in {what}: `{name}`, {reason}")] InvalidCharacter { ch: char, what: &'static str, name: String, reason: &'static str, }, #[error( "profile name `{name}` is reserved\n{help}\n\ See https://doc.rust-lang.org/cargo/reference/profiles.html \ for more on configuring profiles." )] ProfileNameReservedKeyword { name: String, help: &'static str }, #[error("feature named `{0}` is not allowed to start with `dep:`")] FeatureNameStartsWithDepColon(String), } pub(crate) fn validate_package_name(name: &str) -> Result<()> { for part in name.split("::") { validate_name(part, "package name")?; } Ok(()) } pub(crate) fn validate_registry_name(name: &str) -> Result<()> { validate_name(name, "registry name") } pub(crate) fn validate_name(name: &str, what: &'static str) -> Result<()> { if name.is_empty() { return Err(ErrorKind::Empty(what).into()); } let mut chars = name.chars(); if let Some(ch) = chars.next() { if ch.is_digit(10) { // A specific error for a potentially common case. return Err(ErrorKind::InvalidCharacter { ch, what, name: name.into(), reason: "the name cannot start with a digit", } .into()); } if !(unicode_xid::UnicodeXID::is_xid_start(ch) || ch == '_') { return Err(ErrorKind::InvalidCharacter { ch, what, name: name.into(), reason: "the first character must be a Unicode XID start character \ (most letters or `_`)", } .into()); } } for ch in chars { if !(unicode_xid::UnicodeXID::is_xid_continue(ch) || ch == '-') { return Err(ErrorKind::InvalidCharacter { ch, what, name: name.into(), reason: "characters must be Unicode XID characters \ (numbers, `-`, `_`, or most letters)", } .into()); } } Ok(()) } /// Ensure a package name is [valid][validate_package_name] pub(crate) fn sanitize_package_name(name: &str, placeholder: char) -> String { let mut slug = String::new(); for part in name.split("::") { if !slug.is_empty() { slug.push_str("::"); } slug.push_str(&sanitize_name(part, placeholder)); } slug } pub(crate) fn sanitize_name(name: &str, placeholder: char) -> String { let mut slug = String::new(); let mut chars = name.chars(); while let Some(ch) = chars.next() { if (unicode_xid::UnicodeXID::is_xid_start(ch) || ch == '_') && !ch.is_digit(10) { slug.push(ch); break; } } while let Some(ch) = chars.next() { if unicode_xid::UnicodeXID::is_xid_continue(ch) || ch == '-' { slug.push(ch); } else { slug.push(placeholder); } } if slug.is_empty() { slug.push_str("package"); } slug } /// Validate dir-names and profile names according to RFC 2678. pub(crate) fn validate_profile_name(name: &str) -> Result<()> { if let Some(ch) = name .chars() .find(|ch| !ch.is_alphanumeric() && *ch != '_' && *ch != '-') { return Err(ErrorKind::InvalidCharacter { ch, what: "profile name", name: name.into(), reason: "allowed characters are letters, numbers, underscore, and hyphen", } .into()); } let lower_name = name.to_lowercase(); if lower_name == "debug" { return Err(ErrorKind::ProfileNameReservedKeyword { name: name.into(), help: "To configure the default development profile, \ use the name `dev` as in [profile.dev]", } .into()); } if lower_name == "build-override" { return Err(ErrorKind::ProfileNameReservedKeyword { name: name.into(), help: "To configure build dependency settings, use [profile.dev.build-override] \ and [profile.release.build-override]", } .into()); } // These are some arbitrary reservations. We have no plans to use // these, but it seems safer to reserve a few just in case we want to // add more built-in profiles in the future. We can also uses special // syntax like cargo:foo if needed. But it is unlikely these will ever // be used. if matches!( lower_name.as_str(), "build" | "check" | "clean" | "config" | "fetch" | "fix" | "install" | "metadata" | "package" | "publish" | "report" | "root" | "run" | "rust" | "rustc" | "rustdoc" | "target" | "tmp" | "uninstall" ) || lower_name.starts_with("cargo") { return Err(ErrorKind::ProfileNameReservedKeyword { name: name.into(), help: "Please choose a different name.", } .into()); } Ok(()) } pub(crate) fn validate_feature_name(name: &str) -> Result<()> { let what = "feature name"; if name.is_empty() { return Err(ErrorKind::Empty(what).into()); } if name.starts_with("dep:") { return Err(ErrorKind::FeatureNameStartsWithDepColon(name.into()).into()); } if name.contains('/') { return Err(ErrorKind::InvalidCharacter { ch: '/', what, name: name.into(), reason: "feature name is not allowed to contain slashes", } .into()); } let mut chars = name.chars(); if let Some(ch) = chars.next() { if !(unicode_xid::UnicodeXID::is_xid_start(ch) || ch == '_' || ch.is_digit(10)) { return Err(ErrorKind::InvalidCharacter { ch, what, name: name.into(), reason: "the first character must be a Unicode XID start character or digit \ (most letters or `_` or `0` to `9`)", } .into()); } } for ch in chars { if !(unicode_xid::UnicodeXID::is_xid_continue(ch) || ch == '-' || ch == '+' || ch == '.') { return Err(ErrorKind::InvalidCharacter { ch, what, name: name.into(), reason: "characters must be Unicode XID characters, '-', `+`, or `.` \ (numbers, `+`, `-`, `_`, `.`, or most letters)", } .into()); } } Ok(()) } pub(crate) fn validate_path_base_name(name: &str) -> Result<()> { validate_name(name, "path base name") } #[cfg(test)] mod tests { use super::*; #[test] fn valid_feature_names() { assert!(validate_feature_name("c++17").is_ok()); assert!(validate_feature_name("128bit").is_ok()); assert!(validate_feature_name("_foo").is_ok()); assert!(validate_feature_name("feat-name").is_ok()); assert!(validate_feature_name("feat_name").is_ok()); assert!(validate_feature_name("foo.bar").is_ok()); assert!(validate_feature_name("").is_err()); assert!(validate_feature_name("+foo").is_err()); assert!(validate_feature_name("-foo").is_err()); assert!(validate_feature_name(".foo").is_err()); assert!(validate_feature_name("dep:bar").is_err()); assert!(validate_feature_name("foo/bar").is_err()); assert!(validate_feature_name("foo:bar").is_err()); assert!(validate_feature_name("foo?").is_err()); assert!(validate_feature_name("?foo").is_err()); assert!(validate_feature_name("ⒶⒷⒸ").is_err()); assert!(validate_feature_name("a¼").is_err()); assert!(validate_feature_name("").is_err()); } } cargo-util-schemas-0.10.0/src/schema.rs000064400000000000000000000010411046102023000157620ustar 00000000000000use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use toml::Value as TomlValue; #[derive(Serialize, Deserialize)] pub struct TomlValueWrapper(pub TomlValue); impl JsonSchema for TomlValueWrapper { fn schema_name() -> std::borrow::Cow<'static, str> { "TomlValue".into() } fn json_schema(generator: &mut schemars::SchemaGenerator) -> schemars::Schema { // HACK: this is both more and less permissive than `TomlValue` but its close generator.subschema_for::().into() } }