From 65dc03b3ddccc5bbd3cd61ffa22e7e596b8f459b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kat=20March=C3=A1n?= Date: Thu, 28 May 2026 18:44:29 -0700 Subject: [PATCH] IT LIVES --- .github/workflows/ci.yml | 2 +- Cargo.toml | 2 +- clippy.toml | 2 +- src/de.rs | 105 ++++++++++++++++++++++++++++++++------- src/lib.rs | 2 +- 5 files changed, 92 insertions(+), 21 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4ad175e..a8593cc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -28,7 +28,7 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - rust: [1.96, stable] + rust: [1.95, stable] os: [ubuntu-latest, macOS-latest, windows-latest] steps: diff --git a/Cargo.toml b/Cargo.toml index 36be272..03ffee7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ readme = "README.md" homepage = "https://kdl.dev" repository = "https://github.com/kdl-org/kdl-rs" keywords = ["kdl", "document", "serialization", "config"] -rust-version = "1.96" +rust-version = "1.95" edition = "2021" [features] diff --git a/clippy.toml b/clippy.toml index 5bb56f2..83d78ba 100644 --- a/clippy.toml +++ b/clippy.toml @@ -1 +1 @@ -msrv = "1.96" +msrv = "1.95" diff --git a/src/de.rs b/src/de.rs index 26828f3..23ca365 100644 --- a/src/de.rs +++ b/src/de.rs @@ -44,7 +44,7 @@ use serde::de::value::StringDeserializer; use serde::de::{self, DeserializeSeed, Deserializer, MapAccess, SeqAccess, Visitor}; use serde::Deserialize; -use crate::{KdlDocument, KdlEntry, KdlNode, KdlValue}; +use crate::{KdlDocument, KdlEntry, KdlIdentifier, KdlNode, KdlValue}; /// Errors that can occur during KDL deserialization. #[derive(Debug, Clone)] @@ -111,6 +111,49 @@ where T::deserialize(de) } +struct IdentDeserializer<'a> { + ident: &'a KdlIdentifier, +} + +impl<'de, 'a> de::Deserializer<'de> for IdentDeserializer<'a> { + type Error = Error; + + fn deserialize_any>(self, visitor: V) -> Result { + visitor.visit_str(self.ident.value()) + } + + fn deserialize_string>(self, visitor: V) -> Result { + visitor.visit_string(self.ident.value().into()) + } + + fn deserialize_str>(self, visitor: V) -> Result { + visitor.visit_str(self.ident.value()) + } + + fn deserialize_newtype_struct>( + self, + _name: &'static str, + visitor: V, + ) -> Result { + visitor.visit_newtype_struct(self) + } + + fn deserialize_enum>( + self, + _name: &'static str, + _variants: &'static [&'static str], + visitor: V, + ) -> Result { + visitor.visit_enum(str_deserializer(self.ident.value())) + } + + serde::forward_to_deserialize_any! { + bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char + bytes byte_buf unit unit_struct seq tuple + tuple_struct map struct option identifier ignored_any + } +} + struct ValueDeserializer<'a> { value: &'a KdlValue, } @@ -698,6 +741,7 @@ struct NodeMapAccess<'a> { } enum NodeMapValue<'a> { + Ident(&'a KdlIdentifier), Arg(&'a KdlValue), Args(Vec<&'a KdlEntry>), SingleNode(&'a KdlNode), @@ -708,6 +752,10 @@ impl<'a> NodeMapAccess<'a> { fn new(node: &'a KdlNode) -> Self { let mut entries: Vec<(Cow<'a, str>, NodeMapValue<'a>)> = Vec::new(); + entries.push(("#name".into(), NodeMapValue::Ident(node.name()))); + if let Some(ty) = node.ty() { + entries.push(("#type".into(), NodeMapValue::Ident(ty))) + } let args: Vec<_> = node .entries() .iter() @@ -720,19 +768,25 @@ impl<'a> NodeMapAccess<'a> { .collect(); for (i, arg) in args.iter().enumerate() { - entries.push(( - format!("$argument{}", i + 1).into(), - NodeMapValue::Arg(arg.value()), - )); + entries.push((format!("#{}", i).into(), NodeMapValue::Arg(arg.value()))); + if let Some(ty) = arg.ty() { + entries.push((format!("#{}#type", i).into(), NodeMapValue::Ident(ty))); + } } if !args.is_empty() { - entries.push(("$arguments".into(), NodeMapValue::Args(args.clone()))); + entries.push(("#args".into(), NodeMapValue::Args(args.clone()))); } for prop in &props { let name = prop.name().unwrap().value(); - entries.push((format!("@{}", name).into(), NodeMapValue::Arg(prop.value()))); + entries.push(( + format!("#@{}", name).into(), + NodeMapValue::Arg(prop.value()), + )); + if let Some(ty) = prop.ty() { + entries.push((format!("#@{}#type", name).into(), NodeMapValue::Ident(ty))); + } entries.push((name.into(), NodeMapValue::Arg(prop.value()))); } @@ -786,6 +840,7 @@ impl<'de, 'a> MapAccess<'de> for NodeMapAccess<'a> { let (_, ref value) = self.entries[self.idx]; self.idx += 1; match value { + NodeMapValue::Ident(ident) => seed.deserialize(IdentDeserializer { ident }), NodeMapValue::Arg(v) => seed.deserialize(ValueDeserializer { value: v }), NodeMapValue::Args(entries) => seed.deserialize(ArgsSeqDeserializer { entries: entries.clone(), @@ -1346,9 +1401,9 @@ h 8 fn rename_props() { #[derive(Deserialize, Debug, PartialEq)] struct Server { - #[serde(rename = "@host")] + #[serde(rename = "#@host")] host: String, - #[serde(rename = "@port")] + #[serde(rename = "#@port")] port: u16, } @@ -1372,13 +1427,26 @@ h 8 #[test] fn rename_args() { + #[derive(Deserialize, Debug, PartialEq)] + #[serde(rename_all = "kebab-case")] + enum HostType { + IpAddr, + Hostname + } + #[derive(Deserialize, Debug, PartialEq)] struct Server { - #[serde(rename = "$argument1")] + #[serde(rename = "#type")] + server_type: String, + #[serde(rename = "#0#type")] + host_type: HostType, + #[serde(rename = "#0")] host: String, - #[serde(rename = "@port")] + #[serde(rename = "#@port#type")] + port_type: String, + #[serde(rename = "#@port")] port: u16, - #[serde(rename = "$arguments")] + #[serde(rename = "#args")] all: Vec, } @@ -1387,13 +1455,16 @@ h 8 server: Server, } - let kdl = "server localhost port=8080 remote"; + let kdl = "(linux-debian-arm64)server (hostname)localhost port=(number)8080 remote"; let config: Config = from_str(kdl).unwrap(); assert_eq!( config, Config { server: Server { + server_type: "linux-debian-arm64".into(), + host_type: HostType::Hostname, host: "localhost".into(), + port_type: "number".into(), port: 8080, all: Vec::from(["localhost".into(), "remote".into()]) }, @@ -1405,9 +1476,9 @@ h 8 fn rename_all_args() { #[derive(Deserialize, Debug, PartialEq)] struct Command { - #[serde(rename = "@name")] + #[serde(rename = "#@name")] name: String, - #[serde(rename = "$arguments")] + #[serde(rename = "#args")] args: Vec, } @@ -1433,9 +1504,9 @@ h 8 fn rename_children_args() { #[derive(Deserialize, Debug, PartialEq)] struct Server { - #[serde(rename = "@host")] + #[serde(rename = "#@host")] host: String, - #[serde(rename = "$arguments")] + #[serde(rename = "#args")] ports: Vec, } diff --git a/src/lib.rs b/src/lib.rs index 2d487b0..ef28849 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -140,7 +140,7 @@ //! //! ## Minimum Supported Rust Version (MSRV) //! -//! You must be at least `1.96` tall to get on this ride. +//! You must be at least `1.95` tall to get on this ride. //! //! ## License //!