{"id":16851907,"url":"https://github.com/cad97/serde-kdl","last_synced_at":"2025-03-18T09:40:23.818Z","repository":{"id":90989886,"uuid":"410713427","full_name":"CAD97/serde-kdl","owner":"CAD97","description":"A [serde](https://serde.rs/) serializer for [KDL](https://kdl.dev/)","archived":false,"fork":false,"pushed_at":"2021-11-08T05:05:21.000Z","size":62,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-01-24T16:11:10.869Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/CAD97.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE/APACHE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-09-27T02:10:31.000Z","updated_at":"2022-03-15T19:14:49.000Z","dependencies_parsed_at":null,"dependency_job_id":"ace31c39-d2a8-43e4-b496-7b4ae057f2a8","html_url":"https://github.com/CAD97/serde-kdl","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CAD97%2Fserde-kdl","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CAD97%2Fserde-kdl/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CAD97%2Fserde-kdl/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CAD97%2Fserde-kdl/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/CAD97","download_url":"https://codeload.github.com/CAD97/serde-kdl/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244196790,"owners_count":20414373,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-13T13:34:04.444Z","updated_at":"2025-03-18T09:40:23.799Z","avatar_url":"https://github.com/CAD97.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# serde-kdl\n\nYet another implementation of the [KDL Document Language], with a focus on\ncreating \"obvious\" mappings between KDL and the Serde data model, pulling from\nprecident set by serde-json, suitable for human inspection and modification.\n\n## See also\n\n- [kdl-org/kdl](https://github.com/kdl-org/kdl), the KDL specification\n- [kdl-org/kdl-rs](https://github.com/kdl-org/kdl-rs), the Rust reference implementation\n- [Lucretiel/kaydle](https://github.com/lucretiel/kaydle), another in-progress KDL ser/de\n\n## Serde-in-KDL (SiK)\n\nThis is an informal description of how we map between the [Serde data model]\nand the KDL language. This document serves a similar purpose to that of JiK\n(JSON-in-KDL) and XiK (XML-in-KDL).\n\nThis is version 0.0.0-dev of SiK.\n\nNote that this is not an official KDL document, and may be incompatible with\nother Serde/KDL mappings. In this document, \"serialization\" refers to the\nprocess of encoding a Serde value in KDL, and \"deserialization\" refers to the\nreverse, decoding an SiK encoded document to the Serde data model.\n\n### Goals\n\nThe main translation issue is that Serde is a value-based serialization\nframework, while KDL is a node-based document language. Because of this\ndisconnect, one of the two sides of the bridge is going to feel somewhat\nstunted. Here, and in other design decisions, we follow the lead of JiK and\nXiK, in that we favor first the complete embedding of the Serde data model,\nand then the ergonomics of the KDL microsyntax.\n\nAs such, like JiK, we introduce the idea of a \"literal node\" — that is, a\n[KDL Node] with name `-`. This node serves the sole purpose of holding\n[KDL Argument]s, [KDL Property]s, and (optionally) a [KDL Children Block].\n\nSerde leaf values to a literal node with a single KDL Value, and serde compound\nvalues map to a series of child nodes. Additionally, a KDL Value or KDL\nProperty may be used instead of a child leaf node, if (and only if) this does\nnot break KDL's ordering rules. (In practice, this means that Arguments must\nappear before Properties, and that any data structure which should preserve\norder must appear in a child block.) More specific rules are introduced in the\ndetailed mapping specification.\n\nThis results in a KDL microsyntax that feels fairly natural to read, write, and\nedit, and takes advantage of most of the expressiveness that KDL offers. It is,\nhowever, still a microsyntax, as not all valid KDL documents are necessarily\nvalid SiK. The main class of invalid KDL documents are those that utilize the\nsemantic difference between arguments/properties and child nodes, as SiK makes\nno such distinction.\n\n### Mappings\n\nSerde's 14 primitive types map in the trivial manner: `bool` maps to\n[KDL Boolean], all `iN`, `uN`, and `fN` map to [KDL Number], and `char` maps\nto a [KDL String] of length 1. Similarly, a Serde `string` maps to a KDL\nString as well.\n\nSerde `byte array` (`[u8]`) maps to a Base64-encoded KDL String,\noptionally with the standard `base64` type annotation.\n\nSerde `option` follows serde-json's example and maps to either [KDL Null] for\na none value or the wrapped some value directly[^1].\n\nSerde `unit` maps to KDL Null. Serde `unit_struct` serializes as if it were\n`unit`[^4]. Serde `unit_variant` serializes as if it were `unit`, but with\na _mandatory_ [KDL Type Annotation] of the name of the variant.\n\nSerde `newtype_struct` serializes as if it were its wrapped value[^4]. Serde\n`newtype_variant` serializes as if it were `newtype_struct`, but with a\n_mandatory_ KDL Type Annotation of the name of the variant.\n\nSerde `seq` and `tuple` are both ordered collections which serialize as a KDL\nChildren Block containing literal (`-`) nodes. Order is significant, so these\nMUST NOT be represented as KDL Values. `tuple_struct` serializes as if it were\n`tuple`[^4]. `tuple_variant` serializes as if it were `tuple_struct`, but with\na _mandatory_ KDL Type Annotation of the name of the variant. If members of the\n`tuple` are simple values (that is, serialize as a single KDL Value), they\nMAY[^5] be uplifted and serialized as KDL Values of the `tuple` literal node,\nrather than child literal nodes.\n\nSerde `struct` is an unordered key-value pairing that serializes as a sequence\nof child nodes, where the node name is the struct field key, and the node's\nvalue is the serialized value. Serde `struct_variant` serializes as if it were\n`struct`, but with a _mandatory_ KDL Type Annotation of the name of the\nvariant. As `struct` is unordered, any fields which have simple values MAY[^5]\nbe uplifted and serialized as KDL Properties of the `struct` node, rather than\nchild literal nodes. However, even though the fields _are_ unordered, they have\na \"natural\" order, and KDL Arguments MAY[^5] be used for fields as well. Each\nKDL Argument maps to the next field in the natural order. All KDL Arguments\nMUST occur before any KDL Properties; a KDL Argument after a KDL Property MUST\nresult in an error. Any duplicated struct fields (whether from a duplicated\nname or a clash with an implicit name) SHOULD pass these through as duplicates\nin the Serde model. If they are not passed through, then a KDL Argument\nduplicating a named field MUST be an error, a KDL Property duplicating a child\nnode MUST also be an error, and a KDL Property duplicating a KDL Property MUST\nbe rightmost[^6]-Property wins, as specified by the KDL specification.\n\nSerde `map` is tricky, due to the fact that KDL has no native representation\nfor value-value mappings. Note, though, that many serde data formats, such as \nserde-json, only support string-keyed maps. Note also that `map` is an\n_ordered_ mapping, so it _must_ be represented by a KDL Children Block.\n\nThe only map supported without extensions is string-keyed maps. A string-keyed\nmap serializes the same as a `struct`, but MUST NOT use any Proprties. Map keys\nSHOULD be quoted, even when not required by the KDL spec. Maps are inherently\nambiguous with a non-`map` mapping to the same data structure, of just `struct`\nand/or `tuple`. The disambiguation is done via contextual type.\n\nWith extensions, other formats for map entries MAY be allowed. Mixing map entry\nencoding styles MAY be accepted, but is _very strongly_ discouraged for KDL\nauthors and implementation serialization routines. Map entry encoding MAY be\nrequired to match. If an implementation is capable, it SHOULD warn if map entry\nencoding style does not match.\n\n- If the key type is a string, the child node's name is the key string. If and\n  only if the key is a valid KDL identifier, it may be a bare identifier;\n  otherwise, it must be quoted (which is still valid KDL).\n- If the key type is a leaf value, the child node is a literal node with name\n  `-`, and has a property with name `key` which contains the map key; the map\n  value is the node's single argument (if a leaf value) or a children block.\n- The child node is a `tuple` of the key and value[^2].\n- The child node is a `struct` with fields `key` and `value`.\n- _AUTHOR NOTE:_ whoops, I think `- { - { key 0; value {}; } }` is potentially\n  ambiguous with the shortened version `- { - key=0 { value {} } }`, as that\n  could also be `- { - { key 0; value { value {} }; } }`... this is the only\n  place in the spec that allows \"flattening\" a child struct into the parent\n  in this fashion; a tuple `(u32, struct)` cannot (currently) be represented\n  as `node 0 { field {} }`, but has to be `node 0 { - { field {} } }` (short)\n  or `node { - 0; - { field {} }; }` (long). We should investigate if we can\n  make this flattening generally acceptable for deserialization (and then)\n  remove case 2 here as falling out of case 3 and 4 plus flattening, even if\n  serialization will never emit such, due to rewriting expense.\n\n### Standard Extensions\n\n- When a node is known to be a literal node by contextual typing, a name other\n  than `-` (e.g. `item` for a list `items`) MAY be allowed. An implementation\n  MAY place whatever restriction on such replacement names it wishes, but a\n  document using solely `-` literal nodes MUST successfully deserialize. (Note\n  that this _does_ allow disallowing mixing of named and `-` literal nodes.)\n- An implementation MAY allow mixing of KDL Arguments and KDL Properties. In\n  order to remain complient with the KDL specification, it MUST treat the KDL\n  identically to as if all KDL Arguments came before all KDL Properties. An\n  implementation with this extension SHOULD never serialize such SiK, as this\n  behavior is considered non-intuitive, and only exists as an extension for the\n  purpose of compatibility. (A notable use case is implementing SiK on top of a\n  KDL implementation that completely parses the document ahead of time, and\n  properly does not expose the relative ordering of arguments and properties.)\n- Type Annotations not requried by the spec MAY be allowed. If allowed, the\n  type annotations MUST be required to match the expected deserialization type,\n  and result in an error if mismatched. In the case multiple type annotations\n  could be valid (e.g. multiple `newtype_struct` layers), an implementation\n  that accepts a type annotation MUST document which types are allowed.\n- Where this spec requires a Type Annotation, and the value is placed in a\n  literal node, an implementation MAY instead accept the name of the literal\n  node _instead of_ the type annotation. The type annotation then MUST follow\n  the normal rules for optional type annotation.\n- Alternative wrapper type serialization. If supported, these SHOULD be an\n  option during serialization, and always accepted during deserialization.\n  - Serde `option` MAY optionally be de/serialized as if it were a normal Serde\n    `enum` variant of `None`/`Some`, rather than a special type.\n  - Serde `unit_struct` MAY optionally be de/serialized as if it were a Serde\n    `tuple_struct` containing a single `null`.\n  - Serde `unit_variant` MAY optionally be de/serialized as if it were a Serde\n    `tuple_variant` containing a single `null`.\n  - Serde `newtype_struct` MAY optionally be de/serialized as if it were a Serde\n    `tuple_struct` containing the wrapped value.\n  - Serde `newtype_variant` MAY optionally be de/serialized as if it were a Serde\n    `tuple_variant` containing the wrapped value.\n- Alternative map entry serialization.\n  - Map entries MAY be represented as a `tuple` of the key and value[^2].\n  - Map entries MAY be represented as a `struct` with field `key` and `value`.\n- For the specific case of a document root that is a Serde `struct` or `tuple`\n  (or a data type that maps as such _without_ a mandatory type annotation),\n  the literal node for the root MAY be omitted, instead placing its children as\n  the (multiple) root-level nodes of the document.[^3]\n\nIf an implementation claims to implement the SiK spec, it MUST do one of:\n\n- Declare that it implements \"SiK,\" and faithfully implement the base spec.\n  If it implements any extensions, it SHOULD instead do the following option.\n- Declare that it implements \"SiK with extensions,\" and faithfully implement\n  the base spec plus extensions. Any SiK documented extensions SHOULD be\n  documented, and any custom extensions MUST be documented. Base specification\n  conformant SiK documents MUST always deserialize properly, but documents\n  serialized with the implementation MAY require extensions to deserialize.\n- Declare that it implements \"SiK with variations.\" An implementation MAY\n  deviate from the spec, but it MUST document exactly how it deviates.\n  Additionally, such implementations are ENCOURAGED to submit their variation\n  as an extension to this SiK document, and document their variation as\n  mandatory use of the extension, for the purpose of interoperation.\n\n### Special Extensions\n\nThese are specific special-case alternate mappings between specific Serde value\nstructures and KDL, that help translate idioms of either side more accurately.\nThese extensions are less likely to be supported than previous extensions, as\nthey often require extra context that is not easily available in streaming\ncontexts (such as the `serde` crate's API). An implementation which makes a\ndifferentiation between Standard and Special Extensions SHOULD refer to the use\nof Special Extensions as \"Human SiK,\" as the purpose of these extensions is to\nmore closely map to how a human would write KDL if not under the restrictions\nof the SiK microsyntax.\n\n⚠ 🚧 This section is NON NORMATIVE and UNDER CONSTRUCTION 🚧 ⚠\n\n- I'd like `struct { ...; struct { ... } }`\u003csub\u003eserde\u003c/sub\u003e to be able to map\n  to more succinct KDL; it currently optimally (... properties only) maps as\n  `- ... { - ... }`\u003csub\u003eKDL\u003c/sub\u003e; I'd like `- ... - ...`\u003csub\u003eKDL\u003c/sub\u003e to be\n  valid. This would special case \"tail `struct`\". Also _very_ interesting is\n  special casing it into `- ... { ... }`, to support meaningful properties... 👀\n  - This might cause ambiguity in some cases? Is this resolvable in the serde\n    streaming deserialization API without buffering? As an example, consider\n    `- { key=0; { value { ... } }}`\u003csub\u003eKDL\u003c/sub\u003e, which might deserialize as\n    `struct { key; value }` or `struct { key; struct {value}}`\u003csub\u003eserde\u003c/sub\u003e.\n    This is especially meaningful for simplifying value-keyed map encoding.\n  - It's worth calling out that this allows the more succinct value-struct map\n    encoding of `- { - key=0 { ... } }`\u003csub\u003eKDL\u003c/sub\u003e, eliding the tail `value`\n    field into the child block.\n- Supporting an arbitrary number of arguments on a KDL Node followed by other\n  data would require a special case for `struct { seq; ... }`\u003csub\u003eserde\u003c/sub\u003e.\n  The author doesn't know how desirable this is; if you have a KDL document\n  that benefits from structure, please share it! \n- ‼ As the author understands it, the official KDL Schema can't be meaningfully\n  parsed as SiK. This seems _really_ unfortunate, and if it's possible to do so\n  without compromising SiK's design goals, it would be very nice to support.\n  Unfortunately, since the KDL Schema uses a meaningful split between node\n  values, arguments, and children (as is a good source of expressiveness in a\n  node-oriented language), this might require multiple special case mappings to\n  capture properly. Note, though, that the mapped Serde data model _does not_\n  have to match the derived de/serialization of a good Rust format for the\n  schema; we can use custom `De`/`Serialize` implementations in order to map\n  the desired Serde representaiton to the desired Rust data structure.\n- A _really cool_ potential feature I want to see more of is generating schema\n  from serde serialization and/or annotations where possible... 👀\n- If there are idioms you'd like to see more succinct extension mappings for,\n  PLEASE provide a PR! I want SiK to be as nice to work with as possible, no\n  matter how complex an implementation has to be to support the entire list of\n  extensions.\n\n  [KDL Argument]: https://github.com/kdl-org/kdl/blob/main/SPEC.md#argument\n  [KDL Document Language]: https://github.com/kdl-org/kdl\n  [KDL Boolean]: https://github.com/kdl-org/kdl/blob/main/SPEC.md#boolean\n  [KDL Children Block]: https://github.com/kdl-org/kdl/blob/main/SPEC.md#children-block\n  [KDL Identifier]: https://github.com/kdl-org/kdl/blob/main/SPEC.md#identifier\n  [KDL Node]: https://github.com/kdl-org/kdl/blob/main/SPEC.md#node\n  [KDL Null]: https://github.com/kdl-org/kdl/blob/main/SPEC.md#null\n  [KDL Number]: https://github.com/kdl-org/kdl/blob/main/SPEC.md#number\n  [KDL Property]: https://github.com/kdl-org/kdl/blob/main/SPEC.md#property\n  [KDL String]: https://github.com/kdl-org/kdl/blob/main/SPEC.md#string\n  [KDL Type Annotation]: https://github.com/kdl-org/kdl/blob/main/SPEC.md#type-annotation\n  [KDL Value]: https://github.com/kdl-org/kdl/blob/main/SPEC.md#value\n  [Serde data model]: https://serde.rs/data-model.html\n\n## Ideas for Extension?\n\nPlease either [make a new discussion](https://github.com/CAD97/serde-kdl/discussions/new) or\n[drop your idea in the mega discussion](https://github.com/CAD97/serde-kdl/discussions/1);\nwe'd love to hear it!\n\n## serde-kdl Versus SiK\n\nserde-kdl, as a result of being primarly a one-enby project, doesn't fully\nimplement the SiK spec with all of the extensions. As of 0.0.0 development,\nserde-kdl is incomplete and does not fully implement the base spec. For public\nrelease, serde-kdl is intended to SiK with the extensions of:\n\n- All alternative wrapper type serializations: `option` as `enum`,\n  `unit_*` as `tuple_*`, and `newtype_*` as `tuple_*`.\n- Value-value mapping via `struct { key; value; }` entries.\n- Implied root node.\n\n## License\n\nLicensed under either of\n\n * Apache License, Version 2.0\n   ([LICENSE/APACHE](LICENSE/APACHE) or http://www.apache.org/licenses/LICENSE-2.0)\n * MIT license\n   ([LICENSE/MIT](LICENSE/MIT) or http://opensource.org/licenses/MIT)\n\nat your option.\n\nIf you are a highly paid worker at any company that prioritises profit over\npeople, you can still use this crate. I simply wish you will unionise and push\nback against the obsession for growth, control, and power that is rampant in\nsuch workplaces. Please take a stand against the horrible working conditions\nthey inflict on your lesser paid colleagues, and more generally their\ndisrespect for the very human rights they typically claim to fight for.\n\n### Contribution\n\nUnless you explicitly state otherwise, any contribution intentionally submitted\nfor inclusion in the work by you, as defined in the Apache-2.0 license, shall be\ndual licensed as above, without any additional terms or conditions.\n\n-----\n\n[^1]: Yes, this causes ambiguities with nested `option`, but this is a\nconventional limitation of many serde data formats that you have to deal with\nanyway if you want to support serializing to arbitrary formats. For a general\nsolution, you can use a helper like [`serde(with = unwrap_or_skip)`] to enforce\n\"absent `none`, transparent `some`\", or [`serde(with = option_as_enum)`] to\nenforce it to serialize as a `None`/`Some` variant in the serde data model.\nFor a SiK specific solution, many implementations support the extension to\nserialize all `option` in your document as a plain `enum` instead.\n\n[^4]: This follows the expected behavior implemented in serde-json, and makes\nthe introduction of new nominal types (those distinguished solely by name, and\nnot by structure) completely transparent on the data side. If this is\nundesirable, you can use `serde(with)` to serialize as a tuple instead of as a\nnewtype, or enable an extension to apply this transformation globally.\n\n[^2]: This is possible in any data format as [`serde(with = map_as_tuple_list)`].\n\n[^3]: This extension is the most \"core\" of the extensions, and likely expected\nby anyone reading the KDL, as this removes a meaningless level of indentation\nand allows the common practice of multiple root fields for data configuration.\n\n[^5]: An implementation MUST support all of these MAYs; the MAY refers to the\nKDL document/data encoding, not the implementation.\n\n[^6]: \"Rightmost\" is a problematic specification in the face of non-LTR text.\nThis is an [upstream spec issue](https://github.com/kdl-org/kdl/issues/212).\nIt's reasonable to assume \"rightmost\" is intended to mean \"later in the input\ntext,\" rather than lexically located to the right.\n\n  [`serde(with = unwrap_or_skip)`]: \u003chttps://docs.rs/serde_with/1/serde_with/rust/unwrap_or_skip/index.html\u003e\n  [`serde(with = option_as_enum)`]: \u003chttps://github.com/jonasbb/serde_with/issues/365\u003e\n  [`serde(with = map_as_tuple_list)`]: \u003chttps://docs.rs/serde_with/1/serde_with/rust/map_as_tuple_list/index.html\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcad97%2Fserde-kdl","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcad97%2Fserde-kdl","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcad97%2Fserde-kdl/lists"}