Skip to main content

ical/tree/
codec.rs

1//! # Codec
2//!
3//! The bytes-to-model bridge, in both directions and at both levels. It is the
4//! only part of [`crate::tree`] that consults the card version.
5//!
6//! [`decode`] projects a raw syntax tree onto the decoded model and [`encode`]
7//! projects it back; that is the structural level. Underneath, the value-string
8//! level: [`escape`] and [`unescape`] apply and resolve the RFC 5545 3.3.11
9//! value escapes (keyed by the [`mode`] `Escaper`). The structural encoders and
10//! decoders run every value leaf through those. Content transfer encodings
11//! (`QUOTED-PRINTABLE`, `BASE64`) and `CHARSET` are never resolved here: the
12//! core transforms no content, leaving that to the opt-in feature helpers.
13//!
14//! The per-value-type projection is the [`Codec`] trait. One impl per value
15//! type lives under [`crate::tree::value`], mirroring the model's `value/`, so
16//! each value's codec is written exactly once; both the structural dispatch and
17//! the per-property lenses go through it.
18
19use crate::{
20    tree::{codec::mode::Escaper, value::IcalValueNode},
21    value::{IcalUnknownValue, IcalValue},
22};
23
24pub mod decode;
25pub mod encode;
26pub mod escape;
27pub mod mode;
28pub mod unescape;
29
30/// How a decoded value type projects to and from a syntax node: `decode` reads
31/// it from a node (its [`escaper`](IcalValueNode::escaper) carries the mode),
32/// `encode` writes it back, escaping every leaf with the given [`Escaper`] and
33/// stamping it on the node. The escaper is symmetric across the two directions:
34/// decode reads it off the incoming node, encode receives the target mode and
35/// applies it (the decoded value itself is escaper-agnostic clean text).
36pub trait Codec<'v>: Sized {
37    /// Decode the value from a syntax node.
38    fn decode(node: &'v IcalValueNode<'_>) -> Self;
39
40    /// Encode the value into a syntax node for the given escaping mode.
41    fn encode(&self, escaper: Escaper) -> IcalValueNode<'static>;
42}
43
44impl<'v> Codec<'v> for IcalValue<'v> {
45    /// Decode liberally as raw [`Unknown`](IcalValue::Unknown): no value kind
46    /// is known at this level (that is the spec's job), so the
47    /// version-divergent lenses whose target is `IcalValue` override the lens
48    /// `decode` to resolve the real kind; this fallback is what the others
49    /// inherit.
50    fn decode(node: &'v IcalValueNode<'_>) -> Self {
51        IcalValue::Unknown(IcalUnknownValue::decode(node))
52    }
53
54    /// Encode by dispatching to the held value's own codec.
55    fn encode(&self, escaper: Escaper) -> IcalValueNode<'static> {
56        match self {
57            IcalValue::Binary(v) => v.encode(escaper),
58            IcalValue::Boolean(v) => v.encode(escaper),
59            IcalValue::CalAddress(v) => v.encode(escaper),
60            IcalValue::Date(v) => v.encode(escaper),
61            IcalValue::DateTime(v) => v.encode(escaper),
62            IcalValue::DateTimeList(v) => v.encode(escaper),
63            IcalValue::Duration(v) => v.encode(escaper),
64            IcalValue::Float(v) => v.encode(escaper),
65            IcalValue::Geo(v) => v.encode(escaper),
66            IcalValue::Integer(v) => v.encode(escaper),
67            IcalValue::Period(v) => v.encode(escaper),
68            IcalValue::Recur(v) => v.encode(escaper),
69            IcalValue::RequestStatus(v) => v.encode(escaper),
70            IcalValue::Text(v) => v.encode(escaper),
71            IcalValue::TextList(v) => v.encode(escaper),
72            IcalValue::Time(v) => v.encode(escaper),
73            IcalValue::Uri(v) => v.encode(escaper),
74            IcalValue::UtcOffset(v) => v.encode(escaper),
75            IcalValue::Unknown(v) => v.encode(escaper),
76        }
77    }
78}