From 88f09c9cb09cb62210344e56826fcadaf9b7d36f Mon Sep 17 00:00:00 2001 From: Florian Amsallem Date: Fri, 23 Feb 2024 16:17:30 +0100 Subject: [PATCH 1/9] editoast: add support serde support for chrono::duration Co-authored-by: Youness Chrifi Alaoui --- editoast/Cargo.lock | 663 ++++++++++++++------------ editoast/Cargo.toml | 1 + editoast/src/schema/utils/duration.rs | 165 +++++++ editoast/src/schema/utils/mod.rs | 2 + 4 files changed, 533 insertions(+), 298 deletions(-) create mode 100644 editoast/src/schema/utils/duration.rs diff --git a/editoast/Cargo.lock b/editoast/Cargo.lock index 2be910608ad..e7bf605c15c 100644 --- a/editoast/Cargo.lock +++ b/editoast/Cargo.lock @@ -4,11 +4,11 @@ version = 3 [[package]] name = "actix-codec" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "617a8268e3537fe1d8c9ead925fca49ef6400927ee7bc26750e90ecee14ce4b8" +checksum = "5f7b0a21988c1bf877cf4759ef5ddaac04c1c9fe808c9142ecb78ba97d97a28a" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.4.2", "bytes", "futures-core", "futures-sink", @@ -31,7 +31,7 @@ dependencies = [ "futures-util", "log", "once_cell", - "smallvec 1.12.0", + "smallvec 1.13.1", ] [[package]] @@ -44,7 +44,7 @@ dependencies = [ "actix-service", "actix-utils", "actix-web", - "bitflags 2.4.1", + "bitflags 2.4.2", "bytes", "derive_more", "futures-core", @@ -59,9 +59,9 @@ dependencies = [ [[package]] name = "actix-http" -version = "3.5.1" +version = "3.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "129d4c88e98860e1758c5de288d1632b07970a16d59bdf7b8d66053d582bb71f" +checksum = "d223b13fd481fc0d1f83bb12659ae774d9e3601814c68a0bc539731698cca743" dependencies = [ "actix-codec", "actix-rt", @@ -69,7 +69,7 @@ dependencies = [ "actix-utils", "ahash", "base64", - "bitflags 2.4.1", + "bitflags 2.4.2", "brotli", "bytes", "bytestring", @@ -89,7 +89,7 @@ dependencies = [ "pin-project-lite", "rand 0.8.5", "sha1", - "smallvec 1.12.0", + "smallvec 1.13.1", "tokio", "tokio-util", "tracing", @@ -103,7 +103,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e01ed3140b2f8d422c68afa1ed2e85d996ea619c988ac834d255db32138655cb" dependencies = [ "quote", - "syn 2.0.48", + "syn 2.0.50", ] [[package]] @@ -141,7 +141,7 @@ dependencies = [ "parse-size", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.50", ] [[package]] @@ -179,7 +179,7 @@ dependencies = [ "futures-core", "futures-util", "mio", - "socket2 0.5.5", + "socket2 0.5.6", "tokio", "tracing", ] @@ -207,9 +207,9 @@ dependencies = [ [[package]] name = "actix-web" -version = "4.4.1" +version = "4.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e43428f3bf11dee6d166b00ec2df4e3aa8cc1606aaa0b7433c146852e2f4e03b" +checksum = "43a6556ddebb638c2358714d853257ed226ece6023ef9364f23f0c70737ea984" dependencies = [ "actix-codec", "actix-http", @@ -239,8 +239,8 @@ dependencies = [ "serde", "serde_json", "serde_urlencoded", - "smallvec 1.12.0", - "socket2 0.5.5", + "smallvec 1.13.1", + "socket2 0.5.6", "time", "url", ] @@ -254,7 +254,7 @@ dependencies = [ "actix-router", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.50", ] [[package]] @@ -274,9 +274,9 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "ahash" -version = "0.8.7" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77c3a9648d43b9cd48db467b3f87fdd6e146bcc88ab0180006cef2179fe11d01" +checksum = "d713b3834d76b85304d4d525563c1276e2e30dc97cc67bfb4585a4a29fc2c89f" dependencies = [ "cfg-if", "getrandom", @@ -326,9 +326,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.7" +version = "0.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cd2405b3ac1faab2990b74d728624cd9fd115651fcecc7c2d8daf01376275ba" +checksum = "96b09b5178381e0874812a9b157f7fe84982617e48f71f4e3235482775e5b540" dependencies = [ "anstyle", "anstyle-parse", @@ -340,9 +340,9 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.4" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" +checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" [[package]] name = "anstyle-parse" @@ -410,13 +410,13 @@ dependencies = [ [[package]] name = "async-channel" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ca33f4bc4ed1babef42cad36cc1f51fa88be00420404e5b1e80ab1b18f7678c" +checksum = "f28243a43d821d11341ab73c80bed182dc015c514b951616cf79bd4af39af0c3" dependencies = [ "concurrent-queue", - "event-listener 4.0.3", - "event-listener-strategy", + "event-listener 5.1.0", + "event-listener-strategy 0.5.0", "futures-core", "pin-project-lite", ] @@ -441,9 +441,9 @@ version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05b1b633a2115cd122d73b955eadd9916c18c8f510ec9cd1686404c60ad1c29c" dependencies = [ - "async-channel 2.1.1", + "async-channel 2.2.0", "async-executor", - "async-io 2.2.2", + "async-io 2.3.1", "async-lock 3.3.0", "blocking", "futures-lite 2.2.0", @@ -473,9 +473,9 @@ dependencies = [ [[package]] name = "async-io" -version = "2.2.2" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6afaa937395a620e33dc6a742c593c01aced20aa376ffb0f628121198578ccc7" +checksum = "8f97ab0c5b00a7cdbe5a371b9a782ee7be1316095885c8a4ea1daf490eb0ef65" dependencies = [ "async-lock 3.3.0", "cfg-if", @@ -483,8 +483,8 @@ dependencies = [ "futures-io", "futures-lite 2.2.0", "parking", - "polling 3.3.2", - "rustix 0.38.30", + "polling 3.5.0", + "rustix 0.38.31", "slab", "tracing", "windows-sys 0.52.0", @@ -506,7 +506,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d034b430882f8381900d3fe6f0aaa3ad94f2cb4ac519b429692a1bc2dda4ae7b" dependencies = [ "event-listener 4.0.3", - "event-listener-strategy", + "event-listener-strategy 0.4.0", "pin-project-lite", ] @@ -551,7 +551,7 @@ checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.50", ] [[package]] @@ -601,9 +601,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.1" +version = "2.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" +checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" [[package]] name = "block-buffer" @@ -620,7 +620,7 @@ version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a37913e8dc4ddcc604f0c6d3bf2887c995153af3611de9e23c352b44c1b9118" dependencies = [ - "async-channel 2.1.1", + "async-channel 2.2.0", "async-lock 3.3.0", "async-task", "fastrand 2.0.1", @@ -653,15 +653,15 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.14.0" +version = "3.15.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" +checksum = "8ea184aa71bb362a1157c896979544cc23974e08fd265f29ea96b59f0b4a555b" [[package]] name = "bytemuck" -version = "1.14.0" +version = "1.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "374d28ec25809ee0e23827c2ab573d729e293f281dfe393500e7ad618baa61c6" +checksum = "a2ef034f05691a48569bd920a96c81b9d91bbad1ab5ac7c4616c1f6ef36cb79f" [[package]] name = "byteorder" @@ -692,11 +692,10 @@ checksum = "fdd7a427adc0135366d99db65b36dae9237130997e560ed61118041fb72be6e8" [[package]] name = "cc" -version = "1.0.83" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +checksum = "7f9fa1897e4325be0d68d48df6aa1a71ac2ed4d27723887e7754192705350730" dependencies = [ - "jobserver", "libc", ] @@ -718,9 +717,9 @@ dependencies = [ [[package]] name = "chrono" -version = "0.4.31" +version = "0.4.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" +checksum = "5bc015644b92d5890fab7489e49d21f879d5c990186827d42ec511919404f38b" dependencies = [ "android-tzdata", "iana-time-zone", @@ -728,14 +727,14 @@ dependencies = [ "num-traits", "serde", "wasm-bindgen", - "windows-targets 0.48.5", + "windows-targets 0.52.3", ] [[package]] name = "clap" -version = "4.4.16" +version = "4.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58e54881c004cec7895b0068a0a954cd5d62da01aef83fa35b1e594497bf5445" +checksum = "c918d541ef2913577a0f9566e9ce27cb35b6df072075769e0b26cb5a554520da" dependencies = [ "clap_builder", "clap_derive", @@ -743,33 +742,33 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.4.16" +version = "4.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59cb82d7f531603d2fd1f507441cdd35184fa81beff7bd489570de7f773460bb" +checksum = "9f3e7391dad68afb0c2ede1bf619f579a3dc9c2ec67f089baa397123a2f3d1eb" dependencies = [ "anstream", "anstyle", "clap_lex", - "strsim", + "strsim 0.11.0", ] [[package]] name = "clap_derive" -version = "4.4.7" +version = "4.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" +checksum = "307bc0538d5f0f83b8248db3087aa92fe504e4691294d0c96c0eabc33f47ba47" dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.50", ] [[package]] name = "clap_lex" -version = "0.6.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" +checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" [[package]] name = "color_quant" @@ -866,9 +865,9 @@ checksum = "338089f42c427b86394a5ee60ff321da23a5c89c9d89514c829687b26359fcff" [[package]] name = "crc32fast" -version = "1.3.2" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa" dependencies = [ "cfg-if", ] @@ -937,9 +936,9 @@ dependencies = [ [[package]] name = "darling" -version = "0.20.3" +version = "0.20.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0209d94da627ab5605dcccf08bb18afa5009cfbef48d8a8b7d7bdbc79be25c5e" +checksum = "3a5d17510e4a1a87f323de70b7b1eaac1ee0e37866c6720b2d279452d0edf389" dependencies = [ "darling_core", "darling_macro", @@ -947,27 +946,27 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.20.3" +version = "0.20.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "177e3443818124b357d8e76f53be906d60937f0d3a90773a664fa63fa253e621" +checksum = "a98eea36a7ff910fa751413d0895551143a8ea41d695d9798ec7d665df7f7f5e" dependencies = [ "fnv", "ident_case", "proc-macro2", "quote", - "strsim", - "syn 2.0.48", + "strsim 0.10.0", + "syn 2.0.50", ] [[package]] name = "darling_macro" -version = "0.20.3" +version = "0.20.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" +checksum = "d6a366a3f90c5d59a4b91169775f88e52e8f71a0e7804cc98a8db2932cf4ed57" dependencies = [ "darling_core", "quote", - "syn 2.0.48", + "syn 2.0.50", ] [[package]] @@ -1021,7 +1020,7 @@ dependencies = [ "proc-macro2", "quote", "semver", - "syn 2.0.48", + "syn 2.0.50", ] [[package]] @@ -1063,7 +1062,7 @@ version = "2.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62c6fcf842f17f8c78ecf7c81d75c5ce84436b41ee07e03f490fbb5f5a8731d8" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.4.2", "byteorder", "chrono", "diesel_derives", @@ -1097,7 +1096,7 @@ dependencies = [ "diesel_table_macro_syntax", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.50", ] [[package]] @@ -1117,7 +1116,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc5557efc453706fed5e4fa85006fe9817c224c3f480a34c7e5959fd700921c5" dependencies = [ - "syn 2.0.48", + "syn 2.0.50", ] [[package]] @@ -1146,7 +1145,7 @@ dependencies = [ "lazy_static", "regex", "serde", - "strsim", + "strsim 0.10.0", ] [[package]] @@ -1177,6 +1176,7 @@ dependencies = [ "heck", "image", "inventory", + "iso8601", "itertools", "json-patch", "mvt", @@ -1225,14 +1225,14 @@ dependencies = [ "proc-macro2", "quote", "serde_json", - "syn 2.0.48", + "syn 2.0.50", ] [[package]] name = "either" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" +checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" [[package]] name = "encoding_rs" @@ -1260,7 +1260,7 @@ checksum = "f282cfdfe92516eb26c2af8589c274c7c17681f5ecc03c18255fe741c6aa64eb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.50", ] [[package]] @@ -1296,6 +1296,17 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "event-listener" +version = "5.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7ad6fd685ce13acd6d9541a30f6db6567a7a24c9ffd4ba2955d29e3f22c8b27" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + [[package]] name = "event-listener-strategy" version = "0.4.0" @@ -1306,11 +1317,21 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "event-listener-strategy" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "feedafcaa9b749175d5ac357452a9d41ea2911da598fde46ce1fe02c37751291" +dependencies = [ + "event-listener 5.1.0", + "pin-project-lite", +] + [[package]] name = "exr" -version = "1.71.0" +version = "1.72.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "832a761f35ab3e6664babfbdc6cef35a4860e816ec3916dcfd0882954e98a8a8" +checksum = "887d93f60543e9a9362ef8a21beedd0a833c5d9610e18c67abe15a5963dcb1a4" dependencies = [ "bit_field", "flume", @@ -1318,7 +1339,7 @@ dependencies = [ "lebe", "miniz_oxide", "rayon-core", - "smallvec 1.12.0", + "smallvec 1.13.1", "zune-inflate", ] @@ -1345,9 +1366,9 @@ checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" [[package]] name = "fdeflate" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "209098dd6dfc4445aa6111f0e98653ac323eaa4dfd212c9ca3931bf9955c31bd" +checksum = "4f9bfee30e4dedf0ab8b422f03af778d9612b63f502710fc500a334ebe2de645" dependencies = [ "simd-adler32", ] @@ -1541,7 +1562,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.50", ] [[package]] @@ -1558,9 +1579,9 @@ checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" [[package]] name = "futures-timer" -version = "3.0.2" +version = "3.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c" +checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24" [[package]] name = "futures-util" @@ -1616,9 +1637,9 @@ dependencies = [ [[package]] name = "geos" -version = "8.3.0" +version = "8.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c82f7975ac793c4ebf6d48e58d1f30401587d97068cb3b8cc1da054590a1aacb" +checksum = "b57a162bbfe8866b1ab898aad47e7fe634a9177c4b1bacc80af04d2b45772a46" dependencies = [ "c_vec", "geojson", @@ -1629,9 +1650,9 @@ dependencies = [ [[package]] name = "geos-sys" -version = "2.0.5" +version = "2.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2d813c12b0d614852eca1e6381cdeab09d34c31113442219f6ea7c0cafb3859" +checksum = "4dc873d24aefc72aa94c3c1c251afb82beb7be5926002746c0e1f585fef9854c" dependencies = [ "libc", "pkg-config", @@ -1651,9 +1672,9 @@ dependencies = [ [[package]] name = "gif" -version = "0.12.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80792593675e051cf94a4b111980da2ba60d4a83e43e0048c5693baab3977045" +checksum = "3fb2d69b19215e18bb912fa30f7ce15846e301408695e44e0ef719f1da9e19f2" dependencies = [ "color_quant", "weezl", @@ -1685,9 +1706,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.23" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b553656127a00601c8ae5590fcfdc118e4083a7924b6cf4ffc1ea4b99dc429d7" +checksum = "bb2c4422095b67ee78da96fbb51a4cc413b3b25883c7717ff7ca1ab31022c9c9" dependencies = [ "bytes", "fnv", @@ -1704,10 +1725,11 @@ dependencies = [ [[package]] name = "half" -version = "2.2.1" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02b4af3693f1b705df946e9fe5631932443781d0aabb423b62fcd4d73f6d2fd0" +checksum = "bc52e53916c08643f1b56ec082790d1e86a32e58dc5268f897f313fbae7b4872" dependencies = [ + "cfg-if", "crunchy", ] @@ -1725,9 +1747,9 @@ checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "hermit-abi" -version = "0.3.3" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" +checksum = "bd5256b483761cd23699d0da46cc6fd2ee3be420bbe6d020ae4a091e70b7e9fd" [[package]] name = "hex" @@ -1812,7 +1834,7 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", - "socket2 0.5.5", + "socket2 0.5.6", "tokio", "tower-service", "tracing", @@ -1834,9 +1856,9 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.59" +version = "0.1.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6a67363e2aa4443928ce15e57ebae94fd8949958fd1223c4cfc0cd473ad7539" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -1889,9 +1911,9 @@ checksum = "cb56e1aa765b4b4f3aadfab769793b7087bb03a4ea4920644a6d238e2df5b9ed" [[package]] name = "image" -version = "0.24.8" +version = "0.24.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "034bbe799d1909622a74d1193aa50147769440040ff36cb2baa947609b0a4e23" +checksum = "5690139d2f55868e080017335e4b94cb7414274c74f1669c84fb5feba2c9f69d" dependencies = [ "bytemuck", "byteorder", @@ -1907,9 +1929,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.1.0" +version = "2.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" +checksum = "233cf39063f058ea2caae4091bf4a3ef70a653afbc026f5c4a4135d114e3c177" dependencies = [ "equivalent", "hashbrown", @@ -1936,9 +1958,9 @@ dependencies = [ [[package]] name = "inventory" -version = "0.3.14" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8573b2b1fb643a372c73b23f4da5f888677feef3305146d68a539250a9bccc7" +checksum = "f958d3d68f4167080a18141e10381e7634563984a537f2a49a30fd8e53ac5767" [[package]] name = "io-lifetimes" @@ -1957,11 +1979,20 @@ version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" +[[package]] +name = "iso8601" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "924e5d73ea28f59011fec52a0d12185d496a9b075d360657aed2a5707f701153" +dependencies = [ + "nom", +] + [[package]] name = "itertools" -version = "0.12.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" dependencies = [ "either", ] @@ -1972,15 +2003,6 @@ version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" -[[package]] -name = "jobserver" -version = "0.1.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c37f63953c4c63420ed5fd3d6d398c719489b9f872b9fa683262f8edd363c7d" -dependencies = [ - "libc", -] - [[package]] name = "jpeg-decoder" version = "0.3.1" @@ -1992,9 +2014,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.67" +version = "0.3.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a1d36f1235bc969acba30b7f5990b864423a6068a10f7c90ae8f0112e3a59d1" +checksum = "406cda4b368d531c842222cf9d2600a9a4acce8d29423695379c6868a143a9ee" dependencies = [ "wasm-bindgen", ] @@ -2040,9 +2062,9 @@ checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" [[package]] name = "libc" -version = "0.2.152" +version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" [[package]] name = "libm" @@ -2058,9 +2080,9 @@ checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" [[package]] name = "linux-raw-sys" -version = "0.4.12" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" +checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" [[package]] name = "local-channel" @@ -2151,11 +2173,17 @@ dependencies = [ "unicase", ] +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "miniz_oxide" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" dependencies = [ "adler", "simd-adler32", @@ -2175,9 +2203,9 @@ dependencies = [ [[package]] name = "mvt" -version = "0.9.0" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1aef0702a843e65e8e544babd859771fb8d0c9d5da58e438ede9610cf78034d" +checksum = "de84789f070ec86d558f34d64b8afcf194ad9878dbdd03eb57268d44bfac4809" dependencies = [ "log", "num-traits", @@ -2204,6 +2232,16 @@ dependencies = [ "tempfile", ] +[[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 = "nu-ansi-term" version = "0.46.0" @@ -2241,28 +2279,33 @@ dependencies = [ [[package]] name = "num-complex" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ba157ca0885411de85d6ca030ba7e2a83a28636056c7c699b07c8b6f7383214" +checksum = "23c6602fda94a57c990fe0df199a035d83576b496aa29f4e634a8ac6004e68a6" dependencies = [ "num-traits", ] +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + [[package]] name = "num-integer" -version = "0.1.45" +version = "0.1.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" dependencies = [ - "autocfg", "num-traits", ] [[package]] name = "num-iter" -version = "0.1.43" +version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" +checksum = "d869c01cc0c455284163fd0092f1f93835385ccab5a98a0dcc497b2f8bf055a9" dependencies = [ "autocfg", "num-integer", @@ -2283,9 +2326,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.17" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" dependencies = [ "autocfg", "libm", @@ -2318,11 +2361,11 @@ checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "openssl" -version = "0.10.62" +version = "0.10.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cde4d2d9200ad5909f8dac647e29482e07c3a35de8a13fce7c9c7747ad9f671" +checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.4.2", "cfg-if", "foreign-types", "libc", @@ -2339,7 +2382,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.50", ] [[package]] @@ -2350,9 +2393,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.98" +version = "0.9.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1665caf8ab2dc9aef43d1c0023bd904633a6a05cb30b0ad59bec2ae986e57a7" +checksum = "dda2b0f344e78efc2facf7d195d098df0dd72151b26ab98da807afc26c198dff" dependencies = [ "cc", "libc", @@ -2486,7 +2529,7 @@ dependencies = [ "cfg-if", "libc", "redox_syscall", - "smallvec 1.12.0", + "smallvec 1.13.1", "windows-targets 0.48.5", ] @@ -2504,9 +2547,9 @@ checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" [[package]] name = "pathfinding" -version = "4.8.2" +version = "4.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35df0d074a99be583b76dc771329be9f7be45ebae444d601285dc7a094c2ac0a" +checksum = "f0a21c30f03223ae4a4c892f077b3189133689b8a659a84372f8422384ce94c9" dependencies = [ "deprecate-until", "fixedbitset", @@ -2543,22 +2586,22 @@ dependencies = [ [[package]] name = "pin-project" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422" +checksum = "0302c4a0442c456bd56f841aee5c3bfd17967563f6fadc9ceb9f9c23cf3807e0" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" +checksum = "266c042b60c9c76b8d53061e52b2e0d1116abc57cefc8c5cd671619a56ac3690" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.50", ] [[package]] @@ -2586,15 +2629,15 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69d3587f8a9e599cc7ec2c00e331f71c4e69a5f9a4b8a6efd5b07466b9736f9a" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" [[package]] name = "png" -version = "0.17.11" +version = "0.17.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f6c3c3e617595665b8ea2ff95a86066be38fb121ff920a9c0eb282abcd1da5a" +checksum = "06e4b0d3d1312775e782c86c91a111aa1f910cbb65e1337f9975b5f9a554b5e1" dependencies = [ "bitflags 1.3.2", "crc32fast", @@ -2605,9 +2648,9 @@ dependencies = [ [[package]] name = "pointy" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1abf55719b26d1479fd3263bed514942ee51522ee47474e56da8bd30e6fe1268" +checksum = "0da621c0f2ec6aca88545b90ed862dee7ee364e9a8c17b234b9469e3f340ff70" dependencies = [ "num-traits", ] @@ -2630,14 +2673,14 @@ dependencies = [ [[package]] name = "polling" -version = "3.3.2" +version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "545c980a3880efd47b2e262f6a4bb6daad6555cf3367aa9c4e52895f69537a41" +checksum = "24f040dee2588b4963afb4e420540439d126f73fdacf4a9c486a96d840bac3c9" dependencies = [ "cfg-if", "concurrent-queue", "pin-project-lite", - "rustix 0.38.30", + "rustix 0.38.31", "tracing", "windows-sys 0.52.0", ] @@ -2752,9 +2795,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.76" +version = "1.0.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c" +checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" dependencies = [ "unicode-ident", ] @@ -2888,15 +2931,15 @@ dependencies = [ [[package]] name = "rangemap" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "977b1e897f9d764566891689e642653e5ed90c6895106acd005eb4c1d0203991" +checksum = "795915a3930a5d6bafd9053d37602fea3e61be2e5d4d788983a8ba9654c1c6f2" [[package]] name = "rayon" -version = "1.8.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1" +checksum = "fa7237101a77a10773db45d62004a272517633fbcc3df19d96455ede1122e051" dependencies = [ "either", "rayon-core", @@ -2904,9 +2947,9 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.12.0" +version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" dependencies = [ "crossbeam-deque", "crossbeam-utils", @@ -2961,13 +3004,13 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.2" +version = "1.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" +checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.3", + "regex-automata 0.4.5", "regex-syntax 0.8.2", ] @@ -2982,9 +3025,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.3" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" +checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd" dependencies = [ "aho-corasick", "memchr", @@ -3011,9 +3054,9 @@ checksum = "e898588f33fdd5b9420719948f9f2a32c922a246964576f71ba7f24f80610fbc" [[package]] name = "reqwest" -version = "0.11.23" +version = "0.11.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37b1ae8d9ac08420c66222fb9096fc5de435c3c48542bc5336c51892cffafb41" +checksum = "c6920094eb85afde5e4a138be3f2de8bbdf28000f0029e72c45025a56b042251" dependencies = [ "base64", "bytes", @@ -3033,9 +3076,11 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", + "rustls-pemfile", "serde", "serde_json", "serde_urlencoded", + "sync_wrapper", "system-configuration", "tokio", "tokio-native-tls", @@ -3078,7 +3123,7 @@ dependencies = [ "regex", "relative-path", "rustc_version", - "syn 2.0.48", + "syn 2.0.50", "unicode-ident", ] @@ -3119,17 +3164,26 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.30" +version = "0.38.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "322394588aaf33c24007e8bb3238ee3e4c5c09c084ab32bc73890b99ff326bca" +checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.4.2", "errno", "libc", - "linux-raw-sys 0.4.12", + "linux-raw-sys 0.4.13", "windows-sys 0.52.0", ] +[[package]] +name = "rustls-pemfile" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" +dependencies = [ + "base64", +] + [[package]] name = "rustversion" version = "1.0.14" @@ -3138,9 +3192,9 @@ checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" [[package]] name = "ryu" -version = "1.0.16" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" +checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" [[package]] name = "schannel" @@ -3198,15 +3252,15 @@ checksum = "58bf37232d3bb9a2c4e641ca2a11d83b5062066f88df7fed36c28772046d65ba" [[package]] name = "semver" -version = "1.0.21" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97ed7a9823b74f99c7742f5336af7be5ecd3eeafcb1507d1fa93347b1d589b0" +checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" [[package]] name = "sentry" -version = "0.32.1" +version = "0.32.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab18211f62fb890f27c9bb04861f76e4be35e4c2fcbfc2d98afa37aadebb16f1" +checksum = "766448f12e44d68e675d5789a261515c46ac6ccd240abdd451a9c46c84a49523" dependencies = [ "httpdate", "native-tls", @@ -3223,9 +3277,9 @@ dependencies = [ [[package]] name = "sentry-actix" -version = "0.32.1" +version = "0.32.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2576f4d8380c3a5baa1f2f3796e442b6afceb178b6a6e573760e95d07dbb3dd" +checksum = "285f2c101ed64f00585ec98f0d81a402c1cf5c2f9b1e1d53a84901812bcd4530" dependencies = [ "actix-web", "futures-util", @@ -3234,9 +3288,9 @@ dependencies = [ [[package]] name = "sentry-backtrace" -version = "0.32.1" +version = "0.32.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf018ff7d5ce5b23165a9cbfee60b270a55ae219bc9eebef2a3b6039356dd7e5" +checksum = "32701cad8b3c78101e1cd33039303154791b0ff22e7802ed8cc23212ef478b45" dependencies = [ "backtrace", "once_cell", @@ -3246,9 +3300,9 @@ dependencies = [ [[package]] name = "sentry-contexts" -version = "0.32.1" +version = "0.32.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d934df6f9a17b8c15b829860d9d6d39e78126b5b970b365ccbd817bc0fe82c9" +checksum = "17ddd2a91a13805bd8dab4ebf47323426f758c35f7bf24eacc1aded9668f3824" dependencies = [ "hostname", "libc", @@ -3260,9 +3314,9 @@ dependencies = [ [[package]] name = "sentry-core" -version = "0.32.1" +version = "0.32.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e362d3fb1c5de5124bf1681086eaca7adf6a8c4283a7e1545359c729f9128ff" +checksum = "b1189f68d7e7e102ef7171adf75f83a59607fafd1a5eecc9dc06c026ff3bdec4" dependencies = [ "once_cell", "rand 0.8.5", @@ -3273,9 +3327,9 @@ dependencies = [ [[package]] name = "sentry-debug-images" -version = "0.32.1" +version = "0.32.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8bca420d75d9e7a8e54a4806bf4fa8a7e9a804e8f2ff05c7c80234168c6ca66" +checksum = "7b4d0a615e5eeca5699030620c119a094e04c14cf6b486ea1030460a544111a7" dependencies = [ "findshlibs", "once_cell", @@ -3284,9 +3338,9 @@ dependencies = [ [[package]] name = "sentry-panic" -version = "0.32.1" +version = "0.32.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0224e7a8e2bd8a32d96804acb8243d6d6e073fed55618afbdabae8249a964d8" +checksum = "d1c18d0b5fba195a4950f2f4c31023725c76f00aabb5840b7950479ece21b5ca" dependencies = [ "sentry-backtrace", "sentry-core", @@ -3294,9 +3348,9 @@ dependencies = [ [[package]] name = "sentry-tracing" -version = "0.32.1" +version = "0.32.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "087bed8c616d176a9c6b662a8155e5f23b40dc9e1fa96d0bd5fb56e8636a9275" +checksum = "3012699a9957d7f97047fd75d116e22d120668327db6e7c59824582e16e791b2" dependencies = [ "sentry-backtrace", "sentry-core", @@ -3306,9 +3360,9 @@ dependencies = [ [[package]] name = "sentry-types" -version = "0.32.1" +version = "0.32.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb4f0e37945b7a8ce7faebc310af92442e2d7c5aa7ef5b42fe6daa98ee133f65" +checksum = "c7173fd594569091f68a7c37a886e202f4d0c1db1e1fa1d18a051ba695b2e2ec" dependencies = [ "debugid", "hex", @@ -3323,29 +3377,29 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.195" +version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02" +checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.195" +version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c" +checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.50", ] [[package]] name = "serde_json" -version = "1.0.111" +version = "1.0.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "176e46fa42316f18edd598015a5166857fc835ec732f5215eac6b7bdbf0a84f4" +checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0" dependencies = [ "itoa", "ryu", @@ -3375,9 +3429,9 @@ dependencies = [ [[package]] name = "serde_yaml" -version = "0.9.30" +version = "0.9.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1bf28c79a99f70ee1f1d83d10c875d2e70618417fda01ad1785e027579d9d38" +checksum = "8fd075d994154d4a774f95b51fb96bdc2832b0ea48425c92546073816cda1f2f" dependencies = [ "indexmap", "itoa", @@ -3408,7 +3462,7 @@ checksum = "b93fb4adc70021ac1b47f7d45e8cc4169baaa7ea58483bc5b721d19a26202212" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.50", ] [[package]] @@ -3489,9 +3543,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.12.0" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2593d31f82ead8df961d8bd23a64c2ccf2eb5dd34b0a34bfb4dd54011c72009e" +checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" [[package]] name = "smartstring" @@ -3517,12 +3571,12 @@ dependencies = [ [[package]] name = "socket2" -version = "0.5.5" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" +checksum = "05ffd9c0a93b7543e062e759284fcf5f5e3b098501104bfbdde4d404db792871" dependencies = [ "libc", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -3563,6 +3617,12 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +[[package]] +name = "strsim" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01" + [[package]] name = "strum" version = "0.25.0" @@ -3579,7 +3639,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.48", + "syn 2.0.50", ] [[package]] @@ -3601,15 +3661,21 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.48" +version = "2.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" +checksum = "74f1bdc9872430ce9b75da68329d1c1746faf50ffac5f19e02b71e37ff881ffb" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + [[package]] name = "system-configuration" version = "0.5.1" @@ -3633,42 +3699,41 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.9.0" +version = "3.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01ce4141aa927a6d1bd34a041795abd0db1cccba5d5f24b009f694bdf3a1f3fa" +checksum = "a365e8cd18e44762ef95d87f284f4b5cd04107fec2ff3052bd6a3e6069669e67" dependencies = [ "cfg-if", "fastrand 2.0.1", - "redox_syscall", - "rustix 0.38.30", + "rustix 0.38.31", "windows-sys 0.52.0", ] [[package]] name = "thiserror" -version = "1.0.56" +version = "1.0.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad" +checksum = "1e45bcbe8ed29775f228095caf2cd67af7a4ccf756ebff23a306bf3e8b47b24b" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.56" +version = "1.0.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471" +checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.50", ] [[package]] name = "thread_local" -version = "1.1.7" +version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" dependencies = [ "cfg-if", "once_cell", @@ -3687,12 +3752,13 @@ dependencies = [ [[package]] name = "time" -version = "0.3.31" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f657ba42c3f86e7680e53c8cd3af8abbe56b5491790b46e22e19c0d57463583e" +checksum = "c8248b6521bb14bc45b4067159b9b6ad792e2d6d754d6c41fb50e29fefe38749" dependencies = [ "deranged", "itoa", + "num-conv", "powerfmt", "serde", "time-core", @@ -3707,10 +3773,11 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26197e33420244aeb70c3e8c78376ca46571bc4e701e4791c2cd9f57dcb3a43f" +checksum = "7ba3a3ef41e6672a2f0f001392bb5dcd3ff0a9992d618ca761a11c3121547774" dependencies = [ + "num-conv", "time-core", ] @@ -3731,9 +3798,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.35.1" +version = "1.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c89b4efa943be685f629b149f53829423f8f5531ea21249408e8e2f8671ec104" +checksum = "61285f6515fa018fb2d1e46eb21223fff441ee8db5d0f1435e8ab4f5cdb80931" dependencies = [ "backtrace", "bytes", @@ -3743,7 +3810,7 @@ dependencies = [ "parking_lot 0.12.1", "pin-project-lite", "signal-hook-registry", - "socket2 0.5.5", + "socket2 0.5.6", "windows-sys 0.48.0", ] @@ -3789,7 +3856,7 @@ dependencies = [ "postgres-protocol", "postgres-types", "rand 0.8.5", - "socket2 0.5.5", + "socket2 0.5.6", "tokio", "tokio-util", "whoami", @@ -3846,7 +3913,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.50", ] [[package]] @@ -3881,7 +3948,7 @@ dependencies = [ "once_cell", "regex", "sharded-slab", - "smallvec 1.12.0", + "smallvec 1.13.1", "thread_local", "tracing", "tracing-core", @@ -3890,9 +3957,9 @@ dependencies = [ [[package]] name = "treediff" -version = "4.0.2" +version = "4.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52984d277bdf2a751072b5df30ec0377febdb02f7696d64c2d7d54630bac4303" +checksum = "4d127780145176e2b5d16611cc25a900150e86e9fd79d3bde6ff3a37359c9cb5" dependencies = [ "serde_json", ] @@ -3929,9 +3996,9 @@ dependencies = [ [[package]] name = "unicode-bidi" -version = "0.3.14" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f2528f27a9eb2b21e69c95319b30bd0efd85d09c379741b0f78ea1d86be2416" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" [[package]] name = "unicode-ident" @@ -3941,9 +4008,9 @@ checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-normalization" -version = "0.1.22" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" dependencies = [ "tinyvec", ] @@ -3956,9 +4023,9 @@ checksum = "ab4c90930b95a82d00dc9e9ac071b4991924390d46cbd0dfe566148667605e4b" [[package]] name = "ureq" -version = "2.9.1" +version = "2.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8cdd25c339e200129fe4de81451814e5228c9b771d57378817d6117cc2b3f97" +checksum = "11f214ce18d8b2cbe84ed3aa6486ed3f5b285cf8d8fbdbce9f3f767a724adc35" dependencies = [ "base64", "log", @@ -4007,15 +4074,15 @@ dependencies = [ "proc-macro2", "quote", "regex", - "syn 2.0.48", + "syn 2.0.50", "uuid", ] [[package]] name = "uuid" -version = "1.6.1" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e395fcf16a7a3d8127ec99782007af141946b4795001f876d54fb0d55978560" +checksum = "f00cc9702ca12d3c81455259621e676d0f7251cec66a21e98fe2e9a37db93b2a" dependencies = [ "getrandom", "serde", @@ -4077,9 +4144,9 @@ checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" [[package]] name = "value-bag" -version = "1.6.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cdbaf5e132e593e9fc1de6a15bbec912395b11fb9719e061cf64f804524c503" +checksum = "126e423afe2dd9ac52142e7e9d5ce4135d7e13776c529d27fd6bc49f19e3280b" [[package]] name = "vcpkg" @@ -4116,9 +4183,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.90" +version = "0.2.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1223296a201415c7fad14792dbefaace9bd52b62d33453ade1c5b5f07555406" +checksum = "c1e124130aee3fb58c5bdd6b639a0509486b0338acaaae0c84a5124b0f588b7f" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -4126,24 +4193,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.90" +version = "0.2.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcdc935b63408d58a32f8cc9738a0bffd8f05cc7c002086c6ef20b7312ad9dcd" +checksum = "c9e7e1900c352b609c8488ad12639a311045f40a35491fb69ba8c12f758af70b" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.50", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.40" +version = "0.4.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bde2032aeb86bdfaecc8b261eef3cba735cc426c1f3a3416d1e0791be95fc461" +checksum = "877b9c3f61ceea0e56331985743b13f3d25c406a7098d45180fb5f09bc19ed97" dependencies = [ "cfg-if", "js-sys", @@ -4153,9 +4220,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.90" +version = "0.2.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e4c238561b2d428924c49815533a8b9121c664599558a5d9ec51f8a1740a999" +checksum = "b30af9e2d358182b5c7449424f017eba305ed32a7010509ede96cdc4696c46ed" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -4163,28 +4230,28 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.90" +version = "0.2.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bae1abb6806dc1ad9e560ed242107c0f6c84335f1749dd4e8ddb012ebd5e25a7" +checksum = "642f325be6301eb8107a83d12a8ac6c1e1c54345a7ef1a9261962dfefda09e66" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.50", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.90" +version = "0.2.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d91413b1c31d7539ba5ef2451af3f0b833a005eb27a631cec32bc0635a8602b" +checksum = "4f186bd2dcf04330886ce82d6f33dd75a7bfcf69ecf5763b89fcde53b6ac9838" [[package]] name = "web-sys" -version = "0.3.67" +version = "0.3.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58cd2333b6e0be7a39605f0e255892fd7418a682d8da8fe042fe25128794d2ed" +checksum = "96565907687f7aceb35bc5fc03770a8a0471d82e479f25832f54a0e3f4b28446" dependencies = [ "js-sys", "wasm-bindgen", @@ -4192,9 +4259,9 @@ dependencies = [ [[package]] name = "weezl" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9193164d4de03a926d909d3bc7c30543cecb35400c02114792c2cae20d5e2dbb" +checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082" [[package]] name = "whoami" @@ -4234,7 +4301,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets 0.52.0", + "windows-targets 0.52.3", ] [[package]] @@ -4252,7 +4319,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.0", + "windows-targets 0.52.3", ] [[package]] @@ -4272,17 +4339,17 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.0" +version = "0.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +checksum = "d380ba1dc7187569a8a9e91ed34b8ccfc33123bbacb8c0aed2d1ad7f3ef2dc5f" dependencies = [ - "windows_aarch64_gnullvm 0.52.0", - "windows_aarch64_msvc 0.52.0", - "windows_i686_gnu 0.52.0", - "windows_i686_msvc 0.52.0", - "windows_x86_64_gnu 0.52.0", - "windows_x86_64_gnullvm 0.52.0", - "windows_x86_64_msvc 0.52.0", + "windows_aarch64_gnullvm 0.52.3", + "windows_aarch64_msvc 0.52.3", + "windows_i686_gnu 0.52.3", + "windows_i686_msvc 0.52.3", + "windows_x86_64_gnu 0.52.3", + "windows_x86_64_gnullvm 0.52.3", + "windows_x86_64_msvc 0.52.3", ] [[package]] @@ -4293,9 +4360,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.0" +version = "0.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" +checksum = "68e5dcfb9413f53afd9c8f86e56a7b4d86d9a2fa26090ea2dc9e40fba56c6ec6" [[package]] name = "windows_aarch64_msvc" @@ -4305,9 +4372,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.0" +version = "0.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" +checksum = "8dab469ebbc45798319e69eebf92308e541ce46760b49b18c6b3fe5e8965b30f" [[package]] name = "windows_i686_gnu" @@ -4317,9 +4384,9 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.0" +version = "0.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" +checksum = "2a4e9b6a7cac734a8b4138a4e1044eac3404d8326b6c0f939276560687a033fb" [[package]] name = "windows_i686_msvc" @@ -4329,9 +4396,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.0" +version = "0.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" +checksum = "28b0ec9c422ca95ff34a78755cfa6ad4a51371da2a5ace67500cf7ca5f232c58" [[package]] name = "windows_x86_64_gnu" @@ -4341,9 +4408,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.0" +version = "0.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" +checksum = "704131571ba93e89d7cd43482277d6632589b18ecf4468f591fbae0a8b101614" [[package]] name = "windows_x86_64_gnullvm" @@ -4353,9 +4420,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.0" +version = "0.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" +checksum = "42079295511643151e98d61c38c0acc444e52dd42ab456f7ccfd5152e8ecf21c" [[package]] name = "windows_x86_64_msvc" @@ -4365,9 +4432,9 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.0" +version = "0.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" +checksum = "0770833d60a970638e989b3fa9fd2bb1aaadcf88963d1659fd7d9990196ed2d6" [[package]] name = "winreg" @@ -4402,7 +4469,7 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.50", ] [[package]] diff --git a/editoast/Cargo.toml b/editoast/Cargo.toml index efc0793c5a8..8e450c5538f 100644 --- a/editoast/Cargo.toml +++ b/editoast/Cargo.toml @@ -83,6 +83,7 @@ cfg-if = "1.0.0" validator = { version = "0.16.1", features = ["derive"] } inventory = "0.3" heck = "0.4.1" +iso8601 = "0.6.1" [dev-dependencies] async-std = { version = "1.12.0", features = ["attributes", "tokio1"] } diff --git a/editoast/src/schema/utils/duration.rs b/editoast/src/schema/utils/duration.rs new file mode 100644 index 00000000000..7a981f32b78 --- /dev/null +++ b/editoast/src/schema/utils/duration.rs @@ -0,0 +1,165 @@ +//! Serde support for `chrono::Duration` using the ISO 8601 duration format. +//! +//! **Note**: Years and months are not supported. +//! +//! ``` +//! use chrono::Duration; +//! use serde::{Serialize, Deserialize}; +//! +//! #[derive(Serialize, Deserialize)] +//! struct MyStruct { +//! #[serde(with = "crate::schema::utils::duration")] // <- Add this line +//! duration: Duration +//! } +//! +//! let s = r#"{"duration":"PT1H"}"#; // 1 hour +//! let my_struct: MyStruct = serde_json::from_str(s).unwrap(); +//! assert_eq!(my_struct.duration.num_seconds(), 3600); +//! assert_eq!(r#"{"duration":"PT3600S"}"#, serde_json::to_string(&my_struct).unwrap()); +//! +//! let err_s = r#"{"duration":"P1M"}"#; // 1 month +//! assert!(serde_json::from_str::(err_s).is_err()); +//! ``` + +use chrono::Duration as ChronoDuration; +use iso8601::Duration as IsoDuration; +use serde::{Deserialize, Serialize}; +use std::{ops::Deref, str::FromStr}; + +/// Wrapper for `chrono::Duration` to use with Serde. +/// This is useful to serialize `chrono::Duration` using the ISO 8601 duration format. +/// +/// ``` +/// use serde::{Serialize, Deserialize}; +/// use crate::schema::utils::duration::Duration; +/// +/// #[derive(Serialize, Deserialize)] +/// struct MyStruct { +/// duration: Duration +/// } +/// +/// let s = r#"{"duration":"PT1H"}"#; // 1 hour +/// let my_struct: MyStruct = serde_json::from_str(s).unwrap(); +/// assert_eq!(my_struct.duration.num_seconds(), 3600); +/// assert_eq!(r#"{"duration":"PT3600S"}"#, serde_json::to_string(&my_struct).unwrap()); +/// +/// let err_s = r#"{"duration":"P1M"}"#; // 1 month +/// assert!(serde_json::from_str::(err_s).is_err()); +/// ``` +#[derive(Debug, Clone)] +pub struct Duration(ChronoDuration); + +impl From for Duration { + fn from(duration: ChronoDuration) -> Self { + Duration(duration) + } +} + +impl From for ChronoDuration { + fn from(duration: Duration) -> Self { + duration.0 + } +} + +impl Deref for Duration { + type Target = ChronoDuration; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl Serialize for Duration { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + serialize(&self.0, serializer) + } +} + +impl<'de> Deserialize<'de> for Duration { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + deserialize(deserializer).map(Duration) + } +} + +/// Serialize a `chrono::Duration` using the ISO 8601 duration format. +pub fn serialize(duration: &ChronoDuration, serializer: S) -> Result +where + S: serde::Serializer, +{ + serializer.serialize_str(&duration.to_string()) +} + +/// Deserialize a `chrono::Duration` from an ISO 8601 duration string. +pub fn deserialize<'de, D>(deserializer: D) -> Result +where + D: serde::Deserializer<'de>, +{ + let s = String::deserialize(deserializer)?; + let iso_dur = IsoDuration::from_str(&s).map_err(serde::de::Error::custom)?; + Ok(match iso_dur { + IsoDuration::YMDHMS { + year, + month, + day, + hour, + minute, + second, + millisecond, + } => { + if year != 0 || month != 0 { + return Err(serde::de::Error::custom( + "years and months are not supported", + )); + } + ChronoDuration::days(day as i64) + + ChronoDuration::hours(hour as i64) + + ChronoDuration::minutes(minute as i64) + + ChronoDuration::seconds(second as i64) + + ChronoDuration::milliseconds(millisecond as i64) + } + IsoDuration::Weeks(weeks) => ChronoDuration::weeks(weeks as i64), + }) +} + +#[cfg(test)] +mod tests { + use super::Duration; + use serde::{Deserialize, Serialize}; + use serde_json::{from_str, to_string}; + + #[derive(Serialize, Deserialize)] + struct MyStruct { + duration: Duration, + } + + /// Test the deserialization + #[test] + fn test_deserialize() { + let s = r#"{"duration":"PT1H"}"#; // 1 hour + let my_struct: MyStruct = from_str(s).unwrap(); + assert_eq!(my_struct.duration.num_seconds(), 3600); + } + + /// Test the serialization + #[test] + fn test_serialize() { + let s = r#"{"duration":"PT3600S"}"#; // 1 hour + let my_struct = MyStruct { + duration: Duration::from(chrono::Duration::hours(1)), + }; + assert_eq!(s, to_string(&my_struct).unwrap()); + } + + /// Test invalid deserialization + #[test] + fn test_invalid_deserialize() { + let s = r#"{"duration":"P1M"}"#; // 1 month + assert!(from_str::(s).is_err()); + } +} diff --git a/editoast/src/schema/utils/mod.rs b/editoast/src/schema/utils/mod.rs index 5fc6fdec4d0..52537c927e4 100644 --- a/editoast/src/schema/utils/mod.rs +++ b/editoast/src/schema/utils/mod.rs @@ -1,7 +1,9 @@ +pub mod duration; pub mod geometry; mod identifier; mod non_blank_string; +pub use duration::Duration; pub use identifier::Identifier; pub use non_blank_string::NonBlankString; From 450f2ad7a9fde6f6f3c4620acdc4e13e7d1d5106 Mon Sep 17 00:00:00 2001 From: Florian Amsallem Date: Fri, 23 Feb 2024 20:16:49 +0100 Subject: [PATCH 2/9] editoast: add ToSchema implementation for NonBlankString --- editoast/src/schema/utils/non_blank_string.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/editoast/src/schema/utils/non_blank_string.rs b/editoast/src/schema/utils/non_blank_string.rs index ffe31e04bb6..ef59eaf28fe 100644 --- a/editoast/src/schema/utils/non_blank_string.rs +++ b/editoast/src/schema/utils/non_blank_string.rs @@ -5,6 +5,7 @@ use std::{ use rand::{distributions::Alphanumeric, thread_rng, Rng}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; +use utoipa::openapi::{ObjectBuilder, RefOr, Schema}; /// A wrapper around a String that ensures that the string is not empty. #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -89,6 +90,18 @@ impl PartialEq for &str { } } +impl<'a> utoipa::ToSchema<'a> for NonBlankString { + fn schema() -> (&'a str, RefOr) { + ( + "NonBlankString", + ObjectBuilder::new() + .schema_type(utoipa::openapi::SchemaType::String) + .min_length(Some(1)) + .into(), + ) + } +} + #[cfg(test)] mod tests { use super::NonBlankString; From b308189dfda1cbb97db16bef8e7ec357209036df Mon Sep 17 00:00:00 2001 From: Florian Amsallem Date: Fri, 23 Feb 2024 16:26:04 +0100 Subject: [PATCH 3/9] editoast: derive: add enum support to modelv2 Co-authored-by: Youness Chrifi Alaoui --- editoast/editoast_derive/src/lib.rs | 1 + editoast/editoast_derive/src/modelv2.rs | 42 +++++++++++++++++++------ 2 files changed, 33 insertions(+), 10 deletions(-) diff --git a/editoast/editoast_derive/src/lib.rs b/editoast/editoast_derive/src/lib.rs index 500dc8864ab..7070fe03dda 100644 --- a/editoast/editoast_derive/src/lib.rs +++ b/editoast/editoast_derive/src/lib.rs @@ -249,6 +249,7 @@ pub fn search_config_store(input: proc_macro::TokenStream) -> proc_macro::TokenS /// * `#[model(primary)]`: implies `identifier` ; marks the field as the primary key of the table /// * `#[model(json)]`: wraps the row field with `diesel_jsonb::JsonValue` (diesel column type: `diesel_jsonb::Json`) /// * `#[model(to_string)]`: calls `to_string()` before writing the field to the database and calls `String::from` after reading (diesel column type: String) +/// * `#[model(to_enum)]`: is converted as `u8` before writing the field to the database and calls `FromRepr::from_repr` after reading (diesel column type: TinyInt) /// * `#[model(remote = "T")]`: calls `Into::::into` before writing the field to the database and calls `T::from` after reading (diesel column type: T) /// * `#[model(geo)]` **TODO**: TBD /// diff --git a/editoast/editoast_derive/src/modelv2.rs b/editoast/editoast_derive/src/modelv2.rs index ef0d2b70c52..e1747ab350c 100644 --- a/editoast/editoast_derive/src/modelv2.rs +++ b/editoast/editoast_derive/src/modelv2.rs @@ -148,6 +148,8 @@ struct ModelFieldOption { #[darling(default)] to_string: bool, #[darling(default)] + to_enum: bool, + #[darling(default)] remote: Option, } @@ -182,6 +184,7 @@ enum FieldTransformation { Json, Geo, ToString, + ToEnum(syn::Type), } impl FieldTransformation { @@ -190,15 +193,17 @@ impl FieldTransformation { json: bool, geo: bool, to_string: bool, + to_enum: Option, ) -> Result> { - match (remote, json, geo, to_string) { - (Some(ty), false, false, false) => Ok(Some(Self::Remote(ty))), - (None, true, false, false) => Ok(Some(Self::Json)), - (None, false, true, false) => Ok(Some(Self::Geo)), - (None, false, false, true) => Ok(Some(Self::ToString)), - (None, false, false, false) => Ok(None), + match (remote, json, geo, to_string, to_enum) { + (Some(ty), false, false, false, None) => Ok(Some(Self::Remote(ty))), + (None, true, false, false, None) => Ok(Some(Self::Json)), + (None, false, true, false, None) => Ok(Some(Self::Geo)), + (None, false, false, true, None) => Ok(Some(Self::ToString)), + (None, false, false, false, Some(ty)) => Ok(Some(Self::ToEnum(ty))), + (None, false, false, false, None) => Ok(None), _ => Err(Error::custom( - "Model: remote, json, geo, and to_string attributes are mutually exclusive", + "Model: remote, json, geo, to_string and to_enum attributes are mutually exclusive", )), } } @@ -211,9 +216,19 @@ impl ModelField { .ok_or(Error::custom("Model: only works for named structs"))?; let column = value.column.unwrap_or_else(|| ident.to_string()); let builder_ident = value.builder_fn.unwrap_or_else(|| ident.clone()); - let transform = - FieldTransformation::from_args(value.remote, value.json, value.geo, value.to_string) - .map_err(|e| e.with_span(&ident))?; + let to_enum = match value.to_enum { + true => Some(value.ty.clone()), + false => None, + }; + + let transform = FieldTransformation::from_args( + value.remote, + value.json, + value.geo, + value.to_string, + to_enum, + ) + .map_err(|e| e.with_span(&ident))?; Ok(Self { ident, builder_ident, @@ -234,6 +249,9 @@ impl ModelField { Some(FieldTransformation::Json) => quote! { diesel_json::Json(#expr) }, Some(FieldTransformation::Geo) => unimplemented!("to be designed"), Some(FieldTransformation::ToString) => quote! { #expr.to_string() }, + Some(FieldTransformation::ToEnum(_)) => { + quote! { #expr as i16 } + } None => quote! { #expr }, } } @@ -245,6 +263,9 @@ impl ModelField { Some(FieldTransformation::Json) => quote! { #expr.0 }, Some(FieldTransformation::Geo) => unimplemented!("to be designed"), Some(FieldTransformation::ToString) => quote! { String::from(#expr.parse()) }, + Some(FieldTransformation::ToEnum(ref ty)) => { + quote! { #ty::from_repr(#expr as usize).expect("Invalid variant repr") } + } None => quote! { #expr }, } } @@ -256,6 +277,7 @@ impl ModelField { Some(FieldTransformation::Json) => quote! { diesel_json::Json<#ty> }, Some(FieldTransformation::Geo) => unimplemented!("to be designed"), Some(FieldTransformation::ToString) => quote! { String }, + Some(FieldTransformation::ToEnum(_)) => quote! { i16 }, None => quote! { #ty }, } } From ab460687e6120631bd33a0b210c2757246aee291 Mon Sep 17 00:00:00 2001 From: Florian Amsallem Date: Fri, 23 Feb 2024 16:27:26 +0100 Subject: [PATCH 4/9] editoast: add schema for train schedule v2 Co-authored-by: Youness Chrifi Alaoui --- editoast/src/schema/mod.rs | 1 + editoast/src/schema/v2/mod.rs | 1 + editoast/src/schema/v2/trainschedule.rs | 507 ++++++++++++++++++ .../src/tests/train_schedules/simple.json | 75 +++ 4 files changed, 584 insertions(+) create mode 100644 editoast/src/schema/v2/mod.rs create mode 100644 editoast/src/schema/v2/trainschedule.rs create mode 100644 editoast/src/tests/train_schedules/simple.json diff --git a/editoast/src/schema/mod.rs b/editoast/src/schema/mod.rs index 903317ce3a2..28a3c215eb7 100644 --- a/editoast/src/schema/mod.rs +++ b/editoast/src/schema/mod.rs @@ -17,6 +17,7 @@ mod switch; mod switch_type; pub mod track_section; pub mod utils; +pub mod v2; pub use buffer_stop::{BufferStop, BufferStopCache}; pub use detector::{Detector, DetectorCache}; diff --git a/editoast/src/schema/v2/mod.rs b/editoast/src/schema/v2/mod.rs new file mode 100644 index 00000000000..6185c83d673 --- /dev/null +++ b/editoast/src/schema/v2/mod.rs @@ -0,0 +1 @@ +pub mod trainschedule; diff --git a/editoast/src/schema/v2/trainschedule.rs b/editoast/src/schema/v2/trainschedule.rs new file mode 100644 index 00000000000..022ffa58c73 --- /dev/null +++ b/editoast/src/schema/v2/trainschedule.rs @@ -0,0 +1,507 @@ +use crate::schema::utils::{Duration, NonBlankString}; +use chrono::{DateTime, Utc}; +use derivative::Derivative; +use serde::de::Error as SerdeError; +use serde::{Deserialize, Serialize}; +use std::{ + collections::{HashMap, HashSet}, + str::FromStr, +}; +use strum_macros::FromRepr; +use utoipa::ToSchema; + +#[derive(Debug, Default, Clone, Serialize, ToSchema)] +pub struct TrainScheduleBase { + pub train_name: String, + pub labels: Vec, + pub rolling_stock_name: String, + pub start_time: DateTime, + #[schema(inline)] + pub path: Vec, + #[schema(default, inline)] + pub schedule: Vec, + #[schema(default, inline)] + pub margins: Margins, + #[schema(default)] + pub initial_speed: f64, + #[schema(default, inline)] + pub comfort: Comfort, + pub constraint_distribution: Distribution, + #[schema(default, inline)] + pub speed_limit_tag: Option, + #[schema(default, inline)] + pub power_restrictions: Vec, + #[schema(default, inline)] + pub options: TrainScheduleOptions, +} + +impl<'de> Deserialize<'de> for TrainScheduleBase { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + #[derive(Deserialize)] + #[serde(deny_unknown_fields)] + struct Internal { + train_name: String, + labels: Vec, + rolling_stock_name: String, + start_time: DateTime, + path: Vec, + #[serde(default)] + schedule: Vec, + #[serde(default)] + margins: Margins, + #[serde(default)] + initial_speed: f64, + #[serde(default)] + comfort: Comfort, + constraint_distribution: Distribution, + #[serde(default)] + speed_limit_tag: Option, + #[serde(default)] + power_restrictions: Vec, + #[serde(default)] + options: TrainScheduleOptions, + } + let internal = Internal::deserialize(deserializer)?; + + // Look for invalid path waypoint reference + let path_ids: HashSet<_> = internal.path.iter().map(|p| &p.id).collect(); + if path_ids.len() != internal.path.len() { + return Err(SerdeError::custom("Duplicate path waypoint ids")); + } + + for schedule_item in &internal.schedule { + if !path_ids.contains(&schedule_item.at) { + return Err(SerdeError::custom(format!( + "Invalid schedule, path waypoint '{}' not found", + schedule_item.at + ))); + } + } + + for boundary in &internal.margins.boundaries { + if !path_ids.contains(&boundary) { + return Err(SerdeError::custom(format!( + "Invalid boundary, path waypoint '{}' not found", + boundary + ))); + } + } + + for power_restriction in internal.power_restrictions.iter() { + if !path_ids.contains(&power_restriction.from) { + return Err(SerdeError::custom(format!( + "Invalid power restriction, path waypoint '{}' not found", + power_restriction.from + ))); + } + if !path_ids.contains(&power_restriction.to) { + return Err(SerdeError::custom(format!( + "Invalid power restriction, path waypoint '{}' not found", + power_restriction.to + ))); + } + } + + // Check scheduled points + let schedules: HashMap<_, _> = internal.schedule.iter().map(|s| (&s.at, s)).collect(); + if schedules.len() != internal.schedule.len() { + return Err(SerdeError::custom("Schedule points at the same location")); + } + let first_point_id = &internal.path.first().unwrap().id; + if schedules + .get(first_point_id) + .map_or(false, |s| s.arrival.is_some()) + { + return Err(SerdeError::custom( + "First path waypoint can't have an arrival time", + )); + } + + Ok(TrainScheduleBase { + train_name: internal.train_name, + labels: internal.labels, + rolling_stock_name: internal.rolling_stock_name, + start_time: internal.start_time, + path: internal.path, + schedule: internal.schedule, + margins: internal.margins, + initial_speed: internal.initial_speed, + comfort: internal.comfort, + constraint_distribution: internal.constraint_distribution, + speed_limit_tag: internal.speed_limit_tag, + power_restrictions: internal.power_restrictions, + options: internal.options, + }) + } +} + +#[derive(Debug, Default, Clone, Serialize, Deserialize, ToSchema)] +#[serde(deny_unknown_fields)] +pub struct PowerRestrictionItem { + #[schema(inline)] + pub from: NonBlankString, + #[schema(inline)] + pub to: NonBlankString, + pub value: String, +} + +#[derive(Debug, Derivative, Clone, Serialize, Deserialize, ToSchema)] +#[serde(deny_unknown_fields)] +#[derivative(Default)] +pub struct TrainScheduleOptions { + #[derivative(Default(value = "true"))] + #[schema(default = "true")] + use_electrical_profiles: bool, +} + +#[derive(Debug, Default, Clone, Serialize, Deserialize, ToSchema)] +#[serde(deny_unknown_fields)] +pub struct ScheduleItem { + #[schema(inline)] + pub at: NonBlankString, + pub arrival: Option, + pub stop_for: Option, + #[serde(default)] + pub locked: bool, +} + +/// The location of a path waypoint +#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] +#[serde(untagged, deny_unknown_fields)] +pub enum PathItemLocation { + TrackOffset { + /// The track section ID + #[schema(inline)] + track: NonBlankString, + /// The offset in millimeters from the start of the track + offset: u64, + }, + OperationalPointId { + /// The object id of an operational point + #[schema(inline)] + operational_point: NonBlankString, + }, + OperationalPointDescription { + /// The operational point trigram + #[schema(inline)] + trigram: NonBlankString, + /// An optional secondary code to identify a more specific location + secondary_code: Option, + }, + OperationalPointUic { + /// The [UIC](https://en.wikipedia.org/wiki/Railway_vehicle_owner%27s_code) code of an operational point + uic: u32, + /// An optional secondary code to identify a more specific location + secondary_code: Option, + }, +} + +/// A location on the path of a train +#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] +pub struct PathItem { + /// The unique identifier of the path item. + /// This is used to reference path items in the train schedule. + #[schema(inline)] + pub id: NonBlankString, + /// Metadata given to mark a point as wishing to be deleted by the user. + /// It's useful for soft deleting the point (waiting to fix / remove all references) + /// If true, the train schedule is consider as invalid and must be edited + #[serde(default)] + pub deleted: bool, + #[serde(flatten)] + #[schema(inline)] + pub location: PathItemLocation, +} + +#[derive(Debug, Clone, Serialize, Derivative, ToSchema)] +#[serde(deny_unknown_fields)] +#[derivative(Default)] +pub struct Margins { + #[schema(inline)] + pub boundaries: Vec, + #[derivative(Default(value = "vec![MarginValue::None]"))] + /// The values of the margins. Must contains one more element than the boundaries + /// Can be a percentage `X%`, a time in minutes per kilometer `Xmin/km` or `0` + #[schema(value_type = Vec, example = json!(["none", "5%", "2min/km"]))] + pub values: Vec, +} + +impl<'de> Deserialize<'de> for Margins { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + #[derive(Deserialize)] + struct InternalMargins { + boundaries: Vec, + values: Vec, + } + + let InternalMargins { boundaries, values } = InternalMargins::deserialize(deserializer)?; + if boundaries.len() + 1 != values.len() { + return Err(serde::de::Error::custom( + "The number of boudaries and values must be the same", + )); + } + Ok(Margins { boundaries, values }) + } +} + +#[derive(Debug, Copy, Clone, Default, PartialEq)] +pub enum MarginValue { + #[default] + None, + Percentage(f64), + MinPerKm(f64), +} + +impl<'de> Deserialize<'de> for MarginValue { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let value = String::deserialize(deserializer)?; + if value.to_lowercase() == "none" { + return Ok(Self::None); + } + if value.ends_with('%') { + let float_value = f64::from_str(value[0..value.len() - 1].trim()).map_err(|_| { + serde::de::Error::invalid_value( + serde::de::Unexpected::Str(&value), + &"a valid float", + ) + })?; + if float_value <= 0.0 { + return Err(serde::de::Error::invalid_value( + serde::de::Unexpected::Str(&value), + &"a strictly positive number", + )); + } + return Ok(Self::Percentage(float_value)); + } + if value.ends_with("min/km") { + let float_value: f64 = + f64::from_str(value[0..value.len() - 6].trim()).map_err(|_| { + serde::de::Error::invalid_value( + serde::de::Unexpected::Str(&value), + &"a valid float", + ) + })?; + if float_value <= 0.0 { + return Err(serde::de::Error::invalid_value( + serde::de::Unexpected::Str(&value), + &"a strictly positive float", + )); + } + return Ok(Self::MinPerKm(float_value)); + } + Err(serde::de::Error::custom("Margin type not recognized")) + } +} + +impl Serialize for MarginValue { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + match self { + MarginValue::None => serializer.serialize_str("none"), + MarginValue::Percentage(value) => serializer.serialize_str(&format!("{}%", value)), + MarginValue::MinPerKm(value) => serializer.serialize_str(&format!("{}min/km", value)), + } + } +} + +#[derive(Debug, Default, Clone, Copy, Serialize, Deserialize, FromRepr, ToSchema)] +#[serde(rename_all = "SCREAMING_SNAKE_CASE")] +pub enum Comfort { + #[default] + Standard, + AirConditioning, + Heating, +} + +#[derive(Debug, Default, Clone, Copy, Serialize, Deserialize, FromRepr, ToSchema)] +#[serde(rename_all = "UPPERCASE")] +pub enum Distribution { + #[default] + Standard, + Mareco, +} + +#[cfg(test)] +mod tests { + use super::{MarginValue, Margins, PathItem, PathItemLocation}; + use crate::schema::v2::trainschedule::{ScheduleItem, TrainScheduleBase}; + + use chrono::Duration; + use serde_json::{from_str, to_string}; + + /// Test that the `MarginValue` enum can be deserialized from a string + #[test] + fn deserialize_margin_value() { + let none: MarginValue = from_str(r#""none""#).unwrap(); + assert_eq!(none, MarginValue::None); + + let percentage: MarginValue = from_str(r#""10%""#).unwrap(); + assert_eq!(percentage, MarginValue::Percentage(10.0)); + + let min_per_km: MarginValue = from_str(r#""5.3min/km""#).unwrap(); + assert_eq!(min_per_km, MarginValue::MinPerKm(5.3)); + } + + /// Test invalid `MarginValue` deserialization + #[test] + fn deserialize_invalid_margin_value() { + assert!(from_str::(r#""3.5""#).is_err()); + assert!(from_str::(r#""-5%""#).is_err()); + assert!(from_str::(r#""-0.4min/km""#).is_err()); + } + + /// Test that the `MarginValue` enum can be serialized to a string + #[test] + fn serialize_margin_value() { + let none = to_string(&MarginValue::None).unwrap(); + assert_eq!(none, r#""none""#); + + let percentage = to_string(&MarginValue::Percentage(10.0)).unwrap(); + assert_eq!(percentage, r#""10%""#); + + let min_per_km = to_string(&MarginValue::MinPerKm(5.3)).unwrap(); + assert_eq!(min_per_km, r#""5.3min/km""#); + } + + /// Test that Margins deserialization checks works + #[test] + fn deserialize_margins() { + let valid_margins = r#"{"boundaries":["a", "b"],"values":["none","10%","20min/km"]}"#; + assert!(from_str::(valid_margins).is_ok()); + let invalid_margins = r#"{"boundaries":["a"],"values":["none","10%","20min/km"]}"#; + assert!(from_str::(invalid_margins).is_err()); + let invalid_margins = r#"{"boundaries":["a", "b"],"values":["none","10%"]}"#; + assert!(from_str::(invalid_margins).is_err()); + } + + /// Test deserialize a valid train schedule example + #[test] + fn deserialize_train_schedule() { + let train_schedule = include_str!("../../tests/train_schedules/simple.json"); + assert!(from_str::(train_schedule).is_ok()); + } + + /// Test deserialize an invalid train schedule + #[test] + fn deserialize_duplicate_path_id_train_schedule() { + let location = PathItemLocation::OperationalPointId { + operational_point: "op".into(), + }; + let path_item = PathItem { + id: "a".into(), + location, + deleted: false, + }; + let train_schedule = TrainScheduleBase { + path: vec![path_item.clone(), path_item.clone()], + ..Default::default() + }; + let invalid_str = to_string(&train_schedule).unwrap(); + assert!(from_str::(&invalid_str).is_err()); + } + + /// Test deserialize an invalid train schedule + #[test] + fn deserialize_schedule_point_not_found_train_schedule() { + let train_schedule = TrainScheduleBase { + schedule: vec![Default::default()], + ..Default::default() + }; + let invalid_str = to_string(&train_schedule).unwrap(); + assert!(from_str::(&invalid_str).is_err()); + } + + /// Test deserialize an invalid train schedule + #[test] + fn deserialize_boundary_not_found_train_schedule() { + let train_schedule = TrainScheduleBase { + margins: Margins { + boundaries: vec![Default::default()], + ..Default::default() + }, + ..Default::default() + }; + let invalid_str = to_string(&train_schedule).unwrap(); + assert!(from_str::(&invalid_str).is_err()); + } + + /// Test deserialize an invalid train schedule + #[test] + fn deserialize_power_restriction_train_schedule() { + let train_schedule = TrainScheduleBase { + power_restrictions: vec![Default::default()], + ..Default::default() + }; + let invalid_str = to_string(&train_schedule).unwrap(); + assert!(from_str::(&invalid_str).is_err()); + } + + /// Test deserialize an invalid train schedule + #[test] + fn deserialize_duplicate_schedule_points_train_schedule() { + let location = PathItemLocation::OperationalPointId { + operational_point: "op".into(), + }; + let path_item = PathItem { + id: "a".into(), + location, + deleted: false, + }; + let train_schedule = TrainScheduleBase { + path: vec![path_item.clone(), path_item.clone()], + schedule: vec![ + ScheduleItem { + at: "a".into(), + arrival: None, + stop_for: None, + locked: false, + }, + ScheduleItem { + at: "a".into(), + arrival: None, + stop_for: None, + locked: false, + }, + ], + ..Default::default() + }; + let invalid_str = to_string(&train_schedule).unwrap(); + assert!(from_str::(&invalid_str).is_err()); + } + + /// Test deserialize an invalid train schedule + #[test] + fn deserialize_arrival_time_first_waypoint_schedule_train_schedule() { + let location = PathItemLocation::OperationalPointId { + operational_point: "op".into(), + }; + let path_item = PathItem { + id: "a".into(), + location, + deleted: false, + }; + let train_schedule = TrainScheduleBase { + path: vec![path_item.clone(), path_item.clone()], + schedule: vec![ScheduleItem { + at: "a".into(), + arrival: Some(Duration::minutes(5).into()), + stop_for: None, + locked: false, + }], + ..Default::default() + }; + let invalid_str = to_string(&train_schedule).unwrap(); + assert!(from_str::(&invalid_str).is_err()); + } +} diff --git a/editoast/src/tests/train_schedules/simple.json b/editoast/src/tests/train_schedules/simple.json new file mode 100644 index 00000000000..a8987e6c1a1 --- /dev/null +++ b/editoast/src/tests/train_schedules/simple.json @@ -0,0 +1,75 @@ +{ + "train_name": "ABC3615", + "rolling_stock_name": "R2D2", + "labels": [ + "choo-choo", + "tchou-tchou" + ], + "speed_limit_tag": "MA100", + "start_time": "2023-12-21T08:51:30+00:00", + "path": [ + { + "id": "a", + "uic": 87210 + }, + { + "id": "b", + "track": "foo", + "offset": 10 + }, + { + "id": "c", + "deleted": true, + "trigram": "ABC" + }, + { + "id": "d", + "operational_point": "X" + } + ], + "constraint_distribution": "MARECO", + "schedule": [ + { + "at": "a", + "stop_for": "PT5M", + "locked": true + }, + { + "at": "b", + "arrival": "PT10M", + "stop_for": "PT5M" + }, + { + "at": "c", + "stop_for": "PT5M" + }, + { + "at": "d", + "arrival": "PT50M", + "locked": true + } + ], + "margins": { + "boundaries": [ + "b", + "c" + ], + "values": [ + "5%", + "3min/km", + "none" + ] + }, + "initial_speed": 2.5, + "power_restrictions": [ + { + "from": "b", + "to": "c", + "value": "M1C1" + } + ], + "comfort": "AIR_CONDITIONING", + "options": { + "use_electrical_profiles": true + } +} From 4b8a874641e2030b00ee46c0d172b13b9bc6f47e Mon Sep 17 00:00:00 2001 From: Florian Amsallem Date: Fri, 23 Feb 2024 16:28:20 +0100 Subject: [PATCH 5/9] editoast: add into impl for paginated results Co-authored-by: Youness Chrifi Alaoui --- editoast/src/views/pagination.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/editoast/src/views/pagination.rs b/editoast/src/views/pagination.rs index 7e2e41c14c7..9fe6c8eff2b 100644 --- a/editoast/src/views/pagination.rs +++ b/editoast/src/views/pagination.rs @@ -6,6 +6,7 @@ use diesel::sql_types::Untyped; use diesel::{QueryResult, QueryableByName}; use diesel_async::methods::LoadQuery; use diesel_async::AsyncPgConnection as PgConnection; +use itertools::Itertools; use serde::Deserialize; use serde::Serialize; use thiserror::Error; @@ -220,3 +221,14 @@ where impl Query for Paginated { type SqlType = Untyped; } + +impl PaginatedResponse { + pub fn into>(self) -> PaginatedResponse { + PaginatedResponse { + count: self.count, + previous: self.previous, + next: self.next, + results: self.results.into_iter().map_into().collect(), + } + } +} From b699c70a859dfc1d4b5b2e98792031d20195402f Mon Sep 17 00:00:00 2001 From: Florian Amsallem Date: Fri, 23 Feb 2024 16:29:54 +0100 Subject: [PATCH 6/9] editoast: add migrations for timetable and train schedule v2 Co-authored-by: Youness Chrifi Alaoui --- .../down.sql | 2 + .../up.sql | 21 ++++++++++ editoast/src/tables.rs | 40 +++++++++++++++++++ 3 files changed, 63 insertions(+) create mode 100644 editoast/migrations/2024-02-21-002011_create_v2_trainschedule_timetable/down.sql create mode 100644 editoast/migrations/2024-02-21-002011_create_v2_trainschedule_timetable/up.sql diff --git a/editoast/migrations/2024-02-21-002011_create_v2_trainschedule_timetable/down.sql b/editoast/migrations/2024-02-21-002011_create_v2_trainschedule_timetable/down.sql new file mode 100644 index 00000000000..83abf40bf90 --- /dev/null +++ b/editoast/migrations/2024-02-21-002011_create_v2_trainschedule_timetable/down.sql @@ -0,0 +1,2 @@ +DROP TABLE train_schedule_v2; +DROP TABLE timetable_v2; diff --git a/editoast/migrations/2024-02-21-002011_create_v2_trainschedule_timetable/up.sql b/editoast/migrations/2024-02-21-002011_create_v2_trainschedule_timetable/up.sql new file mode 100644 index 00000000000..56fd4e67dd0 --- /dev/null +++ b/editoast/migrations/2024-02-21-002011_create_v2_trainschedule_timetable/up.sql @@ -0,0 +1,21 @@ +CREATE TABLE timetable_v2 ( + id int8 PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, + electrical_profile_set_id int8 NULL REFERENCES electrical_profile_set(id) ON DELETE CASCADE +); +CREATE TABLE train_schedule_v2 ( + id int8 PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, + train_name varchar(128) NOT NULL, + labels text [] NOT NULL, + rolling_stock_name varchar(128) NOT NULL, + timetable_id int8 NOT NULL REFERENCES timetable_v2(id) ON DELETE CASCADE, + start_time timestamptz NOT NULL, + schedule jsonb NOT NULL, + margins jsonb NOT NULL, + initial_speed float8 NOT NULL, + comfort smallint NOT NULL, + path jsonb NOT NULL, + constraint_distribution smallint NOT NULL, + speed_limit_tag varchar(128), + power_restrictions jsonb NOT NULL, + options jsonb NOT NULL +); diff --git a/editoast/src/tables.rs b/editoast/src/tables.rs index 0ff43ffaf5b..ca7bae378de 100644 --- a/editoast/src/tables.rs +++ b/editoast/src/tables.rs @@ -633,6 +633,16 @@ diesel::table! { } } +diesel::table! { + use diesel::sql_types::*; + use postgis_diesel::sql_types::*; + + timetable_v2 (id) { + id -> Int8, + electrical_profile_set_id -> Nullable, + } +} + diesel::table! { use diesel::sql_types::*; use postgis_diesel::sql_types::*; @@ -661,6 +671,32 @@ diesel::table! { } } +diesel::table! { + use diesel::sql_types::*; + use postgis_diesel::sql_types::*; + + train_schedule_v2 (id) { + id -> Int8, + #[max_length = 128] + train_name -> Varchar, + labels -> Array>, + #[max_length = 128] + rolling_stock_name -> Varchar, + timetable_id -> Int8, + start_time -> Timestamptz, + schedule -> Jsonb, + margins -> Jsonb, + initial_speed -> Float8, + comfort -> Int2, + path -> Jsonb, + constraint_distribution -> Int2, + #[max_length = 128] + speed_limit_tag -> Nullable, + power_restrictions -> Jsonb, + options -> Jsonb, + } +} + diesel::joinable!(infra_layer_buffer_stop -> infra (infra_id)); diesel::joinable!(infra_layer_detector -> infra (infra_id)); diesel::joinable!(infra_layer_electrification -> infra (infra_id)); @@ -701,9 +737,11 @@ diesel::joinable!(search_signal -> infra_object_signal (id)); diesel::joinable!(search_study -> study (id)); diesel::joinable!(simulation_output -> train_schedule (train_schedule_id)); diesel::joinable!(study -> project (project_id)); +diesel::joinable!(timetable_v2 -> electrical_profile_set (electrical_profile_set_id)); diesel::joinable!(train_schedule -> pathfinding (path_id)); diesel::joinable!(train_schedule -> rolling_stock (rolling_stock_id)); diesel::joinable!(train_schedule -> timetable (timetable_id)); +diesel::joinable!(train_schedule_v2 -> timetable_v2 (timetable_id)); diesel::allow_tables_to_appear_in_same_query!( document, @@ -747,5 +785,7 @@ diesel::allow_tables_to_appear_in_same_query!( simulation_output, study, timetable, + timetable_v2, train_schedule, + train_schedule_v2, ); From 2592c379ecc50cae7c2bf9b9ac40bf004980e38b Mon Sep 17 00:00:00 2001 From: Florian Amsallem Date: Fri, 23 Feb 2024 16:31:03 +0100 Subject: [PATCH 7/9] editoast: add models for timetable and train schedule v2 Co-authored-by: Youness Chrifi Alaoui --- editoast/src/modelsv2/mod.rs | 5 +- editoast/src/modelsv2/timetable.rs | 75 +++++++++++++++++++++++++ editoast/src/modelsv2/train_schedule.rs | 33 +++++++++++ 3 files changed, 111 insertions(+), 2 deletions(-) create mode 100644 editoast/src/modelsv2/timetable.rs create mode 100644 editoast/src/modelsv2/train_schedule.rs diff --git a/editoast/src/modelsv2/mod.rs b/editoast/src/modelsv2/mod.rs index 7cbf5dbd0e2..062e5599c19 100644 --- a/editoast/src/modelsv2/mod.rs +++ b/editoast/src/modelsv2/mod.rs @@ -2,11 +2,12 @@ pub mod documents; pub mod infra_objects; pub mod projects; pub mod railjson; - -pub use projects::{Ordering, Project}; +pub mod timetable; +pub mod train_schedule; pub use documents::Document; pub use infra_objects::*; +pub use projects::{Ordering, Project}; use async_trait::async_trait; use diesel::{pg::Pg, result::Error::NotFound, AsChangeset, QueryableByName}; diff --git a/editoast/src/modelsv2/timetable.rs b/editoast/src/modelsv2/timetable.rs new file mode 100644 index 00000000000..38c6e8a9722 --- /dev/null +++ b/editoast/src/modelsv2/timetable.rs @@ -0,0 +1,75 @@ +use crate::diesel::query_dsl::methods::DistinctDsl; +use crate::error::Result; +use crate::models::List; +use crate::models::NoParams; +use crate::modelsv2::{Retrieve, Row}; +use crate::tables::timetable_v2::dsl; +use crate::views::pagination::Paginate; +use crate::views::pagination::PaginatedResponse; +use async_trait::async_trait; +use diesel::sql_query; +use diesel::sql_types::{Array, BigInt, Nullable}; +use diesel_async::AsyncPgConnection as PgConnection; +use editoast_derive::ModelV2; + +#[derive(Debug, Default, Clone, ModelV2)] +#[model(table = crate::tables::timetable_v2)] +pub struct Timetable { + pub id: i64, + pub electrical_profile_set_id: Option, +} + +#[async_trait] +impl List for Timetable { + async fn list_conn( + conn: &mut PgConnection, + page: i64, + page_size: i64, + _: NoParams, + ) -> Result> { + let timetable_rows = dsl::timetable_v2 + .distinct() + .paginate(page, page_size) + .load_and_count::>(conn) + .await?; + + Ok(timetable_rows.into()) + } +} + +/// Should be used to retrieve a timetable with its trains +#[derive(Debug, Clone, QueryableByName)] +pub struct TimetableWithTrains { + #[diesel(sql_type = BigInt)] + pub id: i64, + #[diesel(sql_type = Nullable)] + pub electrical_profile_set_id: Option, + #[diesel(sql_type = Array)] + pub train_ids: Vec, +} + +#[async_trait::async_trait] +impl Retrieve for TimetableWithTrains { + async fn retrieve( + conn: &mut diesel_async::AsyncPgConnection, + timetable_id: i64, + ) -> Result> { + use diesel_async::RunQueryDsl; + let result = sql_query( + "SELECT timetable_v2.*, + array_remove(array_agg(train_schedule_v2.id), NULL) as train_ids + FROM timetable_v2 + LEFT JOIN train_schedule_v2 ON timetable_v2.id = train_schedule_v2.timetable_id + WHERE timetable_v2.id = $1 + GROUP BY timetable_v2.id", + ) + .bind::(timetable_id) + .get_result::(conn) + .await; + match result { + Ok(result) => Ok(Some(result)), + Err(diesel::result::Error::NotFound) => Ok(None), + Err(err) => Err(err.into()), + } + } +} diff --git a/editoast/src/modelsv2/train_schedule.rs b/editoast/src/modelsv2/train_schedule.rs new file mode 100644 index 00000000000..32a2a2fd53e --- /dev/null +++ b/editoast/src/modelsv2/train_schedule.rs @@ -0,0 +1,33 @@ +use crate::schema::v2::trainschedule::{ + Comfort, Distribution, Margins, PathItem, PowerRestrictionItem, ScheduleItem, + TrainScheduleOptions, +}; +use chrono::{DateTime, Utc}; +use editoast_derive::ModelV2; + +#[derive(Debug, Default, Clone, ModelV2)] +#[model(table = crate::tables::train_schedule_v2)] +pub struct TrainSchedule { + pub id: i64, + pub train_name: String, + pub labels: Vec>, + pub rolling_stock_name: String, + pub timetable_id: i64, + pub start_time: DateTime, + #[model(json)] + pub schedule: Vec, + #[model(json)] + pub margins: Margins, + pub initial_speed: f64, + #[model(to_enum)] + pub comfort: Comfort, + #[model(json)] + pub path: Vec, + #[model(to_enum)] + pub constraint_distribution: Distribution, + pub speed_limit_tag: Option, + #[model(json)] + pub power_restrictions: Vec, + #[model(json)] + pub options: TrainScheduleOptions, +} From 40308cceeb83ccd89ef44c6ca9e5ca86c5eb429f Mon Sep 17 00:00:00 2001 From: Florian Amsallem Date: Fri, 23 Feb 2024 16:36:40 +0100 Subject: [PATCH 8/9] editoast: add views for timetable and train schedule v2 Co-authored-by: Youness Chrifi Alaoui --- editoast/openapi.yaml | 523 +++++++++++++++++++++++- editoast/src/fixtures.rs | 41 ++ editoast/src/views/mod.rs | 6 +- editoast/src/views/v2/mod.rs | 12 + editoast/src/views/v2/timetable.rs | 294 +++++++++++++ editoast/src/views/v2/train_schedule.rs | 342 ++++++++++++++++ front/src/common/api/osrdEditoastApi.ts | 213 ++++++++++ 7 files changed, 1425 insertions(+), 6 deletions(-) create mode 100644 editoast/src/views/v2/mod.rs create mode 100644 editoast/src/views/v2/timetable.rs create mode 100644 editoast/src/views/v2/train_schedule.rs diff --git a/editoast/openapi.yaml b/editoast/openapi.yaml index dbaae0c251c..d5319254977 100644 --- a/editoast/openapi.yaml +++ b/editoast/openapi.yaml @@ -65,6 +65,17 @@ components: - percentage - value_type type: object + BatchDeletionRequest: + properties: + ids: + items: + format: int64 + type: integer + type: array + uniqueItems: true + required: + - ids + type: object BoundingBox: description: A bounding box items: @@ -180,6 +191,11 @@ components: - end - direction type: object + Distribution: + enum: + - STANDARD + - MARECO + type: string EditoastAttachedErrorTrackNotFound: properties: context: @@ -672,6 +688,7 @@ components: - $ref: '#/components/schemas/EditoastStudyErrorStartDateAfterEndDate' - $ref: '#/components/schemas/EditoastTimetableErrorInfraNotLoaded' - $ref: '#/components/schemas/EditoastTimetableErrorNotFound' + - $ref: '#/components/schemas/EditoastTimetableErrorNotFound' - $ref: '#/components/schemas/EditoastTrainScheduleErrorBatchShouldHaveSameTimetable' - $ref: '#/components/schemas/EditoastTrainScheduleErrorBatchTrainScheduleNotFound' - $ref: '#/components/schemas/EditoastTrainScheduleErrorNoSimulation' @@ -681,6 +698,8 @@ components: - $ref: '#/components/schemas/EditoastTrainScheduleErrorRollingStockNotFound' - $ref: '#/components/schemas/EditoastTrainScheduleErrorTimetableNotFound' - $ref: '#/components/schemas/EditoastTrainScheduleErrorUnsimulatedTrainSchedule' + - $ref: '#/components/schemas/EditoastTrainScheduleErrorBatchTrainScheduleNotFound' + - $ref: '#/components/schemas/EditoastTrainScheduleErrorNotFound' - $ref: '#/components/schemas/EditoastTypeCheckErrorArgMissing' - $ref: '#/components/schemas/EditoastTypeCheckErrorArgTypeMismatch' - $ref: '#/components/schemas/EditoastTypeCheckErrorUnexpectedArg' @@ -2274,16 +2293,21 @@ components: EditoastTrainScheduleErrorBatchTrainScheduleNotFound: properties: context: + properties: + number: + type: integer + required: + - number type: object message: type: string status: enum: - - 400 + - 404 type: integer type: enum: - - editoast:train_schedule:BatchTrainScheduleNotFound + - editoast:train_schedule_v2:BatchTrainScheduleNotFound type: string required: - type @@ -2341,11 +2365,11 @@ components: type: string status: enum: - - 400 + - 404 type: integer type: enum: - - editoast:train_schedule:NotFound + - editoast:train_schedule_v2:NotFound type: string required: - type @@ -3616,6 +3640,34 @@ components: - next - results type: object + PaginatedResponseOfTimetable: + description: A paginated response + properties: + count: + description: The total number of items + format: int64 + type: integer + next: + description: The next page number + format: int64 + nullable: true + type: integer + previous: + description: The previous page number + format: int64 + nullable: true + type: integer + results: + description: The list of results + items: + $ref: '#/components/schemas/TimetableResult' + type: array + required: + - count + - previous + - next + - results + type: object Patch: description: A JSONPatch document as defined by RFC 6902 properties: @@ -5547,6 +5599,38 @@ components: - id - name type: object + TimetableDetailedResult: + allOf: + - description: Creation form for a Timetable + properties: + electrical_profile_set_id: + format: int64 + nullable: true + type: integer + id: + format: int64 + type: integer + required: + - id + type: object + - properties: + train_ids: + items: + format: int64 + type: integer + type: array + required: + - train_ids + type: object + description: Creation form for a Timetable + TimetableForm: + description: Creation form for a Timetable + properties: + electrical_profile_set_id: + format: int64 + nullable: true + type: integer + type: object TimetableImportError: oneOf: - properties: @@ -5703,6 +5787,19 @@ components: - name - departure_time type: object + TimetableResult: + description: Creation form for a Timetable + properties: + electrical_profile_set_id: + format: int64 + nullable: true + type: integer + id: + format: int64 + type: integer + required: + - id + type: object TimetableWithSchedulesDetails: allOf: - $ref: '#/components/schemas/Timetable' @@ -5819,6 +5916,185 @@ components: - timetable_id - scheduled_points type: object + TrainScheduleBase: + properties: + comfort: + allOf: + - enum: + - STANDARD + - AIR_CONDITIONING + - HEATING + type: string + constraint_distribution: + $ref: '#/components/schemas/Distribution' + initial_speed: + format: double + type: number + labels: + items: + type: string + type: array + margins: + allOf: + - additionalProperties: false + properties: + boundaries: + items: + minLength: 1 + type: string + type: array + values: + description: |- + The values of the margins. Must contains one more element than the boundaries + Can be a percentage `X%`, a time in minutes per kilometer `Xmin/km` or `0` + example: + - none + - 5% + - 2min/km + items: + type: string + type: array + required: + - boundaries + - values + type: object + options: + allOf: + - additionalProperties: false + properties: + use_electrical_profiles: + default: 'true' + type: boolean + required: + - use_electrical_profiles + type: object + path: + items: + allOf: + - description: The location of a path waypoint + oneOf: + - properties: + offset: + description: The offset in millimeters from the start of the track + format: int64 + minimum: 0 + type: integer + track: + minLength: 1 + type: string + required: + - track + - offset + type: object + - properties: + operational_point: + minLength: 1 + type: string + required: + - operational_point + type: object + - properties: + secondary_code: + description: An optional secondary code to identify a more specific location + nullable: true + type: string + trigram: + minLength: 1 + type: string + required: + - trigram + type: object + - properties: + secondary_code: + description: An optional secondary code to identify a more specific location + nullable: true + type: string + uic: + description: The [UIC](https://en.wikipedia.org/wiki/Railway_vehicle_owner%27s_code) code of an operational point + format: int32 + minimum: 0 + type: integer + required: + - uic + type: object + - properties: + deleted: + description: |- + Metadata given to mark a point as wishing to be deleted by the user. + It's useful for soft deleting the point (waiting to fix / remove all references) + If true, the train schedule is consider as invalid and must be edited + type: boolean + id: + minLength: 1 + type: string + required: + - id + type: object + description: A location on the path of a train + type: array + power_restrictions: + items: + additionalProperties: false + properties: + from: + minLength: 1 + type: string + to: + minLength: 1 + type: string + value: + type: string + required: + - from + - to + - value + type: object + type: array + rolling_stock_name: + type: string + schedule: + items: + additionalProperties: false + properties: + arrival: + nullable: true + type: string + at: + minLength: 1 + type: string + locked: + type: boolean + stop_for: + nullable: true + type: string + required: + - at + type: object + type: array + speed_limit_tag: + allOf: + - minLength: 1 + type: string + nullable: true + start_time: + format: date-time + type: string + train_name: + type: string + required: + - train_name + - labels + - rolling_stock_name + - start_time + - path + - schedule + - margins + - initial_speed + - comfort + - constraint_distribution + - power_restrictions + - options + type: object TrainScheduleBatchItem: properties: allowances: @@ -5864,6 +6140,16 @@ components: - initial_speed - rolling_stock_id type: object + TrainScheduleForm: + allOf: + - $ref: '#/components/schemas/TrainScheduleBase' + - properties: + timetable_id: + format: int64 + type: integer + required: + - timetable_id + type: object TrainScheduleOptions: description: Options for the standalone simulation properties: @@ -5931,6 +6217,20 @@ components: required: - id type: object + TrainScheduleResult: + allOf: + - $ref: '#/components/schemas/TrainScheduleBase' + - properties: + id: + format: int64 + type: integer + timetable_id: + format: int64 + type: integer + required: + - id + - timetable_id + type: object TrainScheduleScenarioStudyProject: properties: project_id: @@ -8608,6 +8908,221 @@ paths: summary: Retrieve a simulation result tags: - train_schedule + /v2/timetable/: + get: + description: Return all timetables + parameters: + - in: query + name: page + required: false + schema: + default: 1 + format: int64 + minimum: 1 + type: integer + - in: query + name: page_size + required: false + schema: + default: 25 + format: int64 + minimum: 1 + nullable: true + type: integer + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/PaginatedResponseOfTimetable' + description: List timetables + summary: Return all timetables + tags: + - timetablev2 + post: + description: Return a specific timetable with its associated schedules + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/TimetableForm' + required: true + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/TimetableResult' + description: Timetable with train schedules ids + '404': + description: Timetable not found + summary: Return a specific timetable with its associated schedules + tags: + - timetablev2 + /v2/timetable/{id}/: + delete: + description: Return a specific timetable with its associated schedules + parameters: + - description: A timetable ID + in: path + name: id + required: true + schema: + format: int64 + type: integer + responses: + '204': + description: No content + '404': + description: Timetable not found + summary: Return a specific timetable with its associated schedules + tags: + - timetablev2 + get: + description: Return a specific timetable with its associated schedules + parameters: + - description: A timetable ID + in: path + name: id + required: true + schema: + format: int64 + type: integer + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/TimetableDetailedResult' + description: Timetable with train schedules ids + '404': + description: Timetable not found + summary: Return a specific timetable with its associated schedules + tags: + - timetablev2 + put: + description: Update a specific timetable with its associated schedules + parameters: + - description: A timetable ID + in: path + name: id + required: true + schema: + format: int64 + type: integer + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/TimetableForm' + description: '' + required: true + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/TimetableDetailedResult' + description: Timetable with train schedules ids + '404': + description: Timetable not found + summary: Update a specific timetable with its associated schedules + tags: + - timetablev2 + /v2/train_schedule/: + delete: + description: Delete a train schedule and its result + requestBody: + content: + application/json: + schema: + properties: + ids: + items: + format: int64 + type: integer + type: array + uniqueItems: true + required: + - ids + type: object + required: true + responses: + '204': + description: All train schedules have been deleted + summary: Delete a train schedule and its result + tags: + - train_schedulev2 + post: + description: Create train schedule by batch + requestBody: + content: + application/json: + schema: + items: + $ref: '#/components/schemas/TrainScheduleForm' + type: array + required: true + responses: + '200': + content: + application/json: + schema: + items: + $ref: '#/components/schemas/TrainScheduleResult' + type: array + description: The train schedule + summary: Create train schedule by batch + tags: + - train_schedulev2 + /v2/train_schedule/{id}/: + get: + description: Return a specific timetable with its associated schedules + parameters: + - description: A train schedule ID + in: path + name: id + required: true + schema: + format: int64 + type: integer + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/TrainScheduleResult' + description: The train schedule + summary: Return a specific timetable with its associated schedules + tags: + - train_schedulev2 + put: + description: Update train schedule at once + parameters: + - description: A train schedule ID + in: path + name: id + required: true + schema: + format: int64 + type: integer + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/TrainScheduleForm' + required: true + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/TrainScheduleResult' + description: The train schedule have been updated + summary: Update train schedule at once + tags: + - train_schedulev2 + - timetable /version/: get: responses: diff --git a/editoast/src/fixtures.rs b/editoast/src/fixtures.rs index bd22a72ab38..3cbf513514d 100644 --- a/editoast/src/fixtures.rs +++ b/editoast/src/fixtures.rs @@ -10,10 +10,14 @@ pub mod tests { Scenario, SimulationOutput, SimulationOutputChangeset, Study, Timetable, TrainSchedule, }; use crate::modelsv2::projects::Tags; + use crate::modelsv2::timetable::Timetable as TimetableV2; + use crate::modelsv2::train_schedule::TrainSchedule as TrainScheduleV2; use crate::modelsv2::{self, Document, Model, Project}; use crate::schema::electrical_profiles::{ElectricalProfile, ElectricalProfileSetData}; + use crate::schema::v2::trainschedule::TrainScheduleBase; use crate::schema::{RailJson, TrackRange}; use crate::views::infra::InfraForm; + use crate::views::v2::train_schedule::TrainScheduleForm; use crate::DbPool; use actix_web::web::Data; @@ -163,6 +167,34 @@ pub mod tests { train_schedule.create(db_pool).await.unwrap() } + #[derive(Debug)] + pub struct TrainScheduleV2FixtureSet { + pub train_schedule: TestFixture, + pub timetable: TestFixture, + } + + #[fixture] + pub async fn train_schedule_v2( + #[future] timetable_v2: TestFixture, + db_pool: Data, + ) -> TrainScheduleV2FixtureSet { + let timetable = timetable_v2.await; + let train_schedule_base: TrainScheduleBase = + serde_json::from_str(include_str!("./tests/train_schedules/simple.json")) + .expect("Unable to parse"); + let train_schedule_form = TrainScheduleForm { + timetable_id: timetable.id(), + train_schedule: train_schedule_base, + }; + + let train_schedule = TestFixture::create(train_schedule_form.into(), db_pool).await; + + TrainScheduleV2FixtureSet { + train_schedule, + timetable, + } + } + #[derive(Debug)] pub struct TrainScheduleFixtureSet { pub train_schedule: TestFixture, @@ -293,6 +325,15 @@ pub mod tests { TestFixture::create_legacy(timetable_model, db_pool).await } + #[fixture] + pub async fn timetable_v2(db_pool: Data) -> TestFixture { + TestFixture::create( + TimetableV2::changeset().electrical_profile_set_id(None), + db_pool, + ) + .await + } + #[fixture] pub async fn document_example(db_pool: Data) -> TestFixture { let img = image::open("src/tests/example_rolling_stock_image_1.gif").unwrap(); diff --git a/editoast/src/views/mod.rs b/editoast/src/views/mod.rs index c4adc1fdf28..7af12b18697 100644 --- a/editoast/src/views/mod.rs +++ b/editoast/src/views/mod.rs @@ -17,6 +17,7 @@ pub mod stdcm; pub mod study; pub mod timetable; pub mod train_schedule; +pub mod v2; use self::openapi::{merge_path_items, remove_discriminator, OpenApiMerger, Routes}; use crate::client::get_app_version; @@ -48,15 +49,15 @@ fn routes_v2() -> Routes { (health, version, core_version), (rolling_stocks::routes(), light_rolling_stocks::routes()), (pathfinding::routes(), stdcm::routes(), train_schedule::routes()), - timetable::routes(), + (projects::routes(),timetable::routes()), documents::routes(), sprites::routes(), - projects::routes(), search::routes(), electrical_profiles::routes(), layers::routes(), infra::routes(), single_simulation::routes(), + v2::routes() } routes() } @@ -83,6 +84,7 @@ schemas! { electrical_profiles::schemas(), infra::schemas(), single_simulation::schemas(), + v2::schemas(), } #[derive(OpenApi)] diff --git a/editoast/src/views/v2/mod.rs b/editoast/src/views/v2/mod.rs new file mode 100644 index 00000000000..32396a1701d --- /dev/null +++ b/editoast/src/views/v2/mod.rs @@ -0,0 +1,12 @@ +pub mod timetable; +pub mod train_schedule; + +crate::routes! { + train_schedule::routes(), + timetable::routes(), +} + +crate::schemas! { + train_schedule::schemas(), + timetable::schemas(), +} diff --git a/editoast/src/views/v2/timetable.rs b/editoast/src/views/v2/timetable.rs new file mode 100644 index 00000000000..aaf23019dac --- /dev/null +++ b/editoast/src/views/v2/timetable.rs @@ -0,0 +1,294 @@ +use crate::decl_paginated_response; +use crate::error::Result; +use crate::models::List; +use crate::models::NoParams; +use crate::modelsv2::timetable::{Timetable, TimetableWithTrains}; +use crate::modelsv2::{Create, DeleteStatic, Model, Retrieve, Update}; +use crate::views::pagination::PaginatedResponse; +use crate::views::pagination::PaginationQueryParam; +use crate::DbPool; + +use actix_web::web::{Data, Json, Path, Query}; +use actix_web::{delete, get, post, put, HttpResponse}; +use derivative::Derivative; +use editoast_derive::EditoastError; +use serde::{Deserialize, Serialize}; +use thiserror::Error; +use utoipa::{IntoParams, ToSchema}; + +crate::routes! { + "/v2/timetable" => { + post, + list, + "/{id}" => { + delete, + get, + put, + } + }, +} + +crate::schemas! { + PaginatedResponseOfTimetable, + TimetableForm, + TimetableResult, + TimetableDetailedResult, +} + +#[derive(Debug, Error, EditoastError)] +#[editoast_error(base_id = "timetable")] +enum TimetableError { + #[error("Timetable '{timetable_id}', could not be found")] + #[editoast_error(status = 404)] + NotFound { timetable_id: i64 }, +} + +/// Creation form for a Timetable +#[derive(Serialize, Deserialize, Derivative, ToSchema)] +#[derivative(Default)] +struct TimetableForm { + #[serde(default)] + pub electrical_profile_set_id: Option, +} + +/// Creation form for a Timetable +#[derive(Debug, Default, Serialize, Deserialize, Derivative, ToSchema)] +struct TimetableResult { + pub id: i64, + pub electrical_profile_set_id: Option, +} + +impl From for TimetableResult { + fn from(timetable: Timetable) -> Self { + Self { + id: timetable.id, + electrical_profile_set_id: timetable.electrical_profile_set_id, + } + } +} + +/// Creation form for a Timetable +#[derive(Debug, Default, Serialize, Deserialize, Derivative, ToSchema)] +struct TimetableDetailedResult { + #[serde(flatten)] + #[schema(inline)] + pub timetable: TimetableResult, + pub train_ids: Vec, +} + +impl From for TimetableDetailedResult { + fn from(val: TimetableWithTrains) -> Self { + Self { + timetable: TimetableResult { + id: val.id, + electrical_profile_set_id: val.electrical_profile_set_id, + }, + train_ids: val.train_ids, + } + } +} + +#[derive(IntoParams, Deserialize)] +struct TimetableIdParam { + /// A timetable ID + id: i64, +} + +/// Return a specific timetable with its associated schedules +#[utoipa::path( + tag = "timetablev2", + params(TimetableIdParam), + responses( + (status = 200, description = "Timetable with train schedules ids", body = TimetableDetailedResult), + (status = 404, description = "Timetable not found"), + ), +)] +#[get("")] +async fn get( + db_pool: Data, + timetable_id: Path, +) -> Result> { + let timetable_id = timetable_id.id; + // Return the timetable + + let conn = &mut db_pool.get().await?; + let timetable = TimetableWithTrains::retrieve_or_fail(conn, timetable_id, || { + TimetableError::NotFound { timetable_id } + }) + .await?; + + Ok(Json(timetable.into())) +} + +decl_paginated_response!(PaginatedResponseOfTimetable, TimetableResult); +/// Return all timetables +#[utoipa::path( + tag = "timetablev2", + params(PaginationQueryParam), + responses( + (status = 200, description = "List timetables", body = PaginatedResponseOfTimetable), + ), +)] +#[get("")] +async fn list( + db_pool: Data, + pagination_params: Query, +) -> Result>> { + let (page, per_page) = pagination_params + .validate(1000)? + .warn_page_size(100) + .unpack(); + let conn = &mut db_pool.get().await?; + let timetable = Timetable::list_conn(conn, page, per_page, NoParams).await?; + Ok(Json(timetable.into())) +} + +/// Return a specific timetable with its associated schedules +#[utoipa::path( + tag = "timetablev2", + request_body = TimetableForm, + responses( + (status = 200, description = "Timetable with train schedules ids", body = TimetableResult), + (status = 404, description = "Timetable not found"), + ), +)] +#[post("")] +async fn post(db_pool: Data, data: Json) -> Result> { + let conn = &mut db_pool.get().await?; + + let elec_profile_set = data.into_inner().electrical_profile_set_id; + let changeset = Timetable::changeset().electrical_profile_set_id(elec_profile_set); + let timetable = changeset.create(conn).await?; + + Ok(Json(timetable.into())) +} + +/// Update a specific timetable with its associated schedules +#[utoipa::path( + tag = "timetablev2", + params(TimetableIdParam), + responses( + (status = 200, description = "Timetable with train schedules ids", body = TimetableDetailedResult), + (status = 404, description = "Timetable not found"), + ), +)] +#[put("")] +async fn put( + db_pool: Data, + timetable_id: Path, + data: Json, +) -> Result> { + let timetable_id = timetable_id.id; + let conn = &mut db_pool.get().await?; + + let elec_profile_set = data.into_inner().electrical_profile_set_id; + let changeset = Timetable::changeset().electrical_profile_set_id(elec_profile_set); + changeset + .update_or_fail(conn, timetable_id, || TimetableError::NotFound { + timetable_id, + }) + .await?; + + let timetable = TimetableWithTrains::retrieve_or_fail(conn, timetable_id, || { + TimetableError::NotFound { timetable_id } + }) + .await?; + Ok(Json(timetable.into())) +} + +/// Return a specific timetable with its associated schedules +#[utoipa::path( + tag = "timetablev2", + params(TimetableIdParam), + responses( + (status = 204, description = "No content"), + (status = 404, description = "Timetable not found"), + ), +)] +#[delete("")] +async fn delete( + db_pool: Data, + timetable_id: Path, +) -> Result { + let timetable_id = timetable_id.id; + let conn = &mut db_pool.get().await?; + Timetable::delete_static_or_fail(conn, timetable_id, || TimetableError::NotFound { + timetable_id, + }) + .await?; + Ok(HttpResponse::NoContent().finish()) +} + +#[cfg(test)] +mod tests { + + use super::*; + use crate::fixtures::tests::{db_pool, timetable_v2, TestFixture}; + use crate::modelsv2::Delete; + use crate::views::tests::create_test_service; + use actix_web::test::{call_and_read_body_json, call_service, TestRequest}; + use rstest::rstest; + use serde_json::json; + + #[rstest] + async fn get_timetable(#[future] timetable_v2: TestFixture, db_pool: Data) { + let service = create_test_service().await; + let timetable = timetable_v2.await; + + let url = format!("/v2/timetable/{}", timetable.id()); + + // Should succeed + let request = TestRequest::get().uri(&url).to_request(); + let response = call_service(&service, request).await; + assert!(response.status().is_success()); + + // Delete the timetable + assert!(timetable + .model + .delete(&mut db_pool.get().await.unwrap()) + .await + .unwrap()); + + // Should fail + let request = TestRequest::get().uri(&url).to_request(); + let response = call_service(&service, request).await; + assert!(response.status().is_client_error()); + } + + #[rstest] + async fn timetable_post(db_pool: Data) { + let service = create_test_service().await; + + // Insert timetable + let request = TestRequest::post() + .uri("/v2/timetable") + .set_json(json!({ "electrical_profil_set_id": None::})) + .to_request(); + let response: TimetableResult = call_and_read_body_json(&service, request).await; + + // Delete the timetable + assert!( + Timetable::delete_static(&mut db_pool.get().await.unwrap(), response.id) + .await + .unwrap() + ); + } + + #[rstest] + async fn timetable_delete(#[future] timetable_v2: TestFixture) { + let timetable = timetable_v2.await; + let service = create_test_service().await; + let request = TestRequest::delete() + .uri(format!("/v2/timetable/{}", timetable.id()).as_str()) + .to_request(); + assert!(call_service(&service, request).await.status().is_success()); + } + + #[rstest] + async fn timetable_list(#[future] timetable_v2: TestFixture) { + timetable_v2.await; + let service = create_test_service().await; + let request = TestRequest::get().uri("/v2/timetable/").to_request(); + assert!(call_service(&service, request).await.status().is_success()); + } +} diff --git a/editoast/src/views/v2/train_schedule.rs b/editoast/src/views/v2/train_schedule.rs new file mode 100644 index 00000000000..788448d25ee --- /dev/null +++ b/editoast/src/views/v2/train_schedule.rs @@ -0,0 +1,342 @@ +use std::collections::HashSet; + +use crate::error::Result; +use crate::modelsv2::train_schedule::{TrainSchedule, TrainScheduleChangeset}; +use crate::modelsv2::Model; +use crate::schema::v2::trainschedule::{Distribution, TrainScheduleBase}; +use crate::DbPool; +use actix_web::web::{Data, Json, Path}; +use actix_web::{delete, get, post, put, HttpResponse}; +use editoast_derive::EditoastError; +use itertools::Itertools; +use serde::{Deserialize, Serialize}; +use thiserror::Error; +use utoipa::{IntoParams, ToSchema}; + +crate::routes! { + "/v2/train_schedule" => { + post, + delete, + "/{id}" => { + get, + put, + } + }, +} + +crate::schemas! { + Distribution, + TrainScheduleBase, + TrainScheduleForm, + TrainScheduleResult, + BatchDeletionRequest, +} + +#[derive(Debug, Error, EditoastError)] +#[editoast_error(base_id = "train_schedule_v2")] +pub enum TrainScheduleError { + #[error("Train Schedule '{train_schedule_id}', could not be found")] + #[editoast_error(status = 404)] + NotFound { train_schedule_id: i64 }, + #[error("{number} train schedule(s) could not be found")] + #[editoast_error(status = 404)] + BatchTrainScheduleNotFound { number: usize }, +} + +#[derive(IntoParams, Deserialize)] +struct TrainScheduleIdParam { + /// A train schedule ID + id: i64, +} + +#[derive(Debug, Default, Clone, Serialize, Deserialize, ToSchema)] +struct TrainScheduleResult { + id: i64, + timetable_id: i64, + #[serde(flatten)] + train_schedule: TrainScheduleBase, +} + +impl From for TrainScheduleResult { + fn from(value: TrainSchedule) -> Self { + Self { + id: value.id, + timetable_id: value.timetable_id, + train_schedule: TrainScheduleBase { + train_name: value.train_name, + labels: value.labels.into_iter().flatten().collect(), + rolling_stock_name: value.rolling_stock_name, + start_time: value.start_time, + schedule: value.schedule, + margins: value.margins, + initial_speed: value.initial_speed, + comfort: value.comfort, + path: value.path, + constraint_distribution: value.constraint_distribution, + speed_limit_tag: value.speed_limit_tag.map(Into::into), + power_restrictions: value.power_restrictions, + options: value.options, + }, + } + } +} + +#[derive(Debug, Default, Clone, Serialize, Deserialize, ToSchema)] +pub struct TrainScheduleForm { + pub timetable_id: i64, + #[serde(flatten)] + pub train_schedule: TrainScheduleBase, +} + +impl From for TrainScheduleChangeset { + fn from(value: TrainScheduleForm) -> Self { + let TrainScheduleForm { + timetable_id, + train_schedule: ts, + } = value; + + TrainSchedule::changeset() + .timetable_id(timetable_id) + .comfort(ts.comfort) + .constraint_distribution(ts.constraint_distribution) + .initial_speed(ts.initial_speed) + .labels(ts.labels.into_iter().map(Some).collect()) + .margins(ts.margins) + .path(ts.path) + .power_restrictions(ts.power_restrictions) + .rolling_stock_name(ts.rolling_stock_name) + .schedule(ts.schedule) + .speed_limit_tag(ts.speed_limit_tag.map(|s| s.0)) + .start_time(ts.start_time) + .train_name(ts.train_name) + .options(ts.options) + } +} + +#[derive(Debug, Deserialize, ToSchema)] +struct BatchDeletionRequest { + ids: HashSet, +} + +/// Create train schedule by batch +#[utoipa::path( + tag = "train_schedulev2", + request_body = Vec, + responses( + (status = 200, description = "The train schedule", body = Vec) + ) +)] +#[post("")] +async fn post( + db_pool: Data, + data: Json>, +) -> Result>> { + use crate::modelsv2::CreateBatch; + + let changesets: Vec = + data.into_inner().into_iter().map_into().collect(); + let conn = &mut db_pool.get().await?; + + // Create a batch of train_schedule + let train_schedule: Vec<_> = TrainSchedule::create_batch(conn, changesets).await?; + Ok(Json(train_schedule.into_iter().map_into().collect())) +} + +/// Return a specific timetable with its associated schedules +#[utoipa::path( + tag = "train_schedulev2", + params(TrainScheduleIdParam), + responses( + (status = 200, description = "The train schedule", body = TrainScheduleResult) + ) +)] +#[get("")] +async fn get( + db_pool: Data, + train_schedule_id: Path, +) -> Result> { + use crate::modelsv2::Retrieve; + + let train_schedule_id = train_schedule_id.id; + let conn = &mut db_pool.get().await?; + + // Return the timetable + let train_schedule = TrainSchedule::retrieve_or_fail(conn, train_schedule_id, || { + TrainScheduleError::NotFound { train_schedule_id } + }) + .await?; + Ok(Json(train_schedule.into())) +} + +/// Delete a train schedule and its result +#[utoipa::path( + tag = "train_schedulev2", + request_body = inline(BatchDeletionRequest), + responses( + (status = 204, description = "All train schedules have been deleted") + ) +)] +#[delete("")] +async fn delete(db_pool: Data, data: Json) -> Result { + use crate::modelsv2::DeleteBatch; + + let conn = &mut db_pool.get().await?; + let train_ids = data.into_inner().ids; + TrainSchedule::delete_batch_or_fail(conn, train_ids, |number| { + TrainScheduleError::BatchTrainScheduleNotFound { number } + }) + .await?; + + Ok(HttpResponse::NoContent().finish()) +} + +/// Update train schedule at once +#[utoipa::path( + tag = "train_schedulev2,timetable", + request_body = TrainScheduleForm, + params(TrainScheduleIdParam), + responses( + (status = 200, description = "The train schedule have been updated", body = TrainScheduleResult) + ) +)] +#[put("")] +async fn put( + db_pool: Data, + train_schedule_id: Path, + data: Json, +) -> Result> { + use crate::modelsv2::Update; + let conn = &mut db_pool.get().await?; + + let train_id = train_schedule_id.id; + let ts_changeset: TrainScheduleChangeset = data.into_inner().into(); + + let ts_result = ts_changeset + .update_or_fail(conn, train_id, || TrainScheduleError::NotFound { + train_schedule_id: train_id, + }) + .await?; + + Ok(Json(ts_result.into())) +} + +#[cfg(test)] +mod tests { + + use super::*; + use crate::fixtures::tests::{ + db_pool, timetable_v2, train_schedule_v2, TestFixture, TrainScheduleV2FixtureSet, + }; + use crate::modelsv2::timetable::Timetable; + use crate::modelsv2::{Delete, DeleteStatic}; + use crate::views::tests::create_test_service; + use actix_web::test::{call_and_read_body_json, call_service, TestRequest}; + use rstest::rstest; + use serde_json::json; + + #[rstest] + async fn get_trainschedule( + #[future] train_schedule_v2: TrainScheduleV2FixtureSet, + db_pool: Data, + ) { + let service = create_test_service().await; + let fixture = train_schedule_v2.await; + let url = format!("/v2/train_schedule/{}", fixture.train_schedule.id()); + + // Should succeed + let request = TestRequest::get().uri(&url).to_request(); + let response = call_service(&service, request).await; + assert!(response.status().is_success()); + + // Delete the train_schedule + assert!(fixture + .train_schedule + .model + .delete(&mut db_pool.get().await.unwrap()) + .await + .unwrap()); + + // Should fail + let request = TestRequest::get().uri(&url).to_request(); + let response = call_service(&service, request).await; + assert!(response.status().is_client_error()); + } + + #[rstest] + async fn train_schedule_post( + #[future] timetable_v2: TestFixture, + db_pool: Data, + ) { + let service = create_test_service().await; + + let timetable = timetable_v2.await; + // Insert train_schedule + let train_schedule_base: TrainScheduleBase = + serde_json::from_str(include_str!("../../tests/train_schedules/simple.json")) + .expect("Unable to parse"); + let train_schedule = TrainScheduleForm { + timetable_id: timetable.id(), + train_schedule: train_schedule_base, + }; + let request = TestRequest::post() + .uri("/v2/train_schedule") + .set_json(json!(vec![train_schedule])) + .to_request(); + let response: Vec = call_and_read_body_json(&service, request).await; + assert_eq!(response.len(), 1); + let train_id = response[0].id; + + // Delete the train_schedule + assert!( + TrainSchedule::delete_static(&mut db_pool.get().await.unwrap(), train_id) + .await + .is_ok() + ); + } + + #[rstest] + async fn train_schedule_delete(#[future] train_schedule_v2: TrainScheduleV2FixtureSet) { + let fixture = train_schedule_v2.await; + let service = create_test_service().await; + let request = TestRequest::delete() + .uri("/v2/train_schedule/") + .set_json(json!({"ids": vec![fixture.train_schedule.id()]})) + .to_request(); + let response = call_service(&service, request).await; + assert!(response.status().is_success()); + + // Delete should fail + + let request = TestRequest::delete() + .uri("/v2/train_schedule/") + .set_json(json!({"ids": vec![fixture.train_schedule.id()]})) + .to_request(); + assert_eq!(call_service(&service, request).await.status().as_u16(), 404); + } + + #[rstest] + async fn train_schedule_put(#[future] train_schedule_v2: TrainScheduleV2FixtureSet) { + let TrainScheduleV2FixtureSet { + timetable, + train_schedule, + } = train_schedule_v2.await; + let service = create_test_service().await; + let rs_name = String::from("NEW ROLLING_STOCK"); + let train_schedule_base: TrainScheduleBase = TrainScheduleBase { + rolling_stock_name: rs_name.clone(), + ..serde_json::from_str(include_str!("../../tests/train_schedules/simple.json")) + .expect("Unable to parse") + }; + let train_schedule_form = TrainScheduleForm { + timetable_id: timetable.id(), + train_schedule: train_schedule_base, + }; + let request = TestRequest::put() + .uri(format!("/v2/train_schedule/{}", train_schedule.id()).as_str()) + .set_json(json!(train_schedule_form)) + .to_request(); + + let response: TrainScheduleResult = call_and_read_body_json(&service, request).await; + assert_eq!(response.train_schedule.rolling_stock_name, rs_name) + } +} diff --git a/front/src/common/api/osrdEditoastApi.ts b/front/src/common/api/osrdEditoastApi.ts index 5eefb9ccda8..719b57e70e1 100644 --- a/front/src/common/api/osrdEditoastApi.ts +++ b/front/src/common/api/osrdEditoastApi.ts @@ -17,6 +17,8 @@ export const addTagTypes = [ 'stdcm', 'timetable', 'train_schedule', + 'timetablev2', + 'train_schedulev2', ] as const; const injectedRtkApi = api .enhanceEndpoints({ @@ -721,6 +723,76 @@ const injectedRtkApi = api }), providesTags: ['train_schedule'], }), + getV2Timetable: build.query({ + query: (queryArg) => ({ + url: `/v2/timetable/`, + params: { page: queryArg.page, page_size: queryArg.pageSize }, + }), + providesTags: ['timetablev2'], + }), + postV2Timetable: build.mutation({ + query: (queryArg) => ({ + url: `/v2/timetable/`, + method: 'POST', + body: queryArg.timetableForm, + }), + invalidatesTags: ['timetablev2'], + }), + deleteV2TimetableById: build.mutation< + DeleteV2TimetableByIdApiResponse, + DeleteV2TimetableByIdApiArg + >({ + query: (queryArg) => ({ url: `/v2/timetable/${queryArg.id}/`, method: 'DELETE' }), + invalidatesTags: ['timetablev2'], + }), + getV2TimetableById: build.query({ + query: (queryArg) => ({ url: `/v2/timetable/${queryArg.id}/` }), + providesTags: ['timetablev2'], + }), + putV2TimetableById: build.mutation({ + query: (queryArg) => ({ + url: `/v2/timetable/${queryArg.id}/`, + method: 'PUT', + body: queryArg.timetableForm, + }), + invalidatesTags: ['timetablev2'], + }), + deleteV2TrainSchedule: build.mutation< + DeleteV2TrainScheduleApiResponse, + DeleteV2TrainScheduleApiArg + >({ + query: (queryArg) => ({ + url: `/v2/train_schedule/`, + method: 'DELETE', + body: queryArg.body, + }), + invalidatesTags: ['train_schedulev2'], + }), + postV2TrainSchedule: build.mutation< + PostV2TrainScheduleApiResponse, + PostV2TrainScheduleApiArg + >({ + query: (queryArg) => ({ url: `/v2/train_schedule/`, method: 'POST', body: queryArg.body }), + invalidatesTags: ['train_schedulev2'], + }), + getV2TrainScheduleById: build.query< + GetV2TrainScheduleByIdApiResponse, + GetV2TrainScheduleByIdApiArg + >({ + query: (queryArg) => ({ url: `/v2/train_schedule/${queryArg.id}/` }), + providesTags: ['train_schedulev2'], + }), + putV2TrainScheduleById: build.mutation< + PutV2TrainScheduleByIdApiResponse, + PutV2TrainScheduleByIdApiArg + >({ + query: (queryArg) => ({ + url: `/v2/train_schedule/${queryArg.id}/`, + method: 'PUT', + body: queryArg.trainScheduleForm, + }), + invalidatesTags: ['train_schedulev2', 'timetable'], + }), getVersion: build.query({ query: () => ({ url: `/version/` }), }), @@ -1348,6 +1420,59 @@ export type GetTrainScheduleByIdResultApiArg = { /** A train schedule ID */ id: number; }; +export type GetV2TimetableApiResponse = + /** status 200 List timetables */ PaginatedResponseOfTimetable; +export type GetV2TimetableApiArg = { + page?: number; + pageSize?: number | null; +}; +export type PostV2TimetableApiResponse = + /** status 200 Timetable with train schedules ids */ TimetableResult; +export type PostV2TimetableApiArg = { + timetableForm: TimetableForm; +}; +export type DeleteV2TimetableByIdApiResponse = unknown; +export type DeleteV2TimetableByIdApiArg = { + /** A timetable ID */ + id: number; +}; +export type GetV2TimetableByIdApiResponse = + /** status 200 Timetable with train schedules ids */ TimetableDetailedResult; +export type GetV2TimetableByIdApiArg = { + /** A timetable ID */ + id: number; +}; +export type PutV2TimetableByIdApiResponse = + /** status 200 Timetable with train schedules ids */ TimetableDetailedResult; +export type PutV2TimetableByIdApiArg = { + /** A timetable ID */ + id: number; + timetableForm: TimetableForm; +}; +export type DeleteV2TrainScheduleApiResponse = unknown; +export type DeleteV2TrainScheduleApiArg = { + body: { + ids: number[]; + }; +}; +export type PostV2TrainScheduleApiResponse = + /** status 200 The train schedule */ TrainScheduleResult[]; +export type PostV2TrainScheduleApiArg = { + body: TrainScheduleForm[]; +}; +export type GetV2TrainScheduleByIdApiResponse = + /** status 200 The train schedule */ TrainScheduleResult; +export type GetV2TrainScheduleByIdApiArg = { + /** A train schedule ID */ + id: number; +}; +export type PutV2TrainScheduleByIdApiResponse = + /** status 200 The train schedule have been updated */ TrainScheduleResult; +export type PutV2TrainScheduleByIdApiArg = { + /** A train schedule ID */ + id: number; + trainScheduleForm: TrainScheduleForm; +}; export type GetVersionApiResponse = /** status 200 Return the service version */ Version; export type GetVersionApiArg = void; export type GetVersionCoreApiResponse = /** status 200 Return the core service version */ Version; @@ -2500,6 +2625,94 @@ export type TrainScheduleBatchItem = { speed_limit_tags?: string | null; train_name: string; }; +export type TimetableResult = { + electrical_profile_set_id?: number | null; + id: number; +}; +export type PaginatedResponseOfTimetable = { + /** The total number of items */ + count: number; + /** The next page number */ + next: number | null; + /** The previous page number */ + previous: number | null; + /** The list of results */ + results: TimetableResult[]; +}; +export type TimetableForm = { + electrical_profile_set_id?: number | null; +}; +export type TimetableDetailedResult = { + electrical_profile_set_id?: number | null; + id: number; +} & { + train_ids: number[]; +}; +export type Distribution = 'STANDARD' | 'MARECO'; +export type TrainScheduleBase = { + comfort: 'STANDARD' | 'AIR_CONDITIONING' | 'HEATING'; + constraint_distribution: Distribution; + initial_speed: number; + labels: string[]; + margins: { + boundaries: string[]; + /** The values of the margins. Must contains one more element than the boundaries + Can be a percentage `X%`, a time in minutes per kilometer `Xmin/km` or `0` */ + values: string[]; + }; + options: { + use_electrical_profiles: boolean; + }; + path: (( + | { + /** The offset in millimeters from the start of the track */ + offset: number; + track: string; + } + | { + operational_point: string; + } + | { + /** An optional secondary code to identify a more specific location */ + secondary_code?: string | null; + trigram: string; + } + | { + /** An optional secondary code to identify a more specific location */ + secondary_code?: string | null; + /** The [UIC](https://en.wikipedia.org/wiki/Railway_vehicle_owner%27s_code) code of an operational point */ + uic: number; + } + ) & { + /** Metadata given to mark a point as wishing to be deleted by the user. + It's useful for soft deleting the point (waiting to fix / remove all references) + If true, the train schedule is consider as invalid and must be edited */ + deleted?: boolean; + id: string; + })[]; + power_restrictions: { + from: string; + to: string; + value: string; + }[]; + rolling_stock_name: string; + schedule: { + arrival?: string | null; + at: string; + locked?: boolean; + stop_for?: string | null; + }[]; + speed_limit_tag?: string | null; + start_time: string; + train_name: string; +}; +export type TrainScheduleResult = TrainScheduleBase & { + id: number; + timetable_id: number; +}; +export type TrainScheduleForm = TrainScheduleBase & { + timetable_id: number; +}; export type Version = { git_describe: string | null; }; From 0cad0d3fededc9998010bd99e5c28eb8c7bdefdb Mon Sep 17 00:00:00 2001 From: Youness Chrifi Alaoui Date: Tue, 27 Feb 2024 09:59:50 +0100 Subject: [PATCH 9/9] front: add new errors translation for train schedule v2 --- front/public/locales/en/errors.json | 4 ++++ front/public/locales/fr/errors.json | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/front/public/locales/en/errors.json b/front/public/locales/en/errors.json index 974f537d9c3..80ceed54c0a 100644 --- a/front/public/locales/en/errors.json +++ b/front/public/locales/en/errors.json @@ -168,6 +168,10 @@ "TimetableNotFound": "Timetable '{{timetable_id}}' could not be found", "UnsimulatedTrainSchedule": "Train Schedule '{{train_schedule_id}}' is not simulated" }, + "train_schedule_v2": { + "BatchTrainScheduleNotFound": "'{{number}}' train schedule(s) could not be found", + "NotFound": "Train Schedule '{{train_schedule_id}}' could not be found" + }, "url": { "InvalidUrl": "Invalid url '{{url}}'" } diff --git a/front/public/locales/fr/errors.json b/front/public/locales/fr/errors.json index 3f6b54d81fc..66700dd3540 100644 --- a/front/public/locales/fr/errors.json +++ b/front/public/locales/fr/errors.json @@ -168,6 +168,10 @@ "TimetableNotFound": "Grille horaire '{{timetable_id}}' non trouvée", "UnsimulatedTrainSchedule": "La circulation '{{train_schedule_id}}' n'est pas simulée" }, + "train_schedule_v2": { + "BatchTrainScheduleNotFound": "'{{number}}' circulation(s) n'ont pas pu être trouvée(s)", + "NotFound": "Circulation '{{train_schedule_id}}' non trouvée" + }, "url": { "InvalidUrl": "Url invalide '{{url}}'" }