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}