diff --git a/Cargo.lock b/Cargo.lock index 06484658..308e3b97 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -255,6 +255,21 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + [[package]] name = "anes" version = "0.1.6" @@ -539,6 +554,19 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chrono" +version = "0.4.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "num-traits", + "serde", + "windows-targets 0.48.5", +] + [[package]] name = "ciborium" version = "0.2.1" @@ -828,6 +856,41 @@ dependencies = [ "syn 2.0.39", ] +[[package]] +name = "darling" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0209d94da627ab5605dcccf08bb18afa5009cfbef48d8a8b7d7bdbc79be25c5e" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "177e3443818124b357d8e76f53be906d60937f0d3a90773a664fa63fa253e621" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.39", +] + +[[package]] +name = "darling_macro" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.39", +] + [[package]] name = "data-url" version = "0.2.0" @@ -885,6 +948,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0f32d04922c60427da6f9fef14d042d9edddef64cb9d4ce0d64d0685fbeb1fd3" dependencies = [ "powerfmt", + "serde", ] [[package]] @@ -1474,6 +1538,35 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" +[[package]] +name = "iana-time-zone" +version = "0.1.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8326b86b6cff230b97d0d312a6c40a60726df3332e721f72a1b035f451663b20" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + [[package]] name = "idna" version = "0.4.0" @@ -1513,6 +1606,7 @@ dependencies = [ "autocfg", "hashbrown 0.12.3", "rayon", + "serde", ] [[package]] @@ -1523,6 +1617,7 @@ checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" dependencies = [ "equivalent", "hashbrown 0.14.2", + "serde", ] [[package]] @@ -1757,6 +1852,7 @@ dependencies = [ "semver", "serde", "serde_json", + "serde_with", "serde_yaml", "spreet", "subst", @@ -1788,6 +1884,7 @@ dependencies = [ "rstest", "serde", "serde_json", + "serde_with", "serde_yaml", "sqlite-hashes", "sqlx", @@ -2903,6 +3000,35 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_with" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64cd236ccc1b7a29e7e2739f27c0b2dd199804abc4290e32f59f3b68d6405c23" +dependencies = [ + "base64", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.1.0", + "serde", + "serde_json", + "serde_with_macros", + "time", +] + +[[package]] +name = "serde_with_macros" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93634eb5f75a2323b16de4748022ac4297f9e76b6dced2be287a099f41b5e788" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.39", +] + [[package]] name = "serde_yaml" version = "0.9.27" @@ -2949,9 +3075,9 @@ dependencies = [ [[package]] name = "signature" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e1788eed21689f9cf370582dfc467ef36ed9c707f073528ddafa8d83e3b8500" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" dependencies = [ "digest", "rand_core", @@ -4020,6 +4146,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows-core" +version = "0.51.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64" +dependencies = [ + "windows-targets 0.48.5", +] + [[package]] name = "windows-sys" version = "0.45.0" diff --git a/Cargo.toml b/Cargo.toml index 743cffe3..97c95d0e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,6 +50,7 @@ rustls-pemfile = "1" semver = "1" serde = { version = "1", features = ["derive"] } serde_json = "1" +serde_with = "3" serde_yaml = "0.9" spreet = { version = "0.9", default-features = false } sqlite-hashes = { version = "0.5", default-features = false, features = ["md5", "window", "hex"] } diff --git a/martin/Cargo.toml b/martin/Cargo.toml index 16998806..fc36ced5 100644 --- a/martin/Cargo.toml +++ b/martin/Cargo.toml @@ -82,6 +82,7 @@ rustls.workspace = true semver.workspace = true serde.workspace = true serde_json = { workspace = true, features = ["preserve_order"] } +serde_with.workspace = true serde_yaml.workspace = true spreet.workspace = true subst.workspace = true diff --git a/martin/src/config.rs b/martin/src/config.rs index e165b8bc..fcb293e2 100644 --- a/martin/src/config.rs +++ b/martin/src/config.rs @@ -33,7 +33,7 @@ pub struct Config { #[serde(flatten)] pub srv: SrvConfig, - #[serde(default, skip_serializing_if = "OptOneMany::is_none")] + #[serde(default)] pub postgres: OptOneMany, #[serde(default, skip_serializing_if = "FileConfigEnum::is_none")] @@ -45,7 +45,7 @@ pub struct Config { #[serde(default, skip_serializing_if = "FileConfigEnum::is_none")] pub sprites: FileConfigEnum, - #[serde(default, skip_serializing_if = "OptOneMany::is_none")] + #[serde(default)] pub fonts: OptOneMany, #[serde(flatten)] diff --git a/martin/src/file_config.rs b/martin/src/file_config.rs index 50a57e45..0fdc0644 100644 --- a/martin/src/file_config.rs +++ b/martin/src/file_config.rs @@ -111,13 +111,13 @@ impl FileConfigEnum { } } +#[serde_with::skip_serializing_none] #[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)] pub struct FileConfig { /// A list of file paths #[serde(default, skip_serializing_if = "OptOneMany::is_none")] pub paths: OptOneMany, /// A map of source IDs to file paths or config objects - #[serde(skip_serializing_if = "Option::is_none")] #[serde(serialize_with = "sorted_opt_map")] pub sources: Option>, #[serde(flatten)] diff --git a/martin/src/fonts/mod.rs b/martin/src/fonts/mod.rs index 1edf2101..5d325bd1 100644 --- a/martin/src/fonts/mod.rs +++ b/martin/src/fonts/mod.rs @@ -107,10 +107,10 @@ pub struct FontSources { pub type FontCatalog = BTreeMap; +#[serde_with::skip_serializing_none] #[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)] pub struct CatalogFontEntry { pub family: String, - #[serde(skip_serializing_if = "Option::is_none")] pub style: Option, pub glyphs: usize, pub start: usize, diff --git a/martin/src/pg/config.rs b/martin/src/pg/config.rs index 5dfa3c1e..1bd1a9f3 100644 --- a/martin/src/pg/config.rs +++ b/martin/src/pg/config.rs @@ -20,41 +20,34 @@ pub trait PgInfo { fn to_tilejson(&self, source_id: String) -> TileJSON; } +#[serde_with::skip_serializing_none] #[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)] pub struct PgSslCerts { /// Same as PGSSLCERT /// ([docs](https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNECT-SSLCERT)) - #[serde(skip_serializing_if = "Option::is_none")] pub ssl_cert: Option, /// Same as PGSSLKEY /// ([docs](https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNECT-SSLKEY)) - #[serde(skip_serializing_if = "Option::is_none")] pub ssl_key: Option, /// Same as PGSSLROOTCERT /// ([docs](https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNECT-SSLROOTCERT)) - #[serde(skip_serializing_if = "Option::is_none")] pub ssl_root_cert: Option, } +#[serde_with::skip_serializing_none] #[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)] pub struct PgConfig { pub connection_string: Option, #[serde(flatten)] pub ssl_certificates: PgSslCerts, - #[serde(skip_serializing_if = "Option::is_none")] pub default_srid: Option, - #[serde(skip_serializing_if = "Option::is_none")] pub auto_bounds: Option, - #[serde(skip_serializing_if = "Option::is_none")] pub max_feature_count: Option, - #[serde(skip_serializing_if = "Option::is_none")] pub pool_size: Option, #[serde(default, skip_serializing_if = "OptBoolObj::is_none")] pub auto_publish: OptBoolObj, - #[serde(skip_serializing_if = "Option::is_none")] #[serde(serialize_with = "sorted_opt_map")] pub tables: Option, - #[serde(skip_serializing_if = "Option::is_none")] #[serde(serialize_with = "sorted_opt_map")] pub functions: Option, } @@ -70,12 +63,12 @@ pub struct PgCfgPublish { pub functions: OptBoolObj, } +#[serde_with::skip_serializing_none] #[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)] pub struct PgCfgPublishTables { #[serde(default, skip_serializing_if = "OptOneMany::is_none")] #[serde(alias = "from_schema")] pub from_schemas: OptOneMany, - #[serde(skip_serializing_if = "Option::is_none")] #[serde(alias = "id_format")] pub source_id_format: Option, /// A table column to use as the feature ID @@ -84,20 +77,17 @@ pub struct PgCfgPublishTables { #[serde(default, skip_serializing_if = "OptOneMany::is_none")] #[serde(alias = "id_column")] pub id_columns: OptOneMany, - #[serde(skip_serializing_if = "Option::is_none")] pub clip_geom: Option, - #[serde(skip_serializing_if = "Option::is_none")] pub buffer: Option, - #[serde(skip_serializing_if = "Option::is_none")] pub extent: Option, } +#[serde_with::skip_serializing_none] #[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)] pub struct PgCfgPublishFuncs { #[serde(default, skip_serializing_if = "OptOneMany::is_none")] #[serde(alias = "from_schema")] pub from_schemas: OptOneMany, - #[serde(skip_serializing_if = "Option::is_none")] #[serde(alias = "id_format")] pub source_id_format: Option, } diff --git a/martin/src/pg/config_function.rs b/martin/src/pg/config_function.rs index 151d3e54..c287b7a3 100644 --- a/martin/src/pg/config_function.rs +++ b/martin/src/pg/config_function.rs @@ -7,6 +7,7 @@ use crate::pg::utils::{patch_json, InfoMap}; pub type FuncInfoSources = InfoMap; +#[serde_with::skip_serializing_none] #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Default)] pub struct FunctionInfo { /// Schema name @@ -16,18 +17,15 @@ pub struct FunctionInfo { pub function: String, /// An integer specifying the minimum zoom level - #[serde(skip_serializing_if = "Option::is_none")] pub minzoom: Option, /// An integer specifying the maximum zoom level. MUST be >= minzoom - #[serde(skip_serializing_if = "Option::is_none")] pub maxzoom: Option, /// The maximum extent of available map tiles. Bounds MUST define an area /// covered by all zoom levels. The bounds are represented in WGS:84 /// latitude and longitude values, in the order left, bottom, right, top. /// Values may be integers or floating point numbers. - #[serde(skip_serializing_if = "Option::is_none")] pub bounds: Option, /// TileJSON provided by the SQL function comment. Not serialized. diff --git a/martin/src/pg/config_table.rs b/martin/src/pg/config_table.rs index b7bd0fb9..740d020b 100644 --- a/martin/src/pg/config_table.rs +++ b/martin/src/pg/config_table.rs @@ -10,10 +10,10 @@ use crate::utils::sorted_opt_map; pub type TableInfoSources = InfoMap; +#[serde_with::skip_serializing_none] #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Default)] pub struct TableInfo { /// ID of the layer as specified in a tile (ST_AsMVT param) - #[serde(skip_serializing_if = "Option::is_none")] pub layer_id: Option, /// Table schema @@ -37,38 +37,30 @@ pub struct TableInfo { pub is_view: Option, /// Feature id column name - #[serde(skip_serializing_if = "Option::is_none")] pub id_column: Option, /// An integer specifying the minimum zoom level - #[serde(skip_serializing_if = "Option::is_none")] pub minzoom: Option, /// An integer specifying the maximum zoom level. MUST be >= minzoom - #[serde(skip_serializing_if = "Option::is_none")] pub maxzoom: Option, /// The maximum extent of available map tiles. Bounds MUST define an area /// covered by all zoom levels. The bounds are represented in WGS:84 /// latitude and longitude values, in the order left, bottom, right, top. /// Values may be integers or floating point numbers. - #[serde(skip_serializing_if = "Option::is_none")] pub bounds: Option, /// Tile extent in tile coordinate space - #[serde(skip_serializing_if = "Option::is_none")] pub extent: Option, /// Buffer distance in tile coordinate space to optionally clip geometries - #[serde(skip_serializing_if = "Option::is_none")] pub buffer: Option, /// Boolean to control if geometries should be clipped or encoded as is - #[serde(skip_serializing_if = "Option::is_none")] pub clip_geom: Option, /// Geometry type - #[serde(skip_serializing_if = "Option::is_none")] pub geometry_type: Option, /// List of columns, that should be encoded as tile properties diff --git a/martin/src/pg/configurator.rs b/martin/src/pg/configurator.rs index cefbdba4..4d68a02c 100644 --- a/martin/src/pg/configurator.rs +++ b/martin/src/pg/configurator.rs @@ -27,32 +27,21 @@ pub type SqlFuncInfoMapMap = InfoMap>; pub type SqlTableInfoMapMapMap = InfoMap>>; #[derive(Debug, PartialEq)] -#[cfg_attr(test, derive(serde::Serialize))] +#[cfg_attr(test, serde_with::skip_serializing_none, derive(serde::Serialize))] pub struct PgBuilderFuncs { - #[cfg_attr(test, serde(skip_serializing_if = "Option::is_none"))] schemas: Option>, source_id_format: String, } #[derive(Debug, Default, PartialEq)] -#[cfg_attr(test, derive(serde::Serialize))] +#[cfg_attr(test, serde_with::skip_serializing_none, derive(serde::Serialize))] pub struct PgBuilderTables { - #[cfg_attr( - test, - serde( - skip_serializing_if = "Option::is_none", - serialize_with = "crate::utils::sorted_opt_set" - ) - )] + #[cfg_attr(test, serde(serialize_with = "crate::utils::sorted_opt_set"))] schemas: Option>, source_id_format: String, - #[cfg_attr(test, serde(skip_serializing_if = "Option::is_none"))] id_columns: Option>, - #[cfg_attr(test, serde(skip_serializing_if = "Option::is_none"))] clip_geom: Option, - #[cfg_attr(test, serde(skip_serializing_if = "Option::is_none"))] buffer: Option, - #[cfg_attr(test, serde(skip_serializing_if = "Option::is_none"))] extent: Option, } diff --git a/martin/src/source.rs b/martin/src/source.rs index 2c0e246e..db1e4e04 100644 --- a/martin/src/source.rs +++ b/martin/src/source.rs @@ -135,16 +135,13 @@ impl Clone for Box { } } +#[serde_with::skip_serializing_none] #[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)] pub struct CatalogSourceEntry { pub content_type: String, - #[serde(skip_serializing_if = "Option::is_none")] pub content_encoding: Option, - #[serde(skip_serializing_if = "Option::is_none")] pub name: Option, - #[serde(skip_serializing_if = "Option::is_none")] pub description: Option, - #[serde(skip_serializing_if = "Option::is_none")] pub attribution: Option, } diff --git a/martin/src/srv/config.rs b/martin/src/srv/config.rs index f0d11b6e..0db8db58 100644 --- a/martin/src/srv/config.rs +++ b/martin/src/srv/config.rs @@ -3,13 +3,11 @@ use serde::{Deserialize, Serialize}; pub const KEEP_ALIVE_DEFAULT: u64 = 75; pub const LISTEN_ADDRESSES_DEFAULT: &str = "0.0.0.0:3000"; +#[serde_with::skip_serializing_none] #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Default)] pub struct SrvConfig { - #[serde(skip_serializing_if = "Option::is_none")] pub keep_alive: Option, - #[serde(skip_serializing_if = "Option::is_none")] pub listen_addresses: Option, - #[serde(skip_serializing_if = "Option::is_none")] pub worker_processes: Option, } diff --git a/mbtiles/Cargo.toml b/mbtiles/Cargo.toml index c31ab35c..11d024f0 100644 --- a/mbtiles/Cargo.toml +++ b/mbtiles/Cargo.toml @@ -21,6 +21,7 @@ log.workspace = true martin-tile-utils.workspace = true serde.workspace = true serde_json.workspace = true +serde_with.workspace = true sqlite-hashes.workspace = true sqlx.workspace = true thiserror.workspace = true