diff --git a/Cargo.lock b/Cargo.lock index 412d6cd..a71dde5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,18 +4,18 @@ version = 3 [[package]] name = "addr2line" -version = "0.24.1" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5fb1d8e4442bd405fdfd1dacb42792696b0cf9cb15882e5d097b742a676d375" +checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" dependencies = [ "gimli", ] [[package]] -name = "adler2" -version = "2.0.0" +name = "adler" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "ahash" @@ -56,13 +56,13 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.82" +version = "0.1.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a27b8a3a6e1a44fa4c8baf1f653e4172e81486d4941f2237e20dc2d0cf4ddff1" +checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.72", ] [[package]] @@ -73,17 +73,17 @@ checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "backtrace" -version = "0.3.74" +version = "0.3.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" +checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" dependencies = [ "addr2line", + "cc", "cfg-if", "libc", "miniz_oxide", "object", "rustc-demangle", - "windows-targets 0.52.6", ] [[package]] @@ -158,17 +158,11 @@ version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" -[[package]] -name = "byteorder" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" - [[package]] name = "bytes" -version = "1.7.1" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" +checksum = "a12916984aab3fa6e39d655a33e09c0071eb36d6ab3aea5c2d78551f1df6d952" [[package]] name = "case" @@ -178,12 +172,9 @@ checksum = "fd6c0e7b807d60291f42f33f58480c0bfafe28ed08286446f45e463728cf9c1c" [[package]] name = "cc" -version = "1.1.18" +version = "1.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b62ac837cdb5cb22e10a256099b4fc502b1dfe560cb282963a974d7abd80e476" -dependencies = [ - "shlex", -] +checksum = "2aba8f4e9906c7ce3c73463f62a7f0c65183ada1a2d47e397cc8810827f9694f" [[package]] name = "cfg-if" @@ -213,15 +204,15 @@ checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" [[package]] name = "core-foundation-sys" -version = "0.8.7" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" [[package]] name = "cpufeatures" -version = "0.2.14" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" dependencies = [ "libc", ] @@ -306,8 +297,8 @@ dependencies = [ "convert_case", "proc-macro2", "quote", - "rustc_version 0.4.1", - "syn 2.0.77", + "rustc_version 0.4.0", + "syn 2.0.72", ] [[package]] @@ -416,7 +407,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.72", ] [[package]] @@ -472,9 +463,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.31.0" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32085ea23f3234fc7846555e85283ba4de91e21016dc0455a16286d87a292d64" +checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" [[package]] name = "hashbrown" @@ -572,9 +563,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.5.0" +version = "2.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" dependencies = [ "equivalent", "hashbrown", @@ -594,9 +585,9 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.10.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "187674a687eed5fe42285b40c6291f9a01517d415fad1c3cbc6a9f778af7fcd4" +checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" [[package]] name = "itoa" @@ -606,9 +597,9 @@ checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "js-sys" -version = "0.3.70" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" dependencies = [ "wasm-bindgen", ] @@ -621,9 +612,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.158" +version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" [[package]] name = "linked-hash-map" @@ -686,18 +677,18 @@ checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "miniz_oxide" -version = "0.8.0" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" +checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" dependencies = [ - "adler2", + "adler", ] [[package]] name = "mio" -version = "1.0.2" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" +checksum = "4569e456d394deccd22ce1c1913e6ea0e54519f577285001215d33557431afe4" dependencies = [ "hermit-abi", "libc", @@ -707,7 +698,7 @@ dependencies = [ [[package]] name = "mongod" -version = "0.2.2" +version = "0.2.0" dependencies = [ "chrono", "futures", @@ -794,9 +785,9 @@ dependencies = [ [[package]] name = "object" -version = "0.36.4" +version = "0.36.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "084f1a5821ac4c651660a94a7153d27ac9d8a53736203f58b31945ded098070a" +checksum = "3f203fa8daa7bb185f760ae12bd8e097f63d17041dcdcaf675ac54cdf863170e" dependencies = [ "memchr", ] @@ -865,12 +856,9 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "ppv-lite86" -version = "0.2.20" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" -dependencies = [ - "zerocopy", -] +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "proc-macro2" @@ -889,9 +877,9 @@ checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" [[package]] name = "quote" -version = "1.0.37" +version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" dependencies = [ "proc-macro2", ] @@ -943,9 +931,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.6" +version = "1.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" +checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" dependencies = [ "aho-corasick", "memchr", @@ -1012,9 +1000,9 @@ dependencies = [ [[package]] name = "rustc_version" -version = "0.4.1" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ "semver 1.0.23", ] @@ -1105,9 +1093,9 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "serde" -version = "1.0.210" +version = "1.0.204" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" +checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12" dependencies = [ "serde_derive", ] @@ -1123,24 +1111,23 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.210" +version = "1.0.204" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" +checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.72", ] [[package]] name = "serde_json" -version = "1.0.128" +version = "1.0.120" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" +checksum = "4e0d21c9a8cae1235ad58a00c11cb40d4b1e5c784f1ef2c537876ed6ffd8b7c5" dependencies = [ "indexmap", "itoa", - "memchr", "ryu", "serde", ] @@ -1189,12 +1176,6 @@ dependencies = [ "digest", ] -[[package]] -name = "shlex" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" - [[package]] name = "signal-hook-registry" version = "1.4.2" @@ -1281,9 +1262,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.77" +version = "2.0.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" +checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af" dependencies = [ "proc-macro2", "quote", @@ -1319,7 +1300,7 @@ checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.72", ] [[package]] @@ -1370,9 +1351,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.40.0" +version = "1.39.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998" +checksum = "d040ac2b29ab03b09d4129c2f5bbd012a3ac2f79d38ff506a4bf8dd34b0eac8a" dependencies = [ "backtrace", "bytes", @@ -1393,7 +1374,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.72", ] [[package]] @@ -1408,9 +1389,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.12" +version = "0.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a" +checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" dependencies = [ "bytes", "futures-core", @@ -1490,9 +1471,9 @@ checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" [[package]] name = "unicode-ident" -version = "1.0.13" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-normalization" @@ -1505,9 +1486,9 @@ dependencies = [ [[package]] name = "unicode-properties" -version = "0.1.2" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52ea75f83c0137a9b98608359a5f1af8144876eb67bcb1ce837368e906a9f524" +checksum = "e4259d9d4425d9f0661581b804cb85fe66a4c631cadd8f490d1c13a35d5d9291" [[package]] name = "untrusted" @@ -1538,9 +1519,9 @@ dependencies = [ [[package]] name = "version_check" -version = "0.9.5" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "wasi" @@ -1550,35 +1531,34 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.93" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" dependencies = [ "cfg-if", - "once_cell", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.93" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.72", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.93" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1586,22 +1566,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.93" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.72", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.93" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" [[package]] name = "webpki-roots" @@ -1810,7 +1790,6 @@ version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ - "byteorder", "zerocopy-derive", ] @@ -1822,5 +1801,5 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.72", ] diff --git a/Cargo.toml b/Cargo.toml index 06ccfea..d7418e1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mongod" -version = "0.2.2" +version = "0.2.0" edition = "2021" [features] diff --git a/README.md b/README.md index b2dad84..1a8cbc2 100644 --- a/README.md +++ b/README.md @@ -11,9 +11,9 @@ You can derive the `Model` and `Referencable` traits for your struct. This will use serde::{Deserialize, Serialize}; use mongod::Validate; use mongod::Reference; -use mongod::derive::{Model, Referencable}; +use mongod::derive::{Module, Referencable}; -#[derive(Debug, Clone, Serialize, Deserialize, Model, Referencable)] +#[derive(Debug, Clone, Serialize, Deserialize, Module, Referencable)] struct MyStruct { _id: String, name: String, @@ -105,34 +105,3 @@ let ms = MyStruct::get_partial("someid", &serde_json::json!({"other": 1})).await let myref = ms.other.unwrap(); // will be there let name = ms.name.unwrap() // will panic! ``` - -### Updating values -You can either update the values by passing a JSON object overwriting the current values or update the values using a builder pattern. - -```rust -[...] -struct MyStruct { - _id: String, - name: String, - age: u32, - other: Option, -} - -let mut a = MyStruct::get("someid").await.unwrap(); - -// JSON -a.update(serde_json::json!({"name": "bye"})).await.unwrap(); - -// Builder -let mut changes = a.change(); - -// Set fields -changes = changes.name("bye"); - -// There are type specific functions -// Increment age by 1 -changes = changes.age_increment(1); - -// Finalize -let changed_model: MyStruct = changes.update().await.unwrap(); -``` diff --git a/mongod_derive/src/change_fields.rs b/mongod_derive/src/change_fields.rs deleted file mode 100644 index 96894c3..0000000 --- a/mongod_derive/src/change_fields.rs +++ /dev/null @@ -1,213 +0,0 @@ -use quote::quote; -use syn::Field; - -use crate::{extract_inner_type, is_one_of_type, is_type}; - -/// Generate the ChangeBuilder field fns -pub fn builder_change_fields(field: &Field) -> proc_macro2::TokenStream { - let field_name = &field.ident.as_ref().unwrap(); - let field_type = &field.ty; - let field_name_str = field_name.to_string(); - - // Never update _id - if field_name_str == "_id" { - return quote! {}; - } - - let number_types = [ - "u8", "u16", "u32", "u64", "u128", "usize", "i8", "i16", "i32", "i64", "i128", "isize", - "f16", "f32", "f64", "f128", - ]; - - // Number type fn - if is_one_of_type(field_type, &number_types) { - let documentation = format!("Set the value of `{field_name}`"); - let inc_fn_name = syn::Ident::new(&format!("{}_increment", field_name), field_name.span()); - let doc_inc = format!("Increment value of `{field_name}` by `value`. Consecutive calls to this function will not add up, they overwrite the increment."); - - let mul_fn_name = syn::Ident::new(&format!("{}_multiply", field_name), field_name.span()); - let doc_mul = format!("Multiply value of `{field_name}` by `value`. Consecutive calls to this function will not add up, they overwrite the multiply."); - - return quote! { - #[doc = #documentation] - pub fn #field_name(mut self, value: #field_type)-> Self { - self.model.#field_name = value.into(); - - self.changeset.entry("$set".to_string()).or_insert(mongod::mongodb::bson::doc! {}.into()).as_document_mut().unwrap().insert( - #field_name_str.to_string(), - mongod::mongodb::bson::to_bson(&self.model.#field_name).unwrap(), - ); - - self - } - - #[doc = #doc_inc] - pub fn #inc_fn_name(mut self, value: #field_type) -> Self { - self.model.#field_name += value; - - self.changeset.entry("$inc".to_string()).or_insert(mongod::mongodb::bson::doc! {}.into()).as_document_mut().unwrap() - .insert( - #field_name_str.to_string(), - mongod::mongodb::bson::to_bson(&value).unwrap(), - ); - - self - } - - #[doc = #doc_mul] - pub fn #mul_fn_name(mut self, value: #field_type) -> Self { - self.model.#field_name *= value; - - self.changeset.entry("$mul".to_string()).or_insert(mongod::mongodb::bson::doc! {}.into()).as_document_mut().unwrap() - .insert( - #field_name_str.to_string(), - mongod::mongodb::bson::to_bson(&value).unwrap(), - ); - - self - } - }; - } - - if is_type(field_type, "Vec") { - let inner_field_type = extract_inner_type(field_type, "Vec").unwrap(); - - let push_fn_name = syn::Ident::new(&format!("{}_push", field_name), field_name.span()); - - let documentation = format!("Add a value to the Vec `{field_name}`"); - let documentation2 = format!("Set the value of `{field_name}`"); - - return quote! { - #[doc = #documentation] - pub fn #push_fn_name(mut self, value: T) -> Self where T: Into<#inner_field_type> + serde::Serialize { - let mut push = self.changeset.entry("$push".to_string()).or_insert(mongod::mongodb::bson::doc! {}.into()).as_document_mut().unwrap(); - - if push.contains_key(#field_name_str) { - let current = push.get_mut(#field_name_str.to_string()).unwrap(); - - if current.as_document().map(|x| !x.contains_key("$each")).unwrap_or(true) { - let each = mongod::mongodb::bson::doc! { - "$each": [current, mongod::mongodb::bson::to_bson(&value).unwrap()] - }; - - push.insert(#field_name_str.to_string(), each); - } else { - current.as_document_mut().unwrap().get_mut("$each").unwrap().as_array_mut().unwrap().push(mongod::mongodb::bson::to_bson(&value).unwrap()); - } - } else { - push.insert(#field_name_str.to_string(), mongod::mongodb::bson::to_bson(&value).unwrap()); - } - - self.model.#field_name.push(value.into()); - - self - } - - #[doc = #documentation2] - pub fn #field_name(mut self, value: T)-> Self where T: Into<#field_type> + serde::Serialize { - self.model.#field_name = value.into(); - - self.changeset.entry("$set".to_string()).or_insert(mongod::mongodb::bson::doc! {}.into()).as_document_mut().unwrap().insert( - #field_name_str.to_string(), - mongod::mongodb::bson::to_bson(&self.model.#field_name).unwrap(), - ); - - self - } - }; - } - - if is_type(field_type, "Historic") { - let inner_field_type = extract_inner_type(field_type, "Historic").unwrap(); - - let documentation = format!( - "Update the value of `{field_name}`. This change will be recorded by the `Historic`" - ); - - // Code for Historic - return quote! { - #[doc = #documentation] - pub fn #field_name(mut self, value: T) -> Self where T: Into<#inner_field_type> + serde::Serialize { - self.model.#field_name.update(value.into()); - - self.changeset.entry("$set".to_string()).or_insert(mongod::mongodb::bson::doc! {}.into()).as_document_mut().unwrap().insert( - #field_name_str.to_string(), - mongod::mongodb::bson::to_bson(&self.model.#field_name).unwrap(), - ); - - - self - } - }; - } - - if is_type(field_type, "Option") { - let inner_field_type = extract_inner_type(field_type, "Option").unwrap(); - - if is_type(inner_field_type, "Historic") { - let inner_field_type = extract_inner_type(inner_field_type, "Historic").unwrap(); - - let documentation = format!("Update the value of `{field_name}`. This change will be recorded by the `Historic`. If `{field_name}` is `None` a new `Historic` will be initialized."); - - // Code for Option> - return quote! { - #[doc = #documentation] - pub fn #field_name(mut self, value: T) -> Self where T: Into<#inner_field_type> + serde::Serialize { - if let Some(mut opt) = self.model.#field_name.as_mut() { - opt.update(value.into()); - } else { - self.model.#field_name = Some(mongod::Historic::new(value.into())); - } - - self.changeset.entry("$set".to_string()).or_insert(mongod::mongodb::bson::doc! {}.into()).as_document_mut().unwrap().insert( - #field_name_str.to_string(), - mongod::mongodb::bson::to_bson(&self.model.#field_name).unwrap(), - ); - - self - } - }; - } - - let documentation = format!("Set the value of `{field_name}`. If `Some(_)` it will be updated or removed if it is `None`"); - - return quote! { - #[doc = #documentation] - pub fn #field_name(mut self, value: Option<#inner_field_type>) -> Self { - let is_some = value.is_some(); - - self.model.#field_name = value.into(); - - if is_some { - self.changeset.entry("$set".to_string()).or_insert(mongod::mongodb::bson::doc! {}.into()).as_document_mut().unwrap().insert( - #field_name_str.to_string(), - mongod::mongodb::bson::to_bson(&self.model.#field_name).unwrap(), - ); - } else { - self.changeset.entry("$unset".to_string()).or_insert(mongod::mongodb::bson::doc! {}.into()).as_document_mut().unwrap().insert( - #field_name_str.to_string(), - mongod::mongodb::bson::to_bson("").unwrap(), - ); - } - - self - } - }; - } - - let documentation = format!("Set the value of `{field_name}`"); - // Code for T - quote! { - #[doc = #documentation] - pub fn #field_name(mut self, value: T)-> Self where T: Into<#field_type> + serde::Serialize { - self.model.#field_name = value.into(); - - self.changeset.entry("$set".to_string()).or_insert(mongod::mongodb::bson::doc! {}.into()).as_document_mut().unwrap().insert( - #field_name_str.to_string(), - mongod::mongodb::bson::to_bson(&self.model.#field_name).unwrap(), - ); - - self - } - } -} diff --git a/mongod_derive/src/lib.rs b/mongod_derive/src/lib.rs index 8cae41d..7264223 100644 --- a/mongod_derive/src/lib.rs +++ b/mongod_derive/src/lib.rs @@ -3,18 +3,40 @@ use case::CaseExt; use proc_macro::TokenStream; use quote::quote; -use syn::{parse_macro_input, Data, DeriveInput, Fields}; +use syn::{parse_macro_input, Data, DeriveInput, Fields, Type, TypePath}; -mod types; -use types::*; -mod partial_fields; -use partial_fields::partial_code_field; -mod update_fields; -use update_fields::update_code_field; -mod change_fields; -use change_fields::builder_change_fields; +/// Get inner type. Example: Returns `T` for `Option`. +fn extract_inner_type<'a>(ty: &'a Type, parent: &'a str) -> Option<&'a Type> { + if let Type::Path(type_path) = ty { + if type_path.path.segments.len() == 1 { + let segment = &type_path.path.segments[0]; + if segment.ident == parent { + if let syn::PathArguments::AngleBracketed(ref args) = segment.arguments { + if args.args.len() == 1 { + if let syn::GenericArgument::Type(ref inner_type) = args.args[0] { + return Some(inner_type); + } + } + } + } + } + } + None +} + +fn type_path(ty: &syn::Type) -> TypePath { + if let syn::Type::Path(type_path) = ty { + return type_path.clone(); + } + unreachable!(); +} + +fn is_type(ty: &syn::Type, t: &str) -> bool { + let type_path = type_path(ty); + let id = type_path.path.segments.first().unwrap().ident.to_string(); + id == t +} -/// #[derive(Model)] #[proc_macro_derive(Model)] pub fn model_derive(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as DeriveInput); @@ -22,37 +44,84 @@ pub fn model_derive(input: TokenStream) -> TokenStream { let name = input.ident; let name_str = name.to_string().to_snake(); let partial_name = syn::Ident::new(&format!("Partial{}", name), name.span()); - let changebuilder_name = syn::Ident::new(&format!("Change{}", name), name.span()); - - let update_doc = "Commit the builder changes to DB"; // Generate code for each field let field_code = if let Data::Struct(data_struct) = input.data { match data_struct.fields { Fields::Named(fields_named) => { - // Update code - let field_process_code: Vec<_> = - fields_named.named.iter().map(update_code_field).collect(); + let field_process_code: Vec<_> = fields_named.named.iter().map(|field| { + let field_name = &field.ident.as_ref().unwrap(); + let field_type = &field.ty; + let field_name_str = field_name.to_string(); - // Partial struct fields - let partial_struct: Vec<_> = - fields_named.named.iter().map(partial_code_field).collect(); + if field_name_str == "_id" { + return quote! {}; + } - // Builder functions - let builder_change_fields: Vec<_> = fields_named + if is_type(field_type, "Historic") { + let inner_field_type = extract_inner_type(field_type, "Historic").unwrap(); + if is_type(inner_field_type, "Vec") { + return quote! { + mongod::update_historic_vec!(self, obj, #field_name_str, #field_name, update); + }; + } + + return quote! { + mongod::update_historic_str!(self, obj, #field_name_str, #field_name, update); + } + } + + if is_type(field_type, "Option") { + let inner_field_type = extract_inner_type(field_type, "Option").unwrap(); + + if is_type(inner_field_type, "Historic") { + let sub_inner_field_type = extract_inner_type(inner_field_type, "Historic").unwrap(); + if is_type(sub_inner_field_type, "Reference") { + return quote! { + mongod::update_historic_ref_option!(self, obj, #field_name_str, #field_name, update); + }; + } + } + + return quote! { + mongod::update_value_option!(self, obj, #field_name_str, #field_name, update, #inner_field_type); + }; + } + + quote! { + mongod::update_value!(self, obj, #field_name_str, #field_name, update, #field_type); + } + }).collect(); + + let partial_struct: Vec<_> = fields_named .named .iter() - .map(builder_change_fields) + .map(|field| { + let field_name = &field.ident.as_ref().unwrap(); + let field_type = &field.ty; + let field_name_str = field_name.to_string(); + + if field_name_str == "_id" { + return quote! { + pub _id: String, + }; + } + + if is_type(field_type, "Option") { + return quote! { + pub #field_name: #field_type, + }; + } + + quote! { + pub #field_name: Option<#field_type>, + } + }) .collect(); quote! { impl mongod::model::Model for #name { type Partial = #partial_name; - type ChangeBuilder = #changebuilder_name; - - fn change_builder(self) -> Self::ChangeBuilder { - #changebuilder_name::new(self) - } async fn update_values( &mut self, @@ -68,47 +137,6 @@ pub fn model_derive(input: TokenStream) -> TokenStream { #( #partial_struct )* } - #[derive(Debug)] - pub struct #changebuilder_name { - model: #name, - changeset: mongod::mongodb::bson::Document - } - - impl #changebuilder_name { - pub fn new(model: #name) -> Self { - Self { - model, - changeset: mongod::mongodb::bson::doc! {} - } - } - - #[doc = #update_doc] - pub async fn update(self) -> Result<#name, mongod::model::UpdateError> { - let db = mongod::get_mongo!(); - let collection = mongod::col!(db, <#name as mongod::Referencable>::collection_name()); - - if let Err(msg) = mongod::Validate::validate(&self.model).await { - return Err(mongod::model::UpdateError::Validation(msg)); - } - - let changeset = self.changeset; - - collection - .update_one( - mongod::id_of!(mongod::Referencable::id(&self.model)), - changeset, - None, - ) - .await - .map_err(mongod::model::UpdateError::Database)?; - - Ok(self.model) - } - - #( #builder_change_fields )* - - } - impl mongod::model::reference::Referencable for #partial_name { fn collection_name() -> &'static str { #name_str @@ -129,7 +157,6 @@ pub fn model_derive(input: TokenStream) -> TokenStream { TokenStream::from(field_code) } -/// #[derive(Referencable)] #[proc_macro_derive(Referencable)] pub fn referencable_derive(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as DeriveInput); diff --git a/mongod_derive/src/partial_fields.rs b/mongod_derive/src/partial_fields.rs deleted file mode 100644 index 5234cb0..0000000 --- a/mongod_derive/src/partial_fields.rs +++ /dev/null @@ -1,30 +0,0 @@ -use quote::quote; -use syn::Field; - -use crate::is_type; - -/// Generate struct fields for Partial Model -pub fn partial_code_field(field: &Field) -> proc_macro2::TokenStream { - let field_name = &field.ident.as_ref().unwrap(); - let field_type = &field.ty; - let field_name_str = field_name.to_string(); - - // Keep _id - if field_name_str == "_id" { - return quote! { - pub _id: String, - }; - } - - // Leave Option alone - if is_type(field_type, "Option") { - return quote! { - pub #field_name: #field_type, - }; - } - - // Turn every field into Option - quote! { - pub #field_name: Option<#field_type>, - } -} diff --git a/mongod_derive/src/types.rs b/mongod_derive/src/types.rs deleted file mode 100644 index f4bc087..0000000 --- a/mongod_derive/src/types.rs +++ /dev/null @@ -1,42 +0,0 @@ -use syn::{Type, TypePath}; - -/// Get inner type. Example: Returns `T` for `Option`. -pub fn extract_inner_type<'a>(ty: &'a Type, parent: &'a str) -> Option<&'a Type> { - if let Type::Path(type_path) = ty { - if type_path.path.segments.len() == 1 { - let segment = &type_path.path.segments[0]; - if segment.ident == parent { - if let syn::PathArguments::AngleBracketed(ref args) = segment.arguments { - if args.args.len() == 1 { - if let syn::GenericArgument::Type(ref inner_type) = args.args[0] { - return Some(inner_type); - } - } - } - } - } - } - None -} - -pub fn type_path(ty: &syn::Type) -> TypePath { - if let syn::Type::Path(type_path) = ty { - return type_path.clone(); - } - unreachable!(); -} - -pub fn is_one_of_type(ty: &syn::Type, t: &[&str]) -> bool { - for typ in t { - if is_type(ty, typ) { - return true; - } - } - false -} - -pub fn is_type(ty: &syn::Type, t: &str) -> bool { - let type_path = type_path(ty); - let id = type_path.path.segments.first().unwrap().ident.to_string(); - id == t -} diff --git a/mongod_derive/src/update_fields.rs b/mongod_derive/src/update_fields.rs deleted file mode 100644 index f7bb9bd..0000000 --- a/mongod_derive/src/update_fields.rs +++ /dev/null @@ -1,55 +0,0 @@ -use quote::quote; -use syn::Field; - -use crate::{extract_inner_type, is_type}; - -/// Generate code for the update fn of models -pub fn update_code_field(field: &Field) -> proc_macro2::TokenStream { - let field_name = &field.ident.as_ref().unwrap(); - let field_type = &field.ty; - let field_name_str = field_name.to_string(); - - // Never update _id - if field_name_str == "_id" { - return quote! {}; - } - - if is_type(field_type, "Historic") { - let inner_field_type = extract_inner_type(field_type, "Historic").unwrap(); - if is_type(inner_field_type, "Vec") { - // Custom code Historic> - return quote! { - mongod::update_historic_vec!(self, obj, #field_name_str, #field_name, update); - }; - } - - // Code for Historic - return quote! { - mongod::update_historic_str!(self, obj, #field_name_str, #field_name, update); - }; - } - - if is_type(field_type, "Option") { - let inner_field_type = extract_inner_type(field_type, "Option").unwrap(); - - if is_type(inner_field_type, "Historic") { - let sub_inner_field_type = extract_inner_type(inner_field_type, "Historic").unwrap(); - if is_type(sub_inner_field_type, "Reference") { - // Code for Option> - return quote! { - mongod::update_historic_ref_option!(self, obj, #field_name_str, #field_name, update); - }; - } - } - - // Code for Option - return quote! { - mongod::update_value_option!(self, obj, #field_name_str, #field_name, update, #inner_field_type); - }; - } - - // Code for T - quote! { - mongod::update_value!(self, obj, #field_name_str, #field_name, update, #field_type); - } -} diff --git a/src/lib.rs b/src/lib.rs index a278970..b0f1ee2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,10 +3,8 @@ pub use model::historic::Historic; pub use model::reference::*; pub use model::valid::Validate; pub use model::Model; -pub use model::Sort; pub use mongod_derive as derive; pub use mongodb; - #[cfg(feature = "cache")] pub mod cache; #[cfg(feature = "cache")] @@ -57,7 +55,7 @@ macro_rules! get_mongo { if let Some(client) = $crate::MONGO_CLIENT.get() { client } else { - let client = $crate::mongodb::Client::with_uri_str(&std::env::var("DB_URI").unwrap()) + let client = mongodb::Client::with_uri_str(&std::env::var("DB_URI").unwrap()) .await .unwrap(); $crate::MONGO_CLIENT.set(client).unwrap(); @@ -97,20 +95,3 @@ macro_rules! id_of { $crate::mongodb::bson::doc! { "_id": $id} }; } - -/// A trait to generate a Model API representation in JSON format. -pub trait ToAPI: Sized { - /// Generate public API JSON - fn api(&self) -> impl std::future::Future; -} - -/// Converts a slice of items implementing the `ToAPI` trait into a `Vec` of JSON values. -pub async fn vec_to_api(items: &[impl ToAPI]) -> Vec { - let mut ret = Vec::with_capacity(items.len()); - - for e in items { - ret.push(e.api().await); - } - - ret -} diff --git a/src/model/historic.rs b/src/model/historic.rs index b9f5cc6..85d8286 100644 --- a/src/model/historic.rs +++ b/src/model/historic.rs @@ -3,7 +3,7 @@ use std::{collections::HashMap, ops::Deref}; /// A struct to keep track of historical changes to a value. /// This struct represents a value that has a current state and a history of previous states. -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct Historic { /// The current value pub current: T, @@ -24,13 +24,6 @@ impl Historic { } } -impl Historic { - /// Create a new tracked value initialized with Default - pub fn new_default() -> Historic { - Self::new(T::default()) - } -} - impl Historic { /// Update the value. The change will be recorded. /// Will record a change even if the value is the same as the current one. diff --git a/src/model/mod.rs b/src/model/mod.rs index 57b8079..90df8d3 100644 --- a/src/model/mod.rs +++ b/src/model/mod.rs @@ -8,16 +8,15 @@ use serde::de::DeserializeOwned; use serde_json::{Map, Value}; use valid::Validate; -#[cfg(feature = "cache")] -use crate::{cache_read, cache_write}; - -use crate::{col, collect_results, get_mongo, id_of}; +use crate::{cache_read, cache_write, col, collect_results, get_mongo, id_of}; pub mod historic; pub mod reference; pub mod update; pub mod valid; +// todo : use mongodb projection to only get fields you actually use, maybe PartialModel shadow struct? + /// Error type when updating a model #[derive(Debug)] pub enum UpdateError { @@ -33,7 +32,6 @@ pub trait Model: Sized + Referencable + Validate + serde::Serialize + for<'a> serde::Deserialize<'a> { type Partial: DeserializeOwned; - type ChangeBuilder; /// Insert the `Model` into the database fn insert( @@ -217,7 +215,7 @@ pub trait Model: /// Get all `Model`s from the database #[must_use] fn find_all() -> impl std::future::Future>> { - Self::find(doc! {}, None, None) + Self::find(doc! {}, None) } /// Get all `Model`s partial from the database @@ -225,7 +223,7 @@ pub trait Model: fn find_all_partial( part: serde_json::Value, ) -> impl std::future::Future>> { - Self::find_partial(doc! {}, part, None, None) + Self::find_partial(doc! {}, part, None) } /// Get multiple `Model`s by using a filter from the database. Pass a `limit` parameter to limit the amount of `Model`s returned. @@ -233,15 +231,17 @@ pub trait Model: fn find( filter: mongodb::bson::Document, limit: Option, - sort: Option, ) -> impl std::future::Future>> { async move { let db = get_mongo!(); let collection = col!(db, Self::collection_name()); - - let options = FindOptions::builder().limit(limit).sort(sort).build(); - - let mut results = collection.find(filter, options).await.ok()?; + let mut results = collection + .find( + filter, + limit.map(|x| FindOptions::builder().limit(x).build()), + ) + .await + .ok()?; let docs = collect_results!(results); docs.into_iter() .map(|x| mongodb::bson::from_document(x).unwrap()) @@ -249,28 +249,12 @@ pub trait Model: } } - /// Get unique values of a Models field - fn unique( - filter: mongodb::bson::Document, - field: &str, - ) -> impl std::future::Future> { - async move { - let db = get_mongo!(); - let collection = col!(db, Self::collection_name()); - let res = collection.distinct(field, filter, None).await.unwrap(); - res.into_iter() - .filter_map(|x| mongodb::bson::from_bson(x).ok()) - .collect() - } - } - /// Get multiple partial `Model`s by using a filter from the database. Pass a `limit` parameter to limit the amount of `Model`s returned. #[must_use] fn find_partial( filter: mongodb::bson::Document, mut part: serde_json::Value, limit: Option, - sort: Option, ) -> impl std::future::Future>> { async move { let db = get_mongo!(); @@ -278,13 +262,18 @@ pub trait Model: part.as_object_mut()?.insert("_id".into(), 1.into()); - let options = FindOptions::builder() - .limit(limit) - .sort(sort) - .projection(Some(mongodb::bson::to_document(&part).unwrap())) - .build(); - - let mut results = collection.find(filter, Some(options)).await.ok()?; + let mut results = collection + .find( + filter, + Some( + FindOptions::builder() + .projection(Some(mongodb::bson::to_document(&part).unwrap())) + .limit(limit) + .build(), + ), + ) + .await + .ok()?; let docs = collect_results!(results); docs.into_iter() .map(|x| mongodb::bson::from_document(x).unwrap()) @@ -292,18 +281,6 @@ pub trait Model: } } - fn change_builder(self) -> Self::ChangeBuilder; - - /// Update values of `Model` using a builder pattern - fn change(self) -> Self::ChangeBuilder { - #[cfg(feature = "cache")] - { - cache_write!().invalidate(Self::collection_name(), self.id()); - } - - self.change_builder() - } - /// Update values of `Model` into database fn update( &mut self, @@ -356,28 +333,3 @@ pub trait Model: update: &mut mongodb::bson::Document, ) -> impl std::future::Future + Send; } - -/// Sorting Order -/// -/// # Example -/// ```ignore -/// let m = MyModel::find(doc! {}, None, Some(doc! { -/// "sort_key": Sort::Ascending -/// })).await.unwrap(); -/// ``` -#[derive(Debug)] -pub enum Sort { - /// Sort in ascending order - Ascending, - /// Sort in descending order - Descending, -} - -impl Into for Sort { - fn into(self) -> mongodb::bson::Bson { - match self { - Sort::Ascending => mongodb::bson::bson!(1), - Sort::Descending => mongodb::bson::bson!(-1), - } - } -} diff --git a/src/model/reference.rs b/src/model/reference.rs index be33b3a..f6e963e 100644 --- a/src/model/reference.rs +++ b/src/model/reference.rs @@ -10,14 +10,9 @@ use super::{valid::Validate, Model}; pub struct Reference(String); impl Reference { - /// Create a new reference from String without checks - pub fn new_raw(reference: &str) -> Self { - Self(reference.to_string()) - } - - /// Create a new reference - pub async fn new(model: &str, id: &str) -> Option { - let r = Self(format!("{model}::{id}")); + /// Create a new reference from String + pub async fn new_raw(reference: &str) -> Option { + let r = Self(reference.to_string()); if r.validate().await.is_ok() { Some(r) } else { @@ -25,6 +20,11 @@ impl Reference { } } + /// Create a new reference + pub async fn new(model: &str, id: &str) -> Option { + Self::new_raw(&format!("{model}::{id}")).await + } + /// Get just the ID of the referenced `Model` pub fn id(&self) -> &str { self.0.split_once("::").unwrap().1 @@ -130,11 +130,11 @@ macro_rules! reference_of { ($model:ident, $id:ident) => {{ $model::get_partial($id, serde_json::json!({})) .await - .map(|x| mongod::Referencable::reference(&x)) + .map(|x| x.reference()) }}; ($model:ident, $id:literal) => {{ $model::get_partial($id, serde_json::json!({})) .await - .map(|x| mongod::Referencable::reference(&x)) + .map(|x| x.reference()) }}; }