Skip to main content

meta_language/
rust_codec.rs

1//! Rust value and type-shape codec for [`LinkNetwork`].
2//!
3//! The codec uses explicit `ToLinks` / `FromLinks` traits plus the
4//! `LinksObject` helper trait for user structs. This is intentionally smaller
5//! than a proc-macro crate and avoids adding a serde serializer contract before
6//! the links representation has stabilized. User types opt in with ordinary
7//! trait impls, while primitives, `Option`, `Vec`, `BTreeMap`, and
8//! `Rc<RefCell<T>>` get reusable implementations.
9
10use std::any::{Any, TypeId};
11use std::cell::RefCell;
12use std::collections::{BTreeMap, BTreeSet};
13use std::error::Error;
14use std::fmt;
15use std::rc::Rc;
16use std::str::FromStr;
17use std::sync::Arc;
18
19use crate::link_network::{Link, LinkId, LinkMetadata, LinkNetwork, LinkType};
20
21const TYPE_TERM_PREFIX: &str = "rust::type::";
22const LITERAL_TERM_PREFIX: &str = "rust::literal::";
23
24/// High-level Rust type declaration kind represented as links.
25#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
26pub enum RustTypeKind {
27    Primitive,
28    Struct,
29    Enum,
30    Trait,
31    Sequence,
32    Option,
33    Map,
34}
35
36impl RustTypeKind {
37    const fn as_str(self) -> &'static str {
38        match self {
39            Self::Primitive => "primitive",
40            Self::Struct => "struct",
41            Self::Enum => "enum",
42            Self::Trait => "trait",
43            Self::Sequence => "sequence",
44            Self::Option => "option",
45            Self::Map => "map",
46        }
47    }
48}
49
50impl fmt::Display for RustTypeKind {
51    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
52        formatter.write_str(self.as_str())
53    }
54}
55
56/// One named field, variant, or trait item in a Rust type shape.
57#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
58pub struct RustFieldShape {
59    name: String,
60    type_name: String,
61}
62
63impl RustFieldShape {
64    /// Creates a field shape with a Rust field name and display type name.
65    #[must_use]
66    pub fn new(name: impl Into<String>, type_name: impl Into<String>) -> Self {
67        Self {
68            name: name.into(),
69            type_name: type_name.into(),
70        }
71    }
72
73    /// Field, variant, or trait-item name.
74    #[must_use]
75    pub fn name(&self) -> &str {
76        &self.name
77    }
78
79    /// Display type name for the field value.
80    #[must_use]
81    pub fn type_name(&self) -> &str {
82        &self.type_name
83    }
84}
85
86/// Queryable Rust type declaration shape.
87#[derive(Clone, Debug, PartialEq, Eq)]
88pub struct RustTypeShape {
89    name: String,
90    kind: RustTypeKind,
91    fields: Vec<RustFieldShape>,
92}
93
94impl RustTypeShape {
95    /// Creates a primitive type shape.
96    #[must_use]
97    pub fn primitive(name: impl Into<String>) -> Self {
98        Self {
99            name: name.into(),
100            kind: RustTypeKind::Primitive,
101            fields: Vec::new(),
102        }
103    }
104
105    /// Creates a struct type shape.
106    #[must_use]
107    pub fn structure<I>(name: impl Into<String>, fields: I) -> Self
108    where
109        I: IntoIterator<Item = RustFieldShape>,
110    {
111        Self {
112            name: name.into(),
113            kind: RustTypeKind::Struct,
114            fields: fields.into_iter().collect(),
115        }
116    }
117
118    /// Creates an enum type shape, using field entries as variants.
119    #[must_use]
120    pub fn enumeration<I>(name: impl Into<String>, variants: I) -> Self
121    where
122        I: IntoIterator<Item = RustFieldShape>,
123    {
124        Self {
125            name: name.into(),
126            kind: RustTypeKind::Enum,
127            fields: variants.into_iter().collect(),
128        }
129    }
130
131    /// Creates a trait type shape, using field entries as required items.
132    #[must_use]
133    pub fn trait_type<I>(name: impl Into<String>, items: I) -> Self
134    where
135        I: IntoIterator<Item = RustFieldShape>,
136    {
137        Self {
138            name: name.into(),
139            kind: RustTypeKind::Trait,
140            fields: items.into_iter().collect(),
141        }
142    }
143
144    /// Creates a sequence container type shape.
145    #[must_use]
146    pub fn sequence(name: impl Into<String>, element_type: impl Into<String>) -> Self {
147        Self {
148            name: name.into(),
149            kind: RustTypeKind::Sequence,
150            fields: vec![RustFieldShape::new("item", element_type)],
151        }
152    }
153
154    /// Creates an optional-value container type shape.
155    #[must_use]
156    pub fn option(name: impl Into<String>, value_type: impl Into<String>) -> Self {
157        Self {
158            name: name.into(),
159            kind: RustTypeKind::Option,
160            fields: vec![
161                RustFieldShape::new("Some", value_type),
162                RustFieldShape::new("None", "()"),
163            ],
164        }
165    }
166
167    /// Creates a map container type shape.
168    #[must_use]
169    pub fn map(
170        name: impl Into<String>,
171        key_type: impl Into<String>,
172        value_type: impl Into<String>,
173    ) -> Self {
174        Self {
175            name: name.into(),
176            kind: RustTypeKind::Map,
177            fields: vec![
178                RustFieldShape::new("entry", "BTreeMapEntry"),
179                RustFieldShape::new("key", key_type),
180                RustFieldShape::new("value", value_type),
181            ],
182        }
183    }
184
185    /// Rust display name for this type.
186    #[must_use]
187    pub fn name(&self) -> &str {
188        &self.name
189    }
190
191    /// Type declaration kind.
192    #[must_use]
193    pub const fn kind(&self) -> RustTypeKind {
194        self.kind
195    }
196
197    /// Declared fields, variants, or trait items.
198    #[must_use]
199    pub fn fields(&self) -> &[RustFieldShape] {
200        &self.fields
201    }
202}
203
204/// Error returned while decoding Rust values from links.
205#[derive(Clone, Debug, PartialEq, Eq)]
206pub enum LinksCodecError {
207    /// A referenced link id is absent from the network.
208    MissingLink(LinkId),
209    /// The link exists but is not an object instance.
210    ExpectedObject { link: LinkId },
211    /// An object instance does not carry the expected self/type references.
212    MalformedObject { object: LinkId, reason: String },
213    /// An object type did not match the requested Rust type.
214    UnexpectedType {
215        object: LinkId,
216        expected: String,
217        actual: Option<String>,
218    },
219    /// A required field is missing.
220    MissingField { object: LinkId, field: String },
221    /// A field expected to be singular has multiple values.
222    DuplicateField { object: LinkId, field: String },
223    /// A field relation is structurally invalid.
224    MalformedField { field: LinkId, reason: String },
225    /// A literal link is missing or malformed.
226    InvalidLiteral {
227        object: LinkId,
228        type_name: String,
229        value: Option<String>,
230        reason: String,
231    },
232    /// A cached object had an unexpected Rust type.
233    CacheTypeMismatch { type_name: String },
234}
235
236impl fmt::Display for LinksCodecError {
237    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
238        match self {
239            Self::MissingLink(link) => write!(formatter, "missing link {link}"),
240            Self::ExpectedObject { link } => write!(formatter, "link {link} is not an object"),
241            Self::MalformedObject { object, reason } => {
242                write!(formatter, "object {object} is malformed: {reason}")
243            }
244            Self::UnexpectedType {
245                object,
246                expected,
247                actual,
248            } => write!(
249                formatter,
250                "object {object} has type {:?}, expected {expected:?}",
251                actual.as_deref()
252            ),
253            Self::MissingField { object, field } => {
254                write!(formatter, "object {object} is missing field {field:?}")
255            }
256            Self::DuplicateField { object, field } => {
257                write!(formatter, "object {object} has multiple {field:?} fields")
258            }
259            Self::MalformedField { field, reason } => {
260                write!(formatter, "field link {field} is malformed: {reason}")
261            }
262            Self::InvalidLiteral {
263                object,
264                type_name,
265                value,
266                reason,
267            } => write!(
268                formatter,
269                "object {object} has invalid {type_name} literal {:?}: {reason}",
270                value.as_deref()
271            ),
272            Self::CacheTypeMismatch { type_name } => {
273                write!(
274                    formatter,
275                    "cached object was not an Rc<RefCell<{type_name}>>"
276                )
277            }
278        }
279    }
280}
281
282impl Error for LinksCodecError {}
283
284/// Encodes a Rust value into links.
285pub trait ToLinks {
286    /// Inserts links for this value and returns the root link id.
287    fn to_links(&self, encoder: &mut LinksEncoder) -> LinkId;
288}
289
290/// Decodes a Rust value from links.
291pub trait FromLinks: Sized {
292    /// Reconstructs this value from a root link id.
293    fn from_links(decoder: &mut LinksDecoder<'_>, link: LinkId) -> Result<Self, LinksCodecError>;
294}
295
296/// Helper trait for user-defined object types that need identity preservation.
297///
298/// Implement this for structs that may be shared or cyclic through
299/// `Rc<RefCell<T>>`. `decode_fields` mutates an already-allocated default
300/// value, which lets the decoder register an identity before recursively
301/// decoding fields that may point back to the same object.
302pub trait LinksObject: Default + 'static {
303    /// Stable Rust type name stored in the type-shape link.
304    const TYPE_NAME: &'static str;
305
306    /// Queryable type declaration shape.
307    fn type_shape() -> RustTypeShape;
308
309    /// Encodes object fields after an identity link has been allocated.
310    fn encode_fields(&self, encoder: &mut LinksEncoder, object: LinkId);
311
312    /// Decodes fields into an already-allocated object.
313    fn decode_fields(
314        &mut self,
315        decoder: &mut LinksDecoder<'_>,
316        object: LinkId,
317    ) -> Result<(), LinksCodecError>;
318}
319
320impl LinkNetwork {
321    /// Inserts an anonymous object identity linked to its Rust/meta type.
322    ///
323    /// The resulting object references itself first, preserving a stable
324    /// identity link for shared and circular object graphs, and references the
325    /// type shape second so the object's declaration remains queryable.
326    pub fn insert_object_instance(&mut self, type_id: LinkId) -> LinkId {
327        let id = LinkId(self.next_id);
328        self.next_id += 1;
329        self.links.insert(
330            id,
331            Arc::new(Link {
332                id,
333                references: Arc::from(vec![id, type_id]),
334                metadata: LinkMetadata::new().with_link_type(LinkType::Object),
335            }),
336        );
337        id
338    }
339}
340
341/// Stateful Rust value encoder.
342#[derive(Debug)]
343pub struct LinksEncoder {
344    network: LinkNetwork,
345    object_cache: BTreeMap<usize, LinkId>,
346    registered_shapes: BTreeSet<String>,
347}
348
349impl Default for LinksEncoder {
350    fn default() -> Self {
351        Self::new()
352    }
353}
354
355impl LinksEncoder {
356    /// Creates an encoder with a self-describing network.
357    #[must_use]
358    pub fn new() -> Self {
359        Self {
360            network: LinkNetwork::self_describing(),
361            object_cache: BTreeMap::new(),
362            registered_shapes: BTreeSet::new(),
363        }
364    }
365
366    /// Creates an encoder that appends to an existing network.
367    #[must_use]
368    pub const fn with_network(network: LinkNetwork) -> Self {
369        Self {
370            network,
371            object_cache: BTreeMap::new(),
372            registered_shapes: BTreeSet::new(),
373        }
374    }
375
376    /// Returns the underlying network.
377    #[must_use]
378    pub const fn network(&self) -> &LinkNetwork {
379        &self.network
380    }
381
382    /// Returns the underlying network mutably.
383    pub fn network_mut(&mut self) -> &mut LinkNetwork {
384        &mut self.network
385    }
386
387    /// Finishes encoding and returns the network.
388    #[must_use]
389    pub fn into_network(self) -> LinkNetwork {
390        self.network
391    }
392
393    /// Encodes a value and returns its root link.
394    pub fn encode<T: ToLinks + ?Sized>(&mut self, value: &T) -> LinkId {
395        value.to_links(self)
396    }
397
398    /// Registers a queryable Rust type shape and returns its type link.
399    pub fn register_type_shape(&mut self, shape: &RustTypeShape) -> LinkId {
400        let shape_type_term = type_term(shape.name());
401        let definition = format!("Rust {} type {}", shape.kind(), shape.name());
402        let type_id =
403            self.network
404                .insert_typed_point(&shape_type_term, LinkType::Type, Some(&definition));
405
406        if !self.registered_shapes.insert(shape.name().to_string()) {
407            return type_id;
408        }
409
410        let kind = self.network.insert_typed_point(
411            &format!("rust::type-kind::{}", shape.kind()),
412            LinkType::Concept,
413            None,
414        );
415        self.network.insert_dynamic_link(
416            &[type_id, kind],
417            LinkMetadata::new().with_link_type(LinkType::Relation),
418        );
419
420        for field in shape.fields() {
421            let field_type_id = self.network.insert_typed_point(
422                &type_term(field.type_name()),
423                LinkType::Type,
424                Some(&format!("Rust referenced type {}", field.type_name())),
425            );
426            self.network
427                .insert_field(type_id, field.name(), field_type_id);
428        }
429
430        type_id
431    }
432
433    /// Starts an object instance for a Rust type shape.
434    pub fn begin_object(&mut self, shape: &RustTypeShape) -> LinkId {
435        let type_id = self.register_type_shape(shape);
436        self.network.insert_object_instance(type_id)
437    }
438
439    /// Encodes a named field relation from an object to a child value.
440    pub fn encode_field<T: ToLinks + ?Sized>(
441        &mut self,
442        object: LinkId,
443        name: &str,
444        value: &T,
445    ) -> LinkId {
446        let value = value.to_links(self);
447        self.network.insert_field(object, name, value)
448    }
449
450    fn encode_literal(&mut self, type_name: &str, value: &str) -> LinkId {
451        let object = self.begin_object(&RustTypeShape::primitive(type_name));
452        let literal = self.network.insert_typed_point(
453            &literal_term(type_name, value),
454            LinkType::Concept,
455            None,
456        );
457        self.network.insert_field(object, "value", literal);
458        object
459    }
460
461    fn encode_rc<T: LinksObject>(&mut self, value: &Rc<RefCell<T>>) -> LinkId {
462        let key = Rc::as_ptr(value).cast::<()>() as usize;
463        if let Some(existing) = self.object_cache.get(&key) {
464            return *existing;
465        }
466
467        let object = self.begin_object(&T::type_shape());
468        self.object_cache.insert(key, object);
469        value.borrow().encode_fields(self, object);
470        object
471    }
472}
473
474/// Stateful Rust value decoder.
475#[derive(Debug)]
476pub struct LinksDecoder<'network> {
477    network: &'network LinkNetwork,
478    object_cache: BTreeMap<(TypeId, LinkId), Box<dyn Any>>,
479}
480
481impl<'network> LinksDecoder<'network> {
482    /// Creates a decoder over an existing network.
483    #[must_use]
484    pub fn new(network: &'network LinkNetwork) -> Self {
485        Self {
486            network,
487            object_cache: BTreeMap::new(),
488        }
489    }
490
491    /// Returns the decoded network.
492    #[must_use]
493    pub const fn network(&self) -> &'network LinkNetwork {
494        self.network
495    }
496
497    /// Decodes a value from a root link.
498    pub fn decode<T: FromLinks>(&mut self, link: LinkId) -> Result<T, LinksCodecError> {
499        T::from_links(self, link)
500    }
501
502    /// Decodes a singular named field from an object.
503    pub fn decode_field<T: FromLinks>(
504        &mut self,
505        object: LinkId,
506        field: &str,
507    ) -> Result<T, LinksCodecError> {
508        let value = self.field_value(object, field)?;
509        T::from_links(self, value)
510    }
511
512    fn decode_field_values<T: FromLinks>(
513        &mut self,
514        object: LinkId,
515        field: &str,
516    ) -> Result<Vec<T>, LinksCodecError> {
517        let values = self.field_values(object, field)?;
518        values
519            .into_iter()
520            .map(|value| T::from_links(self, value))
521            .collect()
522    }
523
524    fn decode_rc<T: LinksObject>(
525        &mut self,
526        object: LinkId,
527    ) -> Result<Rc<RefCell<T>>, LinksCodecError> {
528        let key = (TypeId::of::<T>(), object);
529        if let Some(cached) = self.object_cache.get(&key) {
530            let typed = cached.downcast_ref::<Rc<RefCell<T>>>().ok_or_else(|| {
531                LinksCodecError::CacheTypeMismatch {
532                    type_name: T::TYPE_NAME.to_string(),
533                }
534            })?;
535            return Ok(Rc::clone(typed));
536        }
537
538        self.expect_object_type(object, T::TYPE_NAME)?;
539        let decoded = Rc::new(RefCell::new(T::default()));
540        self.object_cache.insert(key, Box::new(Rc::clone(&decoded)));
541        decoded.borrow_mut().decode_fields(self, object)?;
542        Ok(decoded)
543    }
544
545    fn expect_object_type(&self, object: LinkId, expected: &str) -> Result<(), LinksCodecError> {
546        let actual = self.object_type_name(object)?;
547        if actual.as_deref() == Some(expected) {
548            return Ok(());
549        }
550
551        Err(LinksCodecError::UnexpectedType {
552            object,
553            expected: expected.to_string(),
554            actual,
555        })
556    }
557
558    fn object_type_name(&self, object: LinkId) -> Result<Option<String>, LinksCodecError> {
559        let link = self
560            .network
561            .link(object)
562            .ok_or(LinksCodecError::MissingLink(object))?;
563        if link.metadata().link_type() != Some(LinkType::Object) {
564            return Err(LinksCodecError::ExpectedObject { link: object });
565        }
566        let references = link.references();
567        if references.first() != Some(&object) {
568            return Err(LinksCodecError::MalformedObject {
569                object,
570                reason: "first reference must preserve object identity".to_string(),
571            });
572        }
573        let type_id = references
574            .get(1)
575            .ok_or_else(|| LinksCodecError::MalformedObject {
576                object,
577                reason: "second reference must point to a type shape".to_string(),
578            })?;
579        let type_link = self
580            .network
581            .link(*type_id)
582            .ok_or(LinksCodecError::MissingLink(*type_id))?;
583        Ok(type_link
584            .metadata()
585            .term()
586            .and_then(|term| term.strip_prefix(TYPE_TERM_PREFIX))
587            .map(ToString::to_string))
588    }
589
590    fn field_value(&self, object: LinkId, field: &str) -> Result<LinkId, LinksCodecError> {
591        let values = self.field_values(object, field)?;
592        match values.as_slice() {
593            [value] => Ok(*value),
594            [] => Err(LinksCodecError::MissingField {
595                object,
596                field: field.to_string(),
597            }),
598            _ => Err(LinksCodecError::DuplicateField {
599                object,
600                field: field.to_string(),
601            }),
602        }
603    }
604
605    fn field_values(&self, object: LinkId, field: &str) -> Result<Vec<LinkId>, LinksCodecError> {
606        if self.network.link(object).is_none() {
607            return Err(LinksCodecError::MissingLink(object));
608        }
609
610        let mut values = Vec::new();
611        for link in self
612            .network
613            .links()
614            .filter(|link| link.metadata().link_type() == Some(LinkType::Field))
615        {
616            let references = link.references();
617            if references.first() != Some(&object) {
618                continue;
619            }
620            let [_, label, value] = references else {
621                return Err(LinksCodecError::MalformedField {
622                    field: link.id(),
623                    reason: "field links must have parent, label, and child references".to_string(),
624                });
625            };
626            let Some(label_term) = self
627                .network
628                .link(*label)
629                .ok_or(LinksCodecError::MissingLink(*label))?
630                .metadata()
631                .term()
632            else {
633                continue;
634            };
635            if label_term == field {
636                values.push(*value);
637            }
638        }
639        Ok(values)
640    }
641
642    fn decode_literal(&self, object: LinkId, type_name: &str) -> Result<String, LinksCodecError> {
643        self.expect_object_type(object, type_name)?;
644        let literal = self.field_value(object, "value")?;
645        let term = self
646            .network
647            .link(literal)
648            .ok_or(LinksCodecError::MissingLink(literal))?
649            .metadata()
650            .term()
651            .map(ToString::to_string);
652        let Some(term) = term else {
653            return Err(LinksCodecError::InvalidLiteral {
654                object,
655                type_name: type_name.to_string(),
656                value: None,
657                reason: "literal link has no term".to_string(),
658            });
659        };
660        let prefix = literal_prefix(type_name);
661        term.strip_prefix(&prefix)
662            .map(ToString::to_string)
663            .ok_or_else(|| LinksCodecError::InvalidLiteral {
664                object,
665                type_name: type_name.to_string(),
666                value: Some(term),
667                reason: "literal term has the wrong prefix".to_string(),
668            })
669    }
670}
671
672impl<T: LinksObject> ToLinks for Rc<RefCell<T>> {
673    fn to_links(&self, encoder: &mut LinksEncoder) -> LinkId {
674        encoder.encode_rc(self)
675    }
676}
677
678impl<T: LinksObject> FromLinks for Rc<RefCell<T>> {
679    fn from_links(decoder: &mut LinksDecoder<'_>, link: LinkId) -> Result<Self, LinksCodecError> {
680        decoder.decode_rc(link)
681    }
682}
683
684impl<T: ToLinks> ToLinks for Option<T> {
685    fn to_links(&self, encoder: &mut LinksEncoder) -> LinkId {
686        let object = encoder.begin_object(&RustTypeShape::option("Option", "T"));
687        match self {
688            Some(value) => {
689                encoder.encode_field(object, "Some", value);
690            }
691            None => {
692                encoder.encode_field(object, "None", &());
693            }
694        }
695        object
696    }
697}
698
699impl<T: FromLinks> FromLinks for Option<T> {
700    fn from_links(decoder: &mut LinksDecoder<'_>, link: LinkId) -> Result<Self, LinksCodecError> {
701        decoder.expect_object_type(link, "Option")?;
702        let some = decoder.field_values(link, "Some")?;
703        let none = decoder.field_values(link, "None")?;
704        match (some.as_slice(), none.as_slice()) {
705            ([value], []) => T::from_links(decoder, *value).map(Some),
706            ([], [_]) => Ok(None),
707            ([], []) => Err(LinksCodecError::MissingField {
708                object: link,
709                field: "Some/None".to_string(),
710            }),
711            _ => Err(LinksCodecError::MalformedObject {
712                object: link,
713                reason: "Option must contain exactly one Some or None variant".to_string(),
714            }),
715        }
716    }
717}
718
719impl<T: ToLinks> ToLinks for Vec<T> {
720    fn to_links(&self, encoder: &mut LinksEncoder) -> LinkId {
721        let object = encoder.begin_object(&RustTypeShape::sequence("Vec", "T"));
722        for item in self {
723            encoder.encode_field(object, "item", item);
724        }
725        object
726    }
727}
728
729impl<T: FromLinks> FromLinks for Vec<T> {
730    fn from_links(decoder: &mut LinksDecoder<'_>, link: LinkId) -> Result<Self, LinksCodecError> {
731        decoder.expect_object_type(link, "Vec")?;
732        decoder.decode_field_values(link, "item")
733    }
734}
735
736impl<K, V> ToLinks for BTreeMap<K, V>
737where
738    K: ToLinks,
739    V: ToLinks,
740{
741    fn to_links(&self, encoder: &mut LinksEncoder) -> LinkId {
742        let object = encoder.begin_object(&RustTypeShape::map("BTreeMap", "K", "V"));
743        for (key, value) in self {
744            let entry = encoder.begin_object(&RustTypeShape::structure(
745                "BTreeMapEntry",
746                [
747                    RustFieldShape::new("key", "K"),
748                    RustFieldShape::new("value", "V"),
749                ],
750            ));
751            encoder.encode_field(entry, "key", key);
752            encoder.encode_field(entry, "value", value);
753            encoder.network.insert_field(object, "entry", entry);
754        }
755        object
756    }
757}
758
759impl<K, V> FromLinks for BTreeMap<K, V>
760where
761    K: FromLinks + Ord,
762    V: FromLinks,
763{
764    fn from_links(decoder: &mut LinksDecoder<'_>, link: LinkId) -> Result<Self, LinksCodecError> {
765        decoder.expect_object_type(link, "BTreeMap")?;
766        let mut map = Self::new();
767        for entry in decoder.field_values(link, "entry")? {
768            decoder.expect_object_type(entry, "BTreeMapEntry")?;
769            let key = decoder.decode_field(entry, "key")?;
770            let value = decoder.decode_field(entry, "value")?;
771            map.insert(key, value);
772        }
773        Ok(map)
774    }
775}
776
777impl ToLinks for () {
778    fn to_links(&self, encoder: &mut LinksEncoder) -> LinkId {
779        encoder.encode_literal("()", "unit")
780    }
781}
782
783impl FromLinks for () {
784    fn from_links(decoder: &mut LinksDecoder<'_>, link: LinkId) -> Result<Self, LinksCodecError> {
785        let _ = decoder.decode_literal(link, "()")?;
786        Ok(())
787    }
788}
789
790impl ToLinks for String {
791    fn to_links(&self, encoder: &mut LinksEncoder) -> LinkId {
792        encoder.encode_literal("String", self)
793    }
794}
795
796impl ToLinks for str {
797    fn to_links(&self, encoder: &mut LinksEncoder) -> LinkId {
798        encoder.encode_literal("String", self)
799    }
800}
801
802impl FromLinks for String {
803    fn from_links(decoder: &mut LinksDecoder<'_>, link: LinkId) -> Result<Self, LinksCodecError> {
804        decoder.decode_literal(link, "String")
805    }
806}
807
808impl ToLinks for char {
809    fn to_links(&self, encoder: &mut LinksEncoder) -> LinkId {
810        encoder.encode_literal("char", &self.to_string())
811    }
812}
813
814impl FromLinks for char {
815    fn from_links(decoder: &mut LinksDecoder<'_>, link: LinkId) -> Result<Self, LinksCodecError> {
816        let value = decoder.decode_literal(link, "char")?;
817        let mut chars = value.chars();
818        let Some(character) = chars.next() else {
819            return Err(invalid_literal(
820                link,
821                "char",
822                Some(value),
823                "literal is empty",
824            ));
825        };
826        if chars.next().is_some() {
827            return Err(invalid_literal(
828                link,
829                "char",
830                Some(value),
831                "literal contains more than one character",
832            ));
833        }
834        Ok(character)
835    }
836}
837
838macro_rules! impl_fromstr_literal_codec {
839    ($type:ty, $name:literal) => {
840        impl ToLinks for $type {
841            fn to_links(&self, encoder: &mut LinksEncoder) -> LinkId {
842                encoder.encode_literal($name, &self.to_string())
843            }
844        }
845
846        impl FromLinks for $type {
847            fn from_links(
848                decoder: &mut LinksDecoder<'_>,
849                link: LinkId,
850            ) -> Result<Self, LinksCodecError> {
851                let value = decoder.decode_literal(link, $name)?;
852                <$type>::from_str(&value)
853                    .map_err(|error| invalid_literal(link, $name, Some(value), &error.to_string()))
854            }
855        }
856    };
857}
858
859impl_fromstr_literal_codec!(bool, "bool");
860impl_fromstr_literal_codec!(i8, "i8");
861impl_fromstr_literal_codec!(i16, "i16");
862impl_fromstr_literal_codec!(i32, "i32");
863impl_fromstr_literal_codec!(i64, "i64");
864impl_fromstr_literal_codec!(i128, "i128");
865impl_fromstr_literal_codec!(isize, "isize");
866impl_fromstr_literal_codec!(u8, "u8");
867impl_fromstr_literal_codec!(u16, "u16");
868impl_fromstr_literal_codec!(u32, "u32");
869impl_fromstr_literal_codec!(u64, "u64");
870impl_fromstr_literal_codec!(u128, "u128");
871impl_fromstr_literal_codec!(usize, "usize");
872impl_fromstr_literal_codec!(f32, "f32");
873impl_fromstr_literal_codec!(f64, "f64");
874
875fn type_term(name: &str) -> String {
876    format!("{TYPE_TERM_PREFIX}{name}")
877}
878
879fn literal_prefix(type_name: &str) -> String {
880    format!("{LITERAL_TERM_PREFIX}{type_name}:")
881}
882
883fn literal_term(type_name: &str, value: &str) -> String {
884    format!("{}{value}", literal_prefix(type_name))
885}
886
887fn invalid_literal(
888    object: LinkId,
889    type_name: &str,
890    value: Option<String>,
891    reason: &str,
892) -> LinksCodecError {
893    LinksCodecError::InvalidLiteral {
894        object,
895        type_name: type_name.to_string(),
896        value,
897        reason: reason.to_string(),
898    }
899}