1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
//! # vcard-rs
//!
//! A single, version-agnostic vCard library: one decoded model and one
//! byte-faithful syntax tree that read and write vCard 2.1 (versitcard), 3.0
//! (RFC 2426) and 4.0 (RFC 6350) alike. The card version is a decoded
//! indicator, never a type parameter or a separate dialect: the syntax tree
//! ignores it, and only the codec and the per-property spec branch on it where
//! escaping or a value's shape genuinely differ. The crate is `no_std` (with
//! `alloc`); its core is dependency-free, and the only dependencies are the
//! small `no_std` crates behind the opt-in content-decoding features
//! ([Cargo features](#cargo-features)).
//!
//! ## Example
//!
//! Parse raw bytes, read a property through its typed lens, edit it in place,
//! and serialize back; every untouched byte is preserved.
//!
//! ```rust
//! use vcard::tree::cst::VcardCst;
//! use vcard::tree::prop::r#fn::FN;
//!
//! let mut card =
//! VcardCst::parse("BEGIN:VCARD\r\nVERSION:4.0\r\nFN:John Doe\r\nEND:VCARD\r\n").unwrap();
//!
//! assert_eq!(&*card.prop::<FN>().unwrap().0, "John Doe");
//!
//! card.prop_mut::<FN>().unwrap().set_text("Jane Doe");
//! assert_eq!(
//! card.to_string(),
//! "BEGIN:VCARD\r\nVERSION:4.0\r\nFN:Jane Doe\r\nEND:VCARD\r\n",
//! );
//! ```
//!
//! ## Postel's law
//!
//! The library is liberal in what it accepts and strict in what it sends.
//! Parsing is maximally liberal: any real card, including properties,
//! parameters and value types that no version officially defines, is accepted
//! and round-trips byte for byte. The decoded model keeps that openness, with
//! an `Unknown` arm on every open vocabulary. Strictness lives only on the way
//! out, as two runtime steps: the builder, which refuses to construct a
//! property the spec forbids, and [`validate`](tree::vcard::validate), which
//! checks a decoded card against its version's RFC contract.
//!
//! ## The two layers
//!
//! The decoded model ([`vcard`], [`version`], [`prop`], [`param`], [`value`])
//! is pure data with no dependency on the syntax side, so it can be depended on
//! alone. Property names, parameter names and value types are closed identity
//! enums ([`VcardPropKind`](prop::VcardPropKind),
//! [`VcardParamKind`](param::VcardParamKind),
//! [`VcardValueKind`](value::VcardValueKind)) whose wire spelling is reached
//! through `FromStr` and `Deref`. A property is a
//! [`VcardProp`](prop::VcardProp) struct of a name, parameters and one value;
//! its parameters and value are open payload enums
//! ([`VcardParam`](param::VcardParam), [`VcardValue`](value::VcardValue)) with
//! an `Unknown` variant, so anything outside the model survives.
//!
//! The syntax tree ([`tree`], gated behind the `parser` feature, on by default)
//! is everything byte-faithful. Its hub is [`VcardCst`](tree::cst::VcardCst), a
//! tree of generic nodes that reproduces the wire bytes exactly.
//! [`parse`](tree::cst::VcardCst::parse) accepts bytes or a string and reads
//! one card (or a bare RFC 2425 record with no `BEGIN`/`END`);
//! [`parse_many`](tree::cst::VcardCst::parse_many) iterates a multi-card file.
//! [`decode`](tree::codec::decode) projects a CST onto the decoded
//! [`Vcard`](vcard::Vcard); [`encode`](tree::codec::encode) (and `From<Vcard>`)
//! projects the model back to a canonical CST. Per-property lens markers
//! ([`VcardPropLens`](tree::prop::VcardPropLens)) read or edit a single line
//! through the byte-preserving [`cursor`](tree::value::VcardValueCursor)s, so
//! editing one property leaves every other byte intact. The three-way
//! [`merge`](tree::merge::merge) builds on those same edits to reconcile two
//! divergent copies of a card against their common base, reporting each side's
//! changes and their conflicts.
//!
//! A property *value* is held as raw bytes, so a value in a foreign charset (a
//! vCard 2.1 `CHARSET`) survives byte for byte; a name or parameter must be
//! UTF-8, as every version's grammar guarantees. Because of that,
//! [`to_bytes`](tree::cst::VcardCst::to_bytes) is the byte-faithful serializer,
//! while [`Display`](core::fmt::Display) / `to_string` are a convenience that
//! is lossy only for a non-UTF-8 value.
//!
//! ## The spec layer
//!
//! Each property carries a [`VcardPropSpec`](tree::prop::VcardPropSpec) on its
//! lens marker: the versions it lives in, its cardinality, the value types and
//! parameters it may take per version, and the value type in force given a
//! declared `VALUE`. A single vtable dispatch bridges the open
//! [`VcardPropKind`](prop::VcardPropKind) back to those static specs, so the
//! decoder consults it to pick a value kind,
//! [`validate`](tree::vcard::validate) consults it to check conformance, and
//! the builder consults it to reject illegal construction. Validity and
//! lossiness are orthogonal: a conformant card may still carry extensions, so
//! validity is that runtime predicate, not
//! a second strict type. A card that passes earns a
//! [`Valid`](tree::vcard::validate::Valid) proof, and both `Vcard` and
//! `Valid<Vcard>` convert back into a [`VcardCst`](tree::cst::VcardCst).
//!
//! ## Content encodings
//!
//! The core transforms no content: a `QUOTED-PRINTABLE` or `BASE64` transfer
//! encoding and a `CHARSET` are surfaced raw, with their parameters kept, so
//! nothing is silently lost or transcoded (only the value grammar, escapes and
//! line folding, is resolved). Decoding them is opt-in, one small `no_std`
//! crate per feature (see [Cargo features](#cargo-features)):
//! [`quoted_printable`](tree::value::VcardValueCursor::quoted_printable) and
//! [`charset`](tree::value::VcardValueCursor::charset) on the value cursor, and
//! [`decode_base64`](value::binary::VcardBinary::decode_base64) on the binary
//! value.
//!
//! ## Cargo features
//!
//! - `parser` (default): the byte-faithful [`tree`] and its codec. Everything
//! under [`tree`] is gated on it; the decoded model is always available.
//! - `jcard` (opt-in): the RFC 7095 jCard codec on the decoded model
//! ([`to_jcard`](vcard::Vcard::to_jcard) /
//! [`from_jcard`](vcard::Vcard::from_jcard)), via the `serde_json` crate
//! (`no_std`, alloc-only). Requires `parser` for the property specs.
//! - `jscontact` (opt-in): the RFC 9555 conversion between the decoded model
//! and an RFC 9553 JSContact Card
//! ([`to_jscontact`](vcard::Vcard::to_jscontact) /
//! [`from_jscontact`](vcard::Vcard::from_jscontact)). Requires `jcard`,
//! whose syntax carries the vCardProps / vCardParams escape hatches.
//! - `quoted-printable` (default): decode `QUOTED-PRINTABLE` value octets, via
//! the `quoted_printable` crate.
//! - `base64` (default): decode inline `BASE64` binary values, via the `base64`
//! crate.
//! - `encoding` (default): transcode a foreign `CHARSET` value to text, via the
//! `encoding_rs` crate (the WHATWG Encoding Standard).
extern crate alloc;