garde-0.22.1/.cargo_vcs_info.json0000644000000001430000000000100121760ustar { "git": { "sha1": "db0277132ccad6368aad7de4c51ce5f2f7369d23" }, "path_in_vcs": "garde" }garde-0.22.1/.gitignore000064400000000000000000000000241046102023000127540ustar 00000000000000/target /Cargo.lock garde-0.22.1/Cargo.lock0000644000001114150000000000100101560ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 4 [[package]] name = "ahash" version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" dependencies = [ "getrandom", "once_cell", "version_check", ] [[package]] name = "aho-corasick" version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] [[package]] name = "arrayvec" version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" [[package]] name = "autocfg" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "bincode" version = "1.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" dependencies = [ "serde", ] [[package]] name = "bitvec" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" dependencies = [ "funty", "radium", "tap", "wyz", ] [[package]] name = "borsh" version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2506947f73ad44e344215ccd6403ac2ae18cd8e046e581a441bf8d199f257f03" dependencies = [ "borsh-derive", "cfg_aliases", ] [[package]] name = "borsh-derive" version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2593a3b8b938bd68373196c9832f516be11fa487ef4ae745eb282e6a56a7244" dependencies = [ "once_cell", "proc-macro-crate", "proc-macro2", "quote", "syn 2.0.90", ] [[package]] name = "bumpalo" version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] name = "bytecheck" version = "0.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23cdc57ce23ac53c931e88a43d06d070a6fd142f2617be5855eb75efc9beb1c2" dependencies = [ "bytecheck_derive", "ptr_meta", "simdutf8", ] [[package]] name = "bytecheck_derive" version = "0.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3db406d29fbcd95542e92559bed4d8ad92636d1ca8b3b72ede10b4bcc010e659" dependencies = [ "proc-macro2", "quote", "syn 1.0.109", ] [[package]] name = "byteorder" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" [[package]] name = "card-validate" version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "655fa70596e2a38372c0c0c4449ec0166ad9cc43d91558bbecc1a6f38bf9eb91" dependencies = [ "lazy_static", "regex", ] [[package]] name = "castaway" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0abae9be0aaf9ea96a3b1b8b1b55c602ca751eba1b1500220cea4ecbafe7c0d5" dependencies = [ "rustversion", ] [[package]] name = "cc" version = "1.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c31a0499c1dc64f458ad13872de75c0eb7e3fdb0e67964610c914b034fc5956e" dependencies = [ "shlex", ] [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "cfg_aliases" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "compact_str" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6050c3a16ddab2e412160b31f2c871015704239bca62f72f6e5f0be631d3f644" dependencies = [ "castaway", "cfg-if", "itoa", "rustversion", "ryu", "serde", "static_assertions", ] [[package]] name = "console" version = "0.15.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea3c6ecd8059b57859df5c69830340ed3c41d30e3da0c1cbed90a96ac853041b" dependencies = [ "encode_unicode", "libc", "once_cell", "windows-sys", ] [[package]] name = "displaydoc" version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", "syn 2.0.90", ] [[package]] name = "either" version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" [[package]] name = "encode_unicode" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" [[package]] name = "equivalent" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "fnv" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[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 = "funty" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" [[package]] name = "garde" version = "0.22.1" dependencies = [ "card-validate", "compact_str", "garde_derive", "glob", "idna", "insta", "js-sys", "once_cell", "owo-colors", "phonenumber", "regex", "rust_decimal", "serde", "serde_json", "smallvec", "trybuild", "unicode-segmentation", "url", "wasm-bindgen-test", ] [[package]] name = "garde_derive" version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7224c08ec489e2840af29ed882b47f7f6ac8f4ce15c275d9fc0d6d1b94578ae6" dependencies = [ "proc-macro2", "quote", "regex", "syn 2.0.90", ] [[package]] name = "getrandom" version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "libc", "wasi", ] [[package]] name = "glob" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "hashbrown" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" dependencies = [ "ahash", ] [[package]] name = "hashbrown" version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" [[package]] name = "heck" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "icu_collections" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" dependencies = [ "displaydoc", "yoke", "zerofrom", "zerovec", ] [[package]] name = "icu_locid" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" dependencies = [ "displaydoc", "litemap", "tinystr", "writeable", "zerovec", ] [[package]] name = "icu_locid_transform" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" dependencies = [ "displaydoc", "icu_locid", "icu_locid_transform_data", "icu_provider", "tinystr", "zerovec", ] [[package]] name = "icu_locid_transform_data" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" [[package]] name = "icu_normalizer" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" dependencies = [ "displaydoc", "icu_collections", "icu_normalizer_data", "icu_properties", "icu_provider", "smallvec", "utf16_iter", "utf8_iter", "write16", "zerovec", ] [[package]] name = "icu_normalizer_data" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" [[package]] name = "icu_properties" version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" dependencies = [ "displaydoc", "icu_collections", "icu_locid_transform", "icu_properties_data", "icu_provider", "tinystr", "zerovec", ] [[package]] name = "icu_properties_data" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" [[package]] name = "icu_provider" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" dependencies = [ "displaydoc", "icu_locid", "icu_provider_macros", "stable_deref_trait", "tinystr", "writeable", "yoke", "zerofrom", "zerovec", ] [[package]] name = "icu_provider_macros" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" dependencies = [ "proc-macro2", "quote", "syn 2.0.90", ] [[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.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" dependencies = [ "icu_normalizer", "icu_properties", ] [[package]] name = "indexmap" version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" dependencies = [ "equivalent", "hashbrown 0.15.2", ] [[package]] name = "insta" version = "1.41.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7e9ffc4d4892617c50a928c52b2961cb5174b6fc6ebf252b2fac9d21955c48b8" dependencies = [ "console", "lazy_static", "linked-hash-map", "similar", ] [[package]] name = "itertools" version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" dependencies = [ "either", ] [[package]] name = "itoa" version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" [[package]] name = "js-sys" version = "0.3.76" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6717b6b5b077764fb5966237269cb3c64edddde4b14ce42647430a78ced9e7b7" dependencies = [ "once_cell", "wasm-bindgen", ] [[package]] name = "lazy_static" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" version = "0.2.169" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" [[package]] name = "linked-hash-map" version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" [[package]] name = "litemap" version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" [[package]] name = "log" version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "lru-cache" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" dependencies = [ "linked-hash-map", ] [[package]] name = "memchr" version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "minicov" version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f27fe9f1cc3c22e1687f9446c2083c4c5fc7f0bcf1c7a86bdbded14985895b4b" dependencies = [ "cc", "walkdir", ] [[package]] name = "minimal-lexical" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "nom" version = "7.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" dependencies = [ "memchr", "minimal-lexical", ] [[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.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" [[package]] name = "oncemutex" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44d11de466f4a3006fe8a5e7ec84e93b79c70cb992ae0aa0eb631ad2df8abfe2" [[package]] name = "owo-colors" version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb37767f6569cd834a413442455e0f066d0d522de8630436e2a1761d9726ba56" [[package]] name = "percent-encoding" version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "phonenumber" version = "0.3.6+8.13.36" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11756237b57b8cc5e97dc8b1e70ea436324d30e7075de63b14fd15073a8f692a" dependencies = [ "bincode", "either", "fnv", "itertools", "lazy_static", "nom", "quick-xml", "regex", "regex-cache", "serde", "serde_derive", "strum", "thiserror", ] [[package]] name = "ppv-lite86" version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" dependencies = [ "zerocopy", ] [[package]] name = "proc-macro-crate" version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" dependencies = [ "toml_edit", ] [[package]] name = "proc-macro2" version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" dependencies = [ "unicode-ident", ] [[package]] name = "ptr_meta" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1" dependencies = [ "ptr_meta_derive", ] [[package]] name = "ptr_meta_derive" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" dependencies = [ "proc-macro2", "quote", "syn 1.0.109", ] [[package]] name = "quick-xml" version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1004a344b30a54e2ee58d66a71b32d2db2feb0a31f9a2d302bf0536f15de2a33" dependencies = [ "memchr", ] [[package]] name = "quote" version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ "proc-macro2", ] [[package]] name = "radium" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" [[package]] name = "rand" version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", "rand_chacha", "rand_core", ] [[package]] name = "rand_chacha" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", "rand_core", ] [[package]] name = "rand_core" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ "getrandom", ] [[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 0.8.5", ] [[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 0.8.5", ] [[package]] name = "regex-cache" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2f7b62d69743b8b94f353b6b7c3deb4c5582828328bcb8d5fedf214373808793" dependencies = [ "lru-cache", "oncemutex", "regex", "regex-syntax 0.6.29", ] [[package]] name = "regex-syntax" version = "0.6.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "rend" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71fe3824f5629716b1589be05dacd749f6aa084c87e00e016714a8cdfccc997c" dependencies = [ "bytecheck", ] [[package]] name = "rkyv" version = "0.7.45" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9008cd6385b9e161d8229e1f6549dd23c3d022f132a2ea37ac3a10ac4935779b" dependencies = [ "bitvec", "bytecheck", "bytes", "hashbrown 0.12.3", "ptr_meta", "rend", "rkyv_derive", "seahash", "tinyvec", "uuid", ] [[package]] name = "rkyv_derive" version = "0.7.45" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "503d1d27590a2b0a3a4ca4c94755aa2875657196ecbf401a42eff41d7de532c0" dependencies = [ "proc-macro2", "quote", "syn 1.0.109", ] [[package]] name = "rust_decimal" version = "1.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b082d80e3e3cc52b2ed634388d436fe1f4de6af5786cc2de9ba9737527bdf555" dependencies = [ "arrayvec", "borsh", "bytes", "num-traits", "rand", "rkyv", "serde", "serde_json", ] [[package]] name = "rustversion" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248" [[package]] name = "ryu" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[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 = "scoped-tls" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" [[package]] name = "seahash" version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" [[package]] name = "serde" version = "1.0.216" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b9781016e935a97e8beecf0c933758c97a5520d32930e460142b4cd80c6338e" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.216" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "46f859dbbf73865c6627ed570e78961cd3ac92407a2d117204c49232485da55e" dependencies = [ "proc-macro2", "quote", "syn 2.0.90", ] [[package]] name = "serde_json" version = "1.0.133" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377" dependencies = [ "indexmap", "itoa", "memchr", "ryu", "serde", ] [[package]] name = "serde_spanned" version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" dependencies = [ "serde", ] [[package]] name = "shlex" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "simdutf8" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" [[package]] name = "similar" version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1de1d4f81173b03af4c0cbed3c898f6bff5b870e4a7f5d6f4057d62a7a4b686e" [[package]] name = "smallvec" version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" dependencies = [ "serde", ] [[package]] name = "stable_deref_trait" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" [[package]] name = "static_assertions" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "strum" version = "0.26.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" dependencies = [ "strum_macros", ] [[package]] name = "strum_macros" version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" dependencies = [ "heck", "proc-macro2", "quote", "rustversion", "syn 2.0.90", ] [[package]] name = "syn" version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "syn" version = "2.0.90" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "synstructure" version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", "syn 2.0.90", ] [[package]] name = "tap" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "target-triple" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42a4d50cdb458045afc8131fd91b64904da29548bcb63c7236e0844936c13078" [[package]] name = "termcolor" version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" dependencies = [ "winapi-util", ] [[package]] name = "thiserror" version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", "syn 2.0.90", ] [[package]] name = "tinystr" version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" dependencies = [ "displaydoc", "zerovec", ] [[package]] name = "tinyvec" version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "022db8904dfa342efe721985167e9fcd16c29b226db4397ed752a761cfce81e8" dependencies = [ "tinyvec_macros", ] [[package]] name = "tinyvec_macros" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "toml" version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" dependencies = [ "serde", "serde_spanned", "toml_datetime", "toml_edit", ] [[package]] name = "toml_datetime" version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" dependencies = [ "serde", ] [[package]] name = "toml_edit" version = "0.22.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" dependencies = [ "indexmap", "serde", "serde_spanned", "toml_datetime", "winnow", ] [[package]] name = "trybuild" version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8dcd332a5496c026f1e14b7f3d2b7bd98e509660c04239c58b0ba38a12daded4" dependencies = [ "glob", "serde", "serde_derive", "serde_json", "target-triple", "termcolor", "toml", ] [[package]] name = "unicode-ident" version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" [[package]] name = "unicode-segmentation" version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" [[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 = "utf16_iter" version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" [[package]] name = "utf8_iter" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] name = "uuid" version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a" [[package]] name = "version_check" version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[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.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396" dependencies = [ "cfg-if", "once_cell", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79" dependencies = [ "bumpalo", "log", "proc-macro2", "quote", "syn 2.0.90", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" version = "0.4.49" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38176d9b44ea84e9184eff0bc34cc167ed044f816accfe5922e54d84cf48eca2" dependencies = [ "cfg-if", "js-sys", "once_cell", "wasm-bindgen", "web-sys", ] [[package]] name = "wasm-bindgen-macro" version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe" dependencies = [ "quote", "wasm-bindgen-macro-support", ] [[package]] name = "wasm-bindgen-macro-support" version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2" dependencies = [ "proc-macro2", "quote", "syn 2.0.90", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6" [[package]] name = "wasm-bindgen-test" version = "0.3.49" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c61d44563646eb934577f2772656c7ad5e9c90fac78aa8013d776fcdaf24625d" dependencies = [ "js-sys", "minicov", "scoped-tls", "wasm-bindgen", "wasm-bindgen-futures", "wasm-bindgen-test-macro", ] [[package]] name = "wasm-bindgen-test-macro" version = "0.3.49" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "54171416ce73aa0b9c377b51cc3cb542becee1cd678204812e8392e5b0e4a031" dependencies = [ "proc-macro2", "quote", "syn 2.0.90", ] [[package]] name = "web-sys" version = "0.3.76" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04dd7223427d52553d3702c004d3b2fe07c148165faa56313cb00211e31c12bc" dependencies = [ "js-sys", "wasm-bindgen", ] [[package]] name = "winapi-util" version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ "windows-sys", ] [[package]] name = "windows-sys" version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ "windows-targets", ] [[package]] name = "windows-targets" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", "windows_i686_gnu", "windows_i686_gnullvm", "windows_i686_msvc", "windows_x86_64_gnu", "windows_x86_64_gnullvm", "windows_x86_64_msvc", ] [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[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_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" version = "0.6.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" dependencies = [ "memchr", ] [[package]] name = "write16" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" [[package]] name = "writeable" version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" [[package]] name = "wyz" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" dependencies = [ "tap", ] [[package]] name = "yoke" version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" dependencies = [ "serde", "stable_deref_trait", "yoke-derive", "zerofrom", ] [[package]] name = "yoke-derive" version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ "proc-macro2", "quote", "syn 2.0.90", "synstructure", ] [[package]] name = "zerocopy" version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ "byteorder", "zerocopy-derive", ] [[package]] name = "zerocopy-derive" version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", "syn 2.0.90", ] [[package]] name = "zerofrom" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" dependencies = [ "zerofrom-derive", ] [[package]] name = "zerofrom-derive" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" dependencies = [ "proc-macro2", "quote", "syn 2.0.90", "synstructure", ] [[package]] name = "zerovec" version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" dependencies = [ "yoke", "zerofrom", "zerovec-derive", ] [[package]] name = "zerovec-derive" version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ "proc-macro2", "quote", "syn 2.0.90", ] garde-0.22.1/Cargo.toml0000644000000056500000000000100102040ustar # 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 = "2021" rust-version = "1.84" name = "garde" version = "0.22.1" build = false autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = "Validation library" readme = "README.md" keywords = [ "validation", "validate", "valid", ] license = "MIT OR Apache-2.0" repository = "https://github.com/jprochazk/garde" [package.metadata.docs.rs] all-features = true [features] credit-card = ["dep:card-validate"] derive = ["dep:garde_derive"] email = ["regex"] email-idna = ["dep:idna"] full = [ "derive", "serde", "url", "credit-card", "phone-number", "email", "email-idna", "regex", "unicode", ] js-sys = [ "dep:js-sys", "garde_derive?/js-sys", ] pattern = ["regex"] phone-number = ["dep:phonenumber"] regex = [ "dep:regex", "dep:once_cell", "garde_derive?/regex", ] rust_decimal = ["dep:rust_decimal"] serde = [ "dep:serde", "compact_str/serde", "smallvec/serde", ] unicode = ["dep:unicode-segmentation"] url = ["dep:url"] [lib] name = "garde" path = "src/lib.rs" [[test]] name = "rules" path = "tests/rules.rs" [[test]] name = "ui" path = "tests/ui.rs" [dependencies.card-validate] version = "2.3" optional = true [dependencies.compact_str] version = "0.8.0" default-features = false [dependencies.garde_derive] version = "=0.22.1" optional = true default-features = false [dependencies.idna] version = "1" optional = true [dependencies.once_cell] version = "1" optional = true [dependencies.phonenumber] version = "0.3.6+8.13.36" optional = true [dependencies.regex] version = "1" features = ["std"] optional = true default-features = false [dependencies.rust_decimal] version = "1" optional = true [dependencies.serde] version = "1" features = ["derive"] optional = true [dependencies.smallvec] version = "1.11.0" default-features = false [dependencies.unicode-segmentation] version = "1.10.1" optional = true [dependencies.url] version = "2" optional = true [dev-dependencies.glob] version = "0.3.1" [dev-dependencies.insta] version = "1.29" [dev-dependencies.owo-colors] version = "4" [dev-dependencies.serde_json] version = "1.0.112" features = ["preserve_order"] [dev-dependencies.trybuild] version = "1.0" [target.'cfg(all(target_arch = "wasm32", target_os = "unknown"))'.dependencies.js-sys] version = "0.3" optional = true [target.'cfg(all(target_arch = "wasm32", target_os = "unknown"))'.dev-dependencies.wasm-bindgen-test] version = "0.3.38" garde-0.22.1/Cargo.toml.orig000064400000000000000000000043231046102023000136610ustar 00000000000000[package] name = "garde" version.workspace = true rust-version.workspace = true edition = "2021" repository = "https://github.com/jprochazk/garde" license = "MIT OR Apache-2.0" description = "Validation library" keywords = ["validation", "validate", "valid"] readme = "../README.md" [package.metadata.docs.rs] all-features = true # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [features] full = [ "derive", "serde", "url", "credit-card", "phone-number", "email", "email-idna", "regex", "unicode", ] serde = ["dep:serde", "compact_str/serde", "smallvec/serde"] derive = ["dep:garde_derive"] url = ["dep:url"] unicode = ["dep:unicode-segmentation"] credit-card = ["dep:card-validate"] phone-number = ["dep:phonenumber"] email = ["regex"] email-idna = ["dep:idna"] regex = ["dep:regex", "dep:once_cell", "garde_derive?/regex"] # for backward compatibility with <0.14.0 pattern = ["regex"] js-sys = ["dep:js-sys", "garde_derive?/js-sys"] rust_decimal = ["dep:rust_decimal"] [dependencies] # Workspace garde_derive = { workspace = true, optional = true, default-features = false } card-validate = { version = "2.3", optional = true } compact_str = { version = "0.8.0", default-features = false } idna = { version = "1", optional = true } once_cell = { version = "1", optional = true } phonenumber = { version = "0.3.6+8.13.36", optional = true } regex = { version = "1", default-features = false, features = ["std"], optional = true } rust_decimal = { version = "1", optional = true } serde = { version = "1", features = ["derive"], optional = true } smallvec = { version = "1.11.0", default-features = false } unicode-segmentation = { version = "1.10.1", optional = true } url = { version = "2", optional = true } [target.'cfg(all(target_arch = "wasm32", target_os = "unknown"))'.dependencies] js-sys = { version = "0.3", optional = true } [dev-dependencies] glob = "0.3.1" insta = { version = "1.29" } owo-colors = { version = "4" } serde_json = { version = "1.0.112", features = ["preserve_order"] } trybuild = { version = "1.0" } [target.'cfg(all(target_arch = "wasm32", target_os = "unknown"))'.dev-dependencies] wasm-bindgen-test = "0.3.38" garde-0.22.1/README.md000064400000000000000000000534601046102023000122570ustar 00000000000000# Garde   [![Documentation]][docs.rs] [![Latest Version]][crates.io] [docs.rs]: https://docs.rs/garde/latest/garde/ [crates.io]: https://crates.io/crates/garde [Documentation]: https://img.shields.io/docsrs/garde [Latest Version]: https://img.shields.io/crates/v/garde.svg A Rust validation library - [Basic usage example](#basic-usage-example) - [Validation rules](#available-validation-rules) - [Length modes](#length-modes) - [Inner type validation](#inner-type-validation) - [Newtypes](#newtypes) - [Handling Option](#handling-option) - [Custom validation](#custom-validation) - [Context/Self access](#contextself-access) - [Implementing rules](#implementing-rules) - [Implementing `Validate`](#implementing-validate) - [Rule adapters](#rule-adapters) - [Integration with web frameworks](#integration-with-web-frameworks) - [Feature flags](#feature-flags) - [Why `garde`?](#why-garde) ### Basic usage example To get started, install `garde`: ```text,ignore cargo add garde -F full ``` And attach the `Validate` derive to your type. `garde` will generate an implementation of the `Validate` trait for you, allowing you to call the `validate` method. Here's what that looks like in full: ```rust use garde::{Validate, Valid}; #[derive(Validate)] struct User<'a> { #[garde(ascii, length(min=3, max=25))] username: &'a str, #[garde(length(min=15))] password: &'a str, } let user = User { username: "test", password: "not_a_very_good_password", }; if let Err(e) = user.validate() { println!("invalid user: {e}"); } ``` Garde can also validate enums: ```rust use garde::{Validate, Valid}; #[derive(Validate)] enum Data { Struct { #[garde(range(min=-10, max=10))] field: i32, }, Tuple( #[garde(ascii)] String ), } let data = Data::Struct { field: 100 }; if let Err(e) = data.validate() { println!("invalid data: {e}"); } ``` ### Available validation rules | name | format | validation | feature flag | |--------------|---------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------| -------------- | | required | `#[garde(required)]` | is value set | - | | ascii | `#[garde(ascii)]` | only contains ASCII | - | | alphanumeric | `#[garde(alphanumeric)]` | only letters and digits | - | | email | `#[garde(email)]` | an email according to the HTML5 spec[^1] | `email` | | url | `#[garde(url)]` | a URL | `url` | | ip | `#[garde(ip)]` | an IP address (either IPv4 or IPv6) | - | | ipv4 | `#[garde(ipv4)]` | an IPv4 address | - | | ipv6 | `#[garde(ipv6)]` | an IPv6 address | - | | credit card | `#[garde(credit_card)]` | a credit card number | `credit-card` | | phone number | `#[garde(phone_number)]` | a phone number | `phone-number` | | length | `#[garde(length(, min=, max=, equal=)]` | a container with length in `min..=max` or `equal` | - | | matches | `#[garde(matches())]` | a field matches another field | - | | range | `#[garde(range(min=, max=, equal=))]` | a number in the range `min..=max` or `equal` | - | | contains | `#[garde(contains())]` | a string-like value containing a substring | - | | prefix | `#[garde(prefix())]` | a string-like value prefixed by some string | - | | suffix | `#[garde(suffix())]` | a string-like value suffixed by some string | - | | pattern | `#[garde(pattern(""))]` | a string-like value matching some regular expression | `regex` | | pattern | `#[garde(pattern())]` | a string-like value matched by some [Matcher](https://docs.rs/garde/latest/garde/rules/pattern/trait.Matcher.html) | - | | dive | `#[garde(dive)]` | nested validation, calls `validate` on the value | - | | skip | `#[garde(skip)]` | skip validation | - | | custom | `#[garde(custom())]` | a custom validator | - | Additional notes: - `required` is only available for `Option` fields. - `dive` accepts an optional context: `#[garde(dive(self.other_field))]` - The `` argument for `length` is [explained here](#length-modes) - For `length` and `range`: - If `equal` is defined, `min` and `max` must be omitted. - Assuming `equal` is omitted, either `min` or `max` may be omitted, but not both. - `min` and `max` use an *inclusive* upper bound (`min..=max`). Setting `min == max` is equivalent to using `equal`. - For `contains`, `prefix`, and `suffix`, the pattern must be a string literal, because the `Pattern` API [is currently unstable](https://github.com/rust-lang/rust/issues/27721). - Garde does not enable the default features of the `regex` crate - if you need extra regex features (e.g. Unicode) or better performance, add a dependency on `regex = "1"` to your `Cargo.toml`. If most of the fields on your struct are annotated with `#[garde(skip)]`, you may use `#[garde(allow_unvalidated)]` instead: ```rust #[derive(garde::Validate)] struct Foo<'a> { #[garde(length(min = 1))] a: &'a str, #[garde(skip)] b: &'a str, // this field will not be validated } #[derive(garde::Validate)] #[garde(allow_unvalidated)] struct Bar<'a> { #[garde(length(min = 1))] a: &'a str, b: &'a str, // this field will not be validated // note the lack of `#[garde(skip)]` } ``` ### Length modes The `length` rule accepts an optional `mode` argument, which determines what _kind_ of length it will validate. The options are: - `simple` - `bytes` - `graphemes` - `utf16` - `chars` The `simple` is the default used when the `mode` argument is omitted. The meaning of "simple length" depends on the type. It is currently implemented for strings, where it validates the number of bytes, and `std::collections`, where it validates the number of items. ```rust #[derive(garde::Validate)] struct Foo { #[garde(length(min = 1, max = 100))] string: String, #[garde(length(min = 1, max = 100))] collection: Vec } ``` The `bytes`, `graphemes`, `utf16`, and `chars` exist mostly for string validation: - `bytes` validates the number of _bytes_ - `graphemens` uses the [`unicode-segmentation`](https://docs.rs/unicode-segmentation) crate, and validates the number of _graphemes_ - `utf16` uses [`encode_utf16`](https://doc.rust-lang.org/stable/std/primitive.str.html#method.encode_utf16), and validates the number of UTF-16 _code points_ - `chars` uses [`chars`](https://doc.rust-lang.org/stable/std/primitive.str.html#method.chars), and validates the number of _unicode scalar values_ ```rust #[derive(garde::Validate)] struct Foo { #[garde(length(bytes, min = 1, max = 100))] a: String, // `a.len()` #[garde(length(graphemes, min = 1, max = 100))] b: String, // `b.graphemes().count()` #[garde(length(utf16, min = 1, max = 100))] c: String, // `c.encode_utf16().count()` #[garde(length(chars, min = 1, max = 100))] d: String, // `d.chars().count()` } ``` ### Inner type validation If you need to validate the "inner" type of a container, such as the `String` in `Vec`, then use the `inner` modifier: ```rust #[derive(garde::Validate)] struct Test { #[garde( length(min = 1), inner(ascii, length(min = 1)), // wrap the rule in `inner` )] items: Vec, } ``` The above type would fail validation if: - the `Vec` is empty - any of the inner `String` elements is empty - any of the inner `String` elements contains non-ASCII characters To validate a deeply-nested type, such as `Vec>`, the `inner` modifier must be nested for each level of generics: ```rust #[derive(garde::Validate)] struct Test { #[garde( length(min = 1), // applies to `Vec` inner(inner(ascii, length(min = 1))), // applies to `String` )] items: Vec>, } ``` You can apply separate rules to every level of the nested type: ```rust #[derive(garde::Validate)] struct Test { #[garde( length(min = 1), // applies to `Vec` inner(required), // applies to `Option` inner(inner(ascii, length(min = 1))), // applies to `String` )] items: Vec>, } ``` ### Newtypes The best way to re-use validation rules on a field is to use the [newtype idiom](https://doc.rust-lang.org/rust-by-example/generics/new_types.html) with `#[garde(transparent)]`: ```rust #[derive(garde::Validate)] #[garde(transparent)] struct Username(#[garde(length(min = 3, max = 20))] String); #[derive(garde::Validate)] struct User { // later used with `dive`: #[garde(dive)] username: Username, } ``` The `username` field in the above example will inherit all the validation rules from the `String` field on `Username`. The result is that the error path will be flattened by one level, resulting in cleaner error messages: ```rust,ignore User { username: Username("") }.validate() "username: length is lower than 3" ``` Without the `#[garde(transparent)]` attribute, it would instead be: ```rust,ignore User { username: Username("") }.validate() "username[0]: length is lower than 3" ``` Structs with the `#[garde(transparent)]` attribute may have more than one field, but there must be only one unskipped field. That means every field other than the one you wish to validate must be `#[garde(skip)]`. ### Handling Option Every rule works on `Option` fields. The field will only be validated if it is `Some`. If you additionally want to validate that the `Option` field is `Some`, use the `required` rule: ```rust #[derive(garde::Validate)] struct Test { #[garde(required, ascii, length(min = 1))] value: Option, } ``` The above type would fail validation if: - `value` is `None` - the inner `value` is empty - the inner `value` contains non-ASCII characters ### Custom validation Validation may be customized via the `custom` rule, and the `context` attribute. The context may be any type without generic parameters. By default, the context is `()`. ```rust,ignore #[derive(garde::Validate)] #[garde(context(PasswordContext))] struct User { #[garde(custom(is_strong_password))] password: String, } struct PasswordContext { min_entropy: f32, entropy: cracken::password_entropy::EntropyEstimator, } fn is_strong_password(value: &str, context: &PasswordContext) -> garde::Result { let bits = context.entropy.estimate_password_entropy(value.as_bytes()) .map(|e| e.mask_entropy) .unwrap_or(0.0); if bits < context.min_entropy { return Err(garde::Error::new("password is not strong enough")); } Ok(()) } let ctx = PasswordContext { /* ... */ }; let user = User { /* ... */ }; user.validate(&ctx)?; ``` The validator function may accept the value as a reference to any type which it derefs to. In the above example, it is possible to use `&str`, because `password` is a `String`, and `String` derefs to `&str`. The `#[garde(custom(...))]` attribute accepts any expression which evalutes to a something which implements the following trait: ```rust,ignore FnOnce(&T, &::Context) -> garde::Result ``` That means it's possible to use higher order functions: ```rust // Returns a function which does the actual validation. fn my_equals(other: &str) -> impl FnOnce(&str, &()) -> garde::Result + '_ { move |value, _| { if value != other { return Err(garde::Error::new(format!("not equal to {other}"))); } Ok(()) } } #[derive(garde::Validate)] struct User { #[garde(length(min = 1, max = 255))] password: String, // Combined with `self` access in rules: #[garde(custom(my_equals(&self.password2)))] password2: String, } ``` ### Context/Self access It's generally possible to also access the context and `self`, because they are in scope in the output of the proc macro: ```rust struct Limits { min: usize, max: usize, } struct Config { username: Limits, } #[derive(garde::Validate)] #[garde(context(Config as ctx))] struct User { #[garde(length(min = ctx.username.min, max = ctx.username.max))] username: String, } ``` ### Implementing rules Say you want to implement length checking for a custom string-like type. To do this, you would implement one of the `length` traits for it, depending on what kind of validation you are looking for. ```rust #[repr(transparent)] pub struct MyString(String); impl garde::rules::length::HasSimpleLength for MyString { fn length(&self) -> usize { self.0.len() } } #[derive(garde::Validate)] struct Foo { // Now the `length` check may be used with `MyString` #[garde(length(min = 1, max = 1000))] field: MyString, } ``` Each rule comes with its own trait that may be implemented by custom types in your code. They are all available under `garde::rules`. ### Implementing `Validate` In case you have a container type for which you'd like to support nested validation (using the `#[garde(dive)]` rule), you may implement `Validate` for it: ```rust #[repr(transparent)] struct MyVec(Vec); impl garde::Validate for MyVec { type Context = T::Context; fn validate_into( &self, ctx: &Self::Context, mut parent: &mut dyn FnMut() -> garde::Path, report: &mut garde::Report ) { for (index, item) in self.0.iter().enumerate() { let mut path = garde::util::nested_path!(parent, index); item.validate_into(ctx, &mut path, report); } } } #[derive(garde::Validate)] struct Foo { #[garde(dive)] field: MyVec, } #[derive(garde::Validate)] struct Bar { #[garde(range(min = 1, max = 10))] value: u32, } ``` ### Rule adapters Adapters allow you to implement validation for third-party types without using a newtype. An adapter may look like this: ```rust mod my_str_adapter { #![allow(unused_imports)] pub use garde::rules::*; // re-export garde's rules pub mod length { pub use garde::rules::length::*; // re-export `length` rules pub mod simple { // re-implement `simple`, but _only_ for the concrete type &str! pub fn apply(v: &str, (min, max): (usize, usize)) -> garde::Result { if !(min..=max).contains(&v.len()) { Err(garde::Error::new("my custom error message")) } else { Ok(()) } } } } } ``` You create a module, add a public glob re-export of `garde::rules` inside of it, and then re-implement the specific rule you're interested in. This is a form of [duck typing](https://en.wikipedia.org/wiki/Duck_typing). Any rule which you have not re-implemented is simply delegated to `garde`'s impl. It's quite verbose, but in exchange it is maximally flexible. To use the adapter, add an `adapt` attribute to a field: ```rust,ignore #[derive(garde::Validate)] struct Stuff<'a> { #[garde( adapt(my_str_adapter), length(min = 1), ascii, )] v: &'a str, } ``` The `length` rule will now use your custom implementation, but the `ascii` rule will continue to use `garde`'s implementation. ### Integration with web frameworks - [`axum`](https://crates.io/crates/axum): [`axum-valid`](https://crates.io/crates/axum-valid) - [`actix-web`](https://crates.io/crates/actix-web): [`garde-actix-web`](https://crates.io/crates/garde-actix-web) ### Feature flags | name | description | extra dependencies | | -------------- | -------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- | | `derive` | Enables the usage of the `derive(Validate)` macro | [`garde_derive`](https://crates.io/crates/garde_derive) | | `url` | Validation of URLs via the `url` crate. | [`url`](https://crates.io/crates/url) | | `email` | Validation of emails according to [HTML5](https://html.spec.whatwg.org/multipage/forms.html#valid-e-mail-address) | [`regex`](https://crates.io/crates/regex), [`once_cell`](https://crates.io/crates/once_cell) | | `email-idna` | Support for [Internationalizing Domain Names for Applications](https://url.spec.whatwg.org/#idna) in email addresses | [`idna`](https://crates.io/crates/idna) | | `regex` | Support for regular expressions in `pattern` via the `regex` crate | [`regex`](https://crates.io/crates/regex), [`once_cell`](https://crates.io/crates/once_cell) | | `credit-card` | Validation of credit card numbers via the `card-validate` crate | [`card-validate`](https://crates.io/crates/card-validate) | | `phone-number` | Validation of phone numbers via the `phonenumber` crate | [`phonenumber`](https://crates.io/crates/phonenumber) | | `unicode` | Validation of grapheme count via the `unicode-segmentation` crate | [`unicode-segmentation`](https://docs.rs/unicode-segmentation) | ### Why `garde`? Garde means guard in French. I am not French, nor do I speak the language, but `guard` was taken, and this is close enough :). ### Development Contributing to `garde` only requires a somewhat recent version of [`Rust`](https://www.rust-lang.org/learn/get-started). This repository also makes use of the following tools, but they are optional: - [`insta`](https://insta.rs/) for snapshot testing ([tests/rules](./garde_derive_tests/tests/rules/)). ### License Licensed under either of - Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or ) - MIT license ([LICENSE-MIT](LICENSE-MIT) or ) at your option. ### Contribution Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. ### Acknowledgements This crate is heavily inspired by the [validator](https://github.com/Keats/validator) crate. It is essentially a full rewrite of `validator`. The creation of this crate was prompted by [this comment](https://github.com/Keats/validator/issues/201#issuecomment-1167018511) and a few others talking about a potential rewrite. [^1]: [HTML5 forms - valid email address](https://html.spec.whatwg.org/multipage/forms.html#valid-e-mail-address) garde-0.22.1/src/error/rc_list/mod.rs000064400000000000000000000070441046102023000155010ustar 00000000000000use std::mem::{swap, transmute}; use std::sync::Arc; /// A reverse singly-linked list. /// /// Each node in the list is reference counted, /// meaning cloning the list is safe and cheap. /// /// We're optimizing for cloning the list and /// appending an item onto its end, both of which /// are O(1). #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct List { node: Option>>, length: usize, } impl List { pub fn new() -> Self { Self { node: None, length: 0, } } pub fn len(&self) -> usize { self.length } pub fn is_empty(&self) -> bool { self.length == 0 } pub fn append(&self, value: T) -> Self { Self { node: Some(Arc::new(Node { prev: self.node.clone(), value, })), length: self.length + 1, } } pub fn iter(&self) -> Iter<'_, T> { Iter::new(self) } } #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] struct Node { prev: Option>>, value: T, } pub struct Iter<'a, T> { list: &'a List, next: Option>>, node: Option>>, } impl<'a, T> Iter<'a, T> { fn new(list: &'a List) -> Self { Self { list, next: None, node: list.node.clone(), } } } impl<'a, T> Iterator for Iter<'a, T> { type Item = &'a T; fn next(&mut self) -> Option { let mut node = self.node.take(); swap(&mut self.next, &mut node); if let Some(prev) = self.next.as_ref().and_then(|node| node.prev.as_ref()) { self.node = Some(Arc::clone(prev)); } self.next.as_ref().map(|next| { // SAFETY: // We're returning a reference here, but the reference points // to the inside of an `Arc>`, meaning the reference // to it is valid for as long as the `Arc` lives. It lives for // as long as the `list` it came from, which is longer than // `self` here. // The items within `list` will never be moved around or // mutated in any way, because it is immutable. The only // supported operation is `append`, which constructs a new // list with a pointer to the old one. // The borrow checker will ensure that the items do not // outlive their parent `list`. unsafe { transmute(&next.value) } }) } } #[cfg(test)] mod tests { use super::*; const _: () = { fn assert() {} let _ = assert::>; }; #[test] fn rc_list_shenanigans() { let list = List::new(); assert_eq!(list.len(), 0); let mut iter = list.iter(); let item = iter.next(); drop(iter); println!("{item:?}"); let a = list.append("a"); let b = list.append("b"); let c_a = a.append("c"); let d_c_a = c_a.append("d"); let mut iter = list.iter(); let item = iter.next(); drop(iter); println!("{item:?}"); assert_eq!(a.len(), 1); assert_eq!(a.iter().copied().collect::>(), ["a"]); assert_eq!(b.len(), 1); assert_eq!(b.iter().copied().collect::>(), ["b"]); assert_eq!(c_a.len(), 2); assert_eq!(c_a.iter().copied().collect::>(), ["c", "a"]); assert_eq!(d_c_a.len(), 3); assert_eq!(d_c_a.iter().copied().collect::>(), ["d", "c", "a"]); } } garde-0.22.1/src/error.rs000064400000000000000000000216751046102023000132710ustar 00000000000000//! Error types used by `garde`. //! //! The entrypoint of this module is the [`Error`] type. #![allow(dead_code)] mod rc_list; use std::borrow::Cow; use compact_str::{CompactString, ToCompactString}; use smallvec::SmallVec; use self::rc_list::List; /// A validation error report. /// /// This type is used as a container for errors aggregated during validation. /// It is a flat list of `(Path, Error)`. /// A single field or list item may have any number of errors attached to it. /// /// It is possible to extract all errors for specific field using the [`select`][`crate::select`] macro. #[derive(Clone, Debug)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Report { errors: Vec<(Path, Error)>, } impl Report { /// Create an empty [`Report`]. #[allow(clippy::new_without_default)] pub fn new() -> Self { Self { errors: Vec::new() } } /// Append an [`Error`] into this report at the given [`Path`]. pub fn append(&mut self, path: Path, error: Error) { self.errors.push((path, error)); } /// Iterate over all `(Path, Error)` pairs. pub fn iter(&self) -> impl Iterator { self.errors.iter() } /// Returns `true` if the report contains no validation errors. pub fn is_empty(&self) -> bool { self.errors.is_empty() } /// Converts into the inner validation errors. pub fn into_inner(self) -> Vec<(Path, Error)> { self.errors } } impl std::fmt::Display for Report { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { for (path, error) in self.iter() { if path.is_empty() { writeln!(f, "{error}")?; } else { writeln!(f, "{path}: {error}")?; } } Ok(()) } } impl std::error::Error for Report {} #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Error { message: CompactString, } impl Error { pub fn new(message: impl ToCompactString) -> Self { Self { message: message.to_compact_string(), } } pub fn message(&self) -> &str { self.message.as_ref() } } impl std::fmt::Display for Error { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "{}", self.message) } } impl std::error::Error for Error {} #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Path { components: List<(Kind, CompactString)>, } #[doc(hidden)] #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] pub enum Kind { None, Key, Index, } /// Represents a path component without a key. This is useful when the container /// only ever holds a single key, which is the case for any 1-tuple struct. /// /// For an example usage, see the implementation of [Inner][`crate::rules::inner::Inner`] for `Option`. #[derive(Default)] pub struct NoKey(()); impl std::fmt::Display for NoKey { fn fmt(&self, _: &mut std::fmt::Formatter) -> std::fmt::Result { Ok(()) } } pub trait PathComponentKind: std::fmt::Display + ToCompactString { fn component_kind() -> Kind; } macro_rules! impl_path_component_kind { ($(@$($G:lifetime)*;)? $T:ty => $which:ident) => { impl $(<$($G),*>)? PathComponentKind for $T { fn component_kind() -> Kind { Kind::$which } } } } impl_path_component_kind!(usize => Index); impl_path_component_kind!(@'a; &'a str => Key); impl_path_component_kind!(@'a; Cow<'a, str> => Key); impl_path_component_kind!(String => Key); impl_path_component_kind!(CompactString => Key); impl_path_component_kind!(NoKey => None); impl PathComponentKind for &T { fn component_kind() -> Kind { T::component_kind() } } impl Path { pub fn empty() -> Self { Self { components: List::new(), } } pub fn len(&self) -> usize { self.components.len() } pub fn is_empty(&self) -> bool { self.components.is_empty() } pub fn new(component: C) -> Self { Self { components: List::new().append((C::component_kind(), component.to_compact_string())), } } pub fn join(&self, component: C) -> Self { Self { components: self .components .append((C::component_kind(), component.to_compact_string())), } } #[doc(hidden)] pub fn __iter( &self, ) -> impl DoubleEndedIterator + ExactSizeIterator { let mut components = TempComponents::with_capacity(self.components.len()); for (kind, component) in self.components.iter() { components.push((*kind, component)); } components.into_iter() } } type TempComponents<'a> = SmallVec<[(Kind, &'a CompactString); 8]>; impl std::fmt::Debug for Path { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { struct Components<'a> { path: &'a Path, } impl std::fmt::Debug for Components<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let mut list = f.debug_list(); list.entries(self.path.__iter().rev().map(|(_, c)| c)) .finish() } } f.debug_struct("Path") .field("components", &Components { path: self }) .finish() } } impl std::fmt::Display for Path { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { let mut components = self.__iter().rev().peekable(); let mut first = true; while let Some((kind, component)) = components.next() { if first && kind == Kind::Index { f.write_str("[")?; } first = false; f.write_str(component.as_str())?; if kind == Kind::Index { f.write_str("]")?; } if let Some((kind, _)) = components.peek() { match kind { Kind::None => {} Kind::Key => f.write_str(".")?, Kind::Index => f.write_str("[")?, } } } Ok(()) } } #[cfg(feature = "serde")] impl serde::Serialize for Path { fn serialize(&self, serializer: S) -> Result where S: serde::Serializer, { use serde::ser::SerializeSeq as _; let components = self.__iter().rev(); let mut seq = serializer.serialize_seq(Some(components.len()))?; for component in components { seq.serialize_element(&component)?; } seq.end() } } #[cfg(feature = "serde")] impl<'de> serde::Deserialize<'de> for Path { fn deserialize(deserializer: D) -> Result where D: serde::Deserializer<'de>, { let mut components = List::new(); for v in SmallVec::<[(Kind, CompactString); 8]>::deserialize(deserializer)? { components = components.append(v); } Ok(Path { components }) } } #[cfg(test)] mod tests { use super::*; const _: () = { fn assert() {} let _ = assert::; }; #[test] fn path_join() { let path = Path::new("a").join("b").join("c"); assert_eq!(path.to_string(), "a.b.c"); } #[test] fn report_select() { let mut report = Report::new(); report.append(Path::new("a").join("b"), Error::new("lol")); report.append( Path::new("a").join("b").join("c"), Error::new("that seems wrong"), ); report.append(Path::new("a").join("b").join("c"), Error::new("pog")); report.append(Path::new("array").join("0").join("c"), Error::new("pog")); assert_eq!( crate::select!(report, a.b.c).collect::>(), [&Error::new("that seems wrong"), &Error::new("pog")] ); assert_eq!( crate::select!(report, array[0].c).collect::>(), [&Error::new("pog")] ); } #[cfg(feature = "serde")] mod serde { use super::*; #[test] fn roundtrip_serde() { let mut report = Report::new(); report.append(Path::new("a").join(0), Error::new("lorem")); report.append(Path::new("a").join(1), Error::new("ispum")); report.append(Path::new("a").join(2), Error::new("dolor")); report.append(Path::new("b").join("c"), Error::new("dolor")); let de: Report = serde_json::from_str(&serde_json::to_string(&report).unwrap()).unwrap(); assert_eq!(report.errors, de.errors); } } } garde-0.22.1/src/lib.rs000064400000000000000000000033741046102023000127020ustar 00000000000000#![doc = include_str!("../README.md")] pub mod error; pub mod rules; pub mod validate; pub use error::{Error, Path, Report}; #[cfg(feature = "derive")] pub use garde_derive::{select, Validate}; pub use validate::{Unvalidated, Valid, Validate}; pub type Result = ::core::result::Result<(), Error>; pub mod external { pub use {compact_str, smallvec}; } #[doc(hidden)] pub mod util { use crate::error::PathComponentKind; use crate::Path; #[inline] pub fn __make_nested_path<'a, C: PathComponentKind + Clone + 'a>( mut parent: impl FnMut() -> Path + 'a, component: C, ) -> impl FnMut() -> Path + 'a { let mut nested = None::; #[inline] move || MaybeJoin::maybe_join(&mut nested, &mut parent, || component.clone()) } #[doc(hidden)] #[macro_export] macro_rules! __nested_path { ($parent:ident, $key:expr) => { $crate::util::__make_nested_path(&mut $parent, &$key) }; } pub use crate::__nested_path as nested_path; pub trait MaybeJoin { fn maybe_join(&mut self, parent: P, component: CF) -> Path where C: PathComponentKind, P: FnMut() -> Path, CF: Fn() -> C; } impl MaybeJoin for Option { #[inline] fn maybe_join(&mut self, mut parent: P, component: CF) -> Path where C: PathComponentKind, P: FnMut() -> Path, CF: Fn() -> C, { match self { Some(path) => path.clone(), None => { let path = parent().join(component()); *self = Some(path.clone()); path } } } } } garde-0.22.1/src/rules/alphanumeric.rs000064400000000000000000000020441046102023000157270ustar 00000000000000//! Alphanumeric validation. //! //! ```rust //! #[derive(garde::Validate)] //! struct Test { //! #[garde(alphanumeric)] //! v: String, //! } //! ``` //! //! The entrypoint is the [`Alphanumeric`] trait. Implementing this trait for a type allows that type to be used with the `#[garde(alphanumeric)]` rule. //! //! This trait has a blanket implementation for all `T: garde::rules::AsStr`. use super::AsStr; use crate::error::Error; pub fn apply(v: &T, _: ()) -> Result<(), Error> { if !v.validate_alphanumeric() { return Err(Error::new("not alphanumeric")); } Ok(()) } pub trait Alphanumeric { fn validate_alphanumeric(&self) -> bool; } impl Alphanumeric for T { fn validate_alphanumeric(&self) -> bool { self.as_str().chars().all(|c| c.is_alphanumeric()) } } impl Alphanumeric for Option { fn validate_alphanumeric(&self) -> bool { match self { Some(value) => value.validate_alphanumeric(), None => true, } } } garde-0.22.1/src/rules/ascii.rs000064400000000000000000000016411046102023000143510ustar 00000000000000//! ASCII validation. //! //! ```rust //! #[derive(garde::Validate)] //! struct Test { //! #[garde(ascii)] //! v: String, //! } //! ``` //! //! The entrypoint is the [`Ascii`] trait. Implementing this trait for a type allows that type to be used with the `#[garde(ascii)]` rule. //! //! This trait has a blanket implementation for all `T: garde::rules::AsStr`. use super::AsStr; use crate::error::Error; pub fn apply(v: &T, _: ()) -> Result<(), Error> { if !v.validate_ascii() { return Err(Error::new("not ascii")); } Ok(()) } pub trait Ascii { fn validate_ascii(&self) -> bool; } impl Ascii for T { fn validate_ascii(&self) -> bool { self.as_str().is_ascii() } } impl Ascii for Option { fn validate_ascii(&self) -> bool { match self { Some(value) => value.validate_ascii(), None => true, } } } garde-0.22.1/src/rules/contains.rs000064400000000000000000000021671046102023000151030ustar 00000000000000//! Substring validation. //! //! ```rust //! const STR: &str = "test"; //! //! #[derive(garde::Validate)] //! struct Test { //! #[garde(contains("test"))] //! v: String, //! #[garde(contains(STR))] //! w: String, //! } //! ``` //! //! The entrypoint is the [`Contains`] trait. Implementing this trait for a type allows that type to be used with the `#[garde(contains)]` rule. //! //! This trait has a blanket implementation for all `T: garde::rules::AsStr`. use super::AsStr; use crate::error::Error; pub fn apply(v: &T, (pat,): (&str,)) -> Result<(), Error> { if !v.validate_contains(pat) { return Err(Error::new(format!("does not contain \"{pat}\""))); } Ok(()) } pub trait Contains { fn validate_contains(&self, pat: &str) -> bool; } impl Contains for T { fn validate_contains(&self, pat: &str) -> bool { self.as_str().contains(pat) } } impl Contains for Option { fn validate_contains(&self, pat: &str) -> bool { match self { Some(value) => value.validate_contains(pat), None => true, } } } garde-0.22.1/src/rules/credit_card.rs000064400000000000000000000037651046102023000155350ustar 00000000000000//! Credit card validation using the [`card_validate`] crate. //! //! ```rust //! #[derive(garde::Validate)] //! struct Test { //! #[garde(credit_card)] //! v: String, //! } //! ``` //! //! The entrypoint is the [`CreditCard`] trait. Implementing this trait for a type allows that type to be used with the `#[garde(credit_card)]` rule. //! //! This trait has a blanket implementation for all `T: garde::rules::AsStr`. use std::fmt::Display; use super::AsStr; use crate::error::Error; pub fn apply(v: &T, _: ()) -> Result<(), Error> { if let Err(e) = v.validate_credit_card() { return Err(Error::new(format!("not a valid credit card number: {e}"))); } Ok(()) } pub trait CreditCard { type Error: Display; fn validate_credit_card(&self) -> Result<(), Self::Error>; } impl CreditCard for T { type Error = InvalidCard; fn validate_credit_card(&self) -> Result<(), Self::Error> { let _ = card_validate::Validate::from(self.as_str())?; Ok(()) } } impl CreditCard for Option { type Error = T::Error; fn validate_credit_card(&self) -> Result<(), Self::Error> { match self { Some(value) => value.validate_credit_card(), None => Ok(()), } } } pub struct InvalidCard(card_validate::ValidateError); impl Display for InvalidCard { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match &self.0 { card_validate::ValidateError::InvalidFormat => write!(f, "invalid format"), card_validate::ValidateError::InvalidLength => write!(f, "invalid length"), card_validate::ValidateError::InvalidLuhn => write!(f, "invalid luhn"), card_validate::ValidateError::UnknownType => write!(f, "unknown type"), _ => write!(f, "unknown error"), } } } impl From for InvalidCard { fn from(value: card_validate::ValidateError) -> Self { Self(value) } } garde-0.22.1/src/rules/email.rs000064400000000000000000000233461046102023000143560ustar 00000000000000//! Email validation. //! //! ```rust //! #[derive(garde::Validate)] //! struct Test { //! #[garde(email)] //! v: String, //! } //! ``` //! //! The entrypoint is the [`Email`] trait. Implementing this trait for a type allows that type to be used with the `#[garde(email)]` rule. //! //! This trait has a blanket implementation for all `T: garde::rules::AsStr`. use std::fmt::Display; use std::str::FromStr; use super::pattern::Matcher; use super::AsStr; use crate::error::Error; macro_rules! init_regex { ($var:ident => $p:literal) => { #[cfg(not(all(feature = "js-sys", target_arch = "wasm32", target_os = "unknown")))] static $var: $crate::rules::pattern::regex::StaticPattern = $crate::rules::pattern::regex::init_pattern!($p); #[cfg(all(feature = "js-sys", target_arch = "wasm32", target_os = "unknown"))] static $var: $crate::rules::pattern::regex_js_sys::StaticPattern = $crate::rules::pattern::regex_js_sys::init_pattern!($p); }; } pub fn apply(v: &T, _: ()) -> Result<(), Error> { if let Err(e) = v.validate_email() { return Err(Error::new(format!("not a valid email: {e}"))); } Ok(()) } pub trait Email { type Error: Display; fn validate_email(&self) -> Result<(), Self::Error>; } impl Email for T { type Error = InvalidEmail; fn validate_email(&self) -> Result<(), Self::Error> { parse_email(self.as_str()) } } impl Email for Option { type Error = T::Error; fn validate_email(&self) -> Result<(), Self::Error> { match self { Some(value) => value.validate_email(), None => Ok(()), } } } #[derive(Debug, Clone, Copy, PartialEq)] pub enum InvalidEmail { Empty, MissingAt, UserLengthExceeded, InvalidUser, DomainLengthExceeded, InvalidDomain, } impl Display for InvalidEmail { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { InvalidEmail::Empty => write!(f, "value is empty"), InvalidEmail::MissingAt => write!(f, "value is missing `@`"), InvalidEmail::UserLengthExceeded => { write!(f, "user length exceeded maximum of 64 characters") } InvalidEmail::InvalidUser => write!(f, "user contains unexpected characters"), InvalidEmail::DomainLengthExceeded => { write!(f, "domain length exceeded maximum of 255 characters") } InvalidEmail::InvalidDomain => write!(f, "domain contains unexpected characters"), } } } pub fn parse_email(s: &str) -> Result<(), InvalidEmail> { if s.is_empty() { return Err(InvalidEmail::Empty); } let (user, domain) = s.split_once('@').ok_or(InvalidEmail::MissingAt)?; if user.len() > 64 { return Err(InvalidEmail::UserLengthExceeded); } init_regex! { USER_RE => r"(?i-u)^[a-z0-9.!#$%&'*+/=?^_`{|}~-]+\z" } if !USER_RE.is_match(user) { return Err(InvalidEmail::InvalidUser); } if domain.len() > 255 { return Err(InvalidEmail::DomainLengthExceeded); } if !is_valid_domain(domain) { #[cfg(not(feature = "email-idna"))] { return Err(InvalidEmail::InvalidDomain); } #[cfg(feature = "email-idna")] { match idna::domain_to_ascii_cow(domain.as_bytes(), idna::AsciiDenyList::URL) { Ok(domain) => { if !is_valid_domain(&domain) { return Err(InvalidEmail::InvalidDomain); } } Err(_) => return Err(InvalidEmail::InvalidDomain), } } } Ok(()) } fn is_valid_domain(domain: &str) -> bool { init_regex! { DOMAIN_NAME_RE => r"(?i-u)^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)*$" }; if DOMAIN_NAME_RE.is_match(domain) { return true; } if is_smtp_addr(domain) { return true; } false } fn is_smtp_addr(domain: &str) -> bool { let domain = match domain.strip_prefix('[') { Some(domain) => domain, None => return false, }; let domain = match domain.strip_suffix(']') { Some(domain) => domain, None => return false, }; std::net::IpAddr::from_str(domain).is_ok() } // Tests taken from `validator`, modified for this API // https://github.com/Keats/validator/blob/09efa7e78e6fbc853a6a56af6904a00e2e6632b8/validator/src/validation/email.rs#L76 #[cfg(test)] mod tests { use std::borrow::Cow; use super::*; #[test] fn test_parse_email() { // Test cases taken from Django // https://github.com/django/django/blob/master/tests/validators/tests.py#L48 let tests = &[ ("email@here.com", None), ("weirder-email@here.and.there.com", None), (r#"!def!xyz%abc@example.com"#, None), ("email@[127.0.0.1]", None), ("email@[2001:dB8::1]", None), ("email@[2001:dB8:0:0:0:0:0:1]", None), ("email@[::fffF:127.0.0.1]", None), ("example@valid-----hyphens.com", None), ("example@valid-with-hyphens.com", None), ("test@domain.with.idn.tld.उदाहरण.परीक्षा", None), ( r#""test@test"@example.com"#, Some(InvalidEmail::InvalidUser), ), // max length for domain name labels is 63 characters per RFC 1034 ( "a@atm.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", None, ), ( "a@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.atm", None, ), ( "a@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.bbbbbbbbbb.atm", None, ), // 64 * a ( "a@atm.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", Some(InvalidEmail::InvalidDomain), ), ("", Some(InvalidEmail::Empty)), ("abc", Some(InvalidEmail::MissingAt)), ("abc@", Some(InvalidEmail::InvalidDomain)), ("abc@bar", None), ("a @x.cz", Some(InvalidEmail::InvalidUser)), ("abc@.com", Some(InvalidEmail::InvalidDomain)), ( "something@@somewhere.com", Some(InvalidEmail::InvalidDomain), ), ("email@127.0.0.1", None), ("email@[127.0.0.256]", Some(InvalidEmail::InvalidDomain)), ("email@[2001:db8::12345]", Some(InvalidEmail::InvalidDomain)), ( "email@[2001:db8:0:0:0:0:1]", Some(InvalidEmail::InvalidDomain), ), ( "email@[::ffff:127.0.0.256]", Some(InvalidEmail::InvalidDomain), ), ("example@invalid-.com", Some(InvalidEmail::InvalidDomain)), ("example@-invalid.com", Some(InvalidEmail::InvalidDomain)), ("example@invalid.com-", Some(InvalidEmail::InvalidDomain)), ("example@inv-.alid-.com", Some(InvalidEmail::InvalidDomain)), ("example@inv-.-alid.com", Some(InvalidEmail::InvalidDomain)), ( r#"test@example.com\n\n