Skip to main content

vcard/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 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, at the
8//! value-string level, [`escape`] and [`unescape`] apply and resolve the RFC
9//! 6350 3.4 value escapes, keyed by the [`mode`] `VcardEscaper`, and every value
10//! leaf the structural codecs touch runs through them. Content transfer
11//! encodings (`QUOTED-PRINTABLE`, `BASE64`) and `CHARSET` are never resolved
12//! here: the core transforms no content, leaving that to the opt-in feature
13//! helpers.
14//!
15//! The per-value-type projection is the [`VcardCodec`] trait, implemented once
16//! per value type under [`crate::tree::value`], mirroring the model's `value/`;
17//! both the structural dispatch and the per-property lenses go through it.
18
19use crate::{
20    tree::{codec::mode::VcardEscaper, value::node::VcardValueNode},
21    value::{VcardValue, VcardValueUnknown},
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`](VcardValueNode::escaper) carries the mode),
32/// `encode` writes it back, escaping every leaf with the given [`VcardEscaper`]
33/// and stamping it on the node. The escaper is symmetric across the two
34/// directions: decode reads it off the incoming node, encode receives the
35/// target mode and applies it (the decoded value itself is escaper-agnostic
36/// clean text).
37pub trait VcardCodec<'v>: Sized {
38    /// Decode the value from a syntax node.
39    fn decode(node: &'v VcardValueNode<'_>) -> Self;
40
41    /// Encode the value into a syntax node for the given escaping mode.
42    fn encode(&self, escaper: VcardEscaper) -> VcardValueNode<'static>;
43}
44
45impl<'v> VcardCodec<'v> for VcardValue<'v> {
46    /// Decode liberally as raw [`Unknown`](VcardValue::Unknown): no value kind
47    /// is known at this level (that is the spec's job), so the
48    /// version-divergent lenses whose target is `VcardValue` override the lens
49    /// `decode` to resolve the real kind; this fallback is what the others
50    /// inherit.
51    fn decode(node: &'v VcardValueNode<'_>) -> Self {
52        VcardValue::Unknown(VcardValueUnknown::decode(node))
53    }
54
55    /// Encode by dispatching to the held value's own codec.
56    fn encode(&self, escaper: VcardEscaper) -> VcardValueNode<'static> {
57        match self {
58            VcardValue::Text(v) => v.encode(escaper),
59            VcardValue::TextList(v) => v.encode(escaper),
60            VcardValue::Uri(v) => v.encode(escaper),
61            VcardValue::DateAndOrTime(v) => v.encode(escaper),
62            VcardValue::Timestamp(v) => v.encode(escaper),
63            VcardValue::LanguageTag(v) => v.encode(escaper),
64            VcardValue::UtcOffset(v) => v.encode(escaper),
65            VcardValue::N(v) => v.encode(escaper),
66            VcardValue::Adr(v) => v.encode(escaper),
67            VcardValue::Binary(v) => v.encode(escaper),
68            VcardValue::Gender(v) => v.encode(escaper),
69            VcardValue::Geo(v) => v.encode(escaper),
70            VcardValue::Org(v) => v.encode(escaper),
71            VcardValue::ClientPidMap(v) => v.encode(escaper),
72            VcardValue::Unknown(v) => v.encode(escaper),
73        }
74    }
75}