Skip to main content

ical/tree/
codec.rs

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