Skip to main content

vcard/
lib.rs

1#![no_std]
2#![cfg_attr(docsrs, feature(doc_cfg))]
3
4//! # vcard-rs
5//!
6//! One version-agnostic vCard library: a decoded model and a byte-faithful
7//! syntax tree, reading and writing vCard 2.1 (versitcard), 3.0 (RFC 2426) and
8//! 4.0 (RFC 6350) alike. The version is a decoded indicator, never a type
9//! parameter: the tree ignores it, and only the codec and the per-property spec
10//! branch on it. The crate is `no_std` (with `alloc`), its core is
11//! dependency-free, and every dependency sits behind a
12//! [cargo feature](#cargo-features).
13//!
14//! This header is the architecture; the behaviour behind it is specified
15//! capability by capability in the repository's cairn/spec folder.
16//!
17//! ## Example
18//!
19//! Parse raw bytes, read a property through its typed lens, edit it in place,
20//! and serialize back; every untouched byte is preserved.
21//!
22//! ```rust
23//! use vcard::tree::cst::VcardCst;
24//! use vcard::tree::prop::r#fn::FN;
25//!
26//! let mut card =
27//!     VcardCst::parse("BEGIN:VCARD\r\nVERSION:4.0\r\nFN:John Doe\r\nEND:VCARD\r\n").unwrap();
28//!
29//! assert_eq!(&*card.prop::<FN>().unwrap().0, "John Doe");
30//!
31//! card.prop_mut::<FN>().unwrap().set_text("Jane Doe");
32//! assert_eq!(
33//!     card.to_string(),
34//!     "BEGIN:VCARD\r\nVERSION:4.0\r\nFN:Jane Doe\r\nEND:VCARD\r\n",
35//! );
36//! ```
37//!
38//! ## Postel's law
39//!
40//! Parsing is maximally liberal: any real card round-trips byte for byte,
41//! including vocabulary no version defines, and an `Unknown` arm on every open
42//! enum carries that openness into the model. Strictness lives on the way out:
43//! the builder refuses to construct a property the spec forbids, and
44//! [`validate`](tree::vcard::validate) checks a decoded card against its
45//! version's RFC contract.
46//!
47//! ## The two layers
48//!
49//! The decoded model ([`vcard`], [`version`], [`prop`], [`param`], [`value`])
50//! is pure data, with no dependency on the syntax side, so it can be depended
51//! on alone. Property names, parameter names and value kinds are closed enums
52//! ([`VcardPropKind`](prop::VcardPropKind),
53//! [`VcardParamKind`](param::VcardParamKind),
54//! [`VcardValueKind`](value::VcardValueKind)) reaching their wire spelling
55//! through `FromStr` and `Deref`; a [`VcardProp`](prop::VcardProp) is a name,
56//! its parameters and one value, the last two open payload enums
57//! ([`VcardParam`](param::VcardParam), [`VcardValue`](value::VcardValue)) with
58//! an `Unknown` arm.
59//!
60//! The syntax tree ([`tree`], behind the default `parser` feature) is
61//! everything byte-faithful. Its hub is [`VcardCst`](tree::cst::VcardCst),
62//! generic nodes reproducing the wire exactly:
63//! [`parse`](tree::cst::VcardCst::parse) reads one card (or a bare RFC 2425
64//! record), [`parse_many`](tree::cst::VcardCst::parse_many) iterates a file,
65//! and [`decode`](tree::codec::decode) / [`encode`](tree::codec::encode)
66//! project between tree and model. Per-property lens markers
67//! ([`VcardPropLens`](tree::prop::lens::VcardPropLens)) edit one line through
68//! byte-preserving [`cursor`](tree::value::cursor::VcardValueCursor)s, and the
69//! three-way [`merge`](tree::merge::merge) reconciles two divergent copies on
70//! top of those same edits.
71//!
72//! A property value is raw bytes, so a foreign charset (a vCard 2.1 `CHARSET`)
73//! survives; a name or parameter must be UTF-8, as every grammar guarantees.
74//! [`to_bytes`](tree::cst::VcardCst::to_bytes) is therefore the faithful
75//! serializer, and [`Display`](core::fmt::Display) a convenience that is lossy
76//! only for a non-UTF-8 value.
77//!
78//! ## The spec layer
79//!
80//! Each property carries a [`VcardPropSpec`](tree::prop::spec::VcardPropSpec)
81//! on its lens marker, declaring per version what it allows, and one vtable
82//! dispatch bridges the open [`VcardPropKind`](prop::VcardPropKind) back to
83//! those static specs. That one source of truth has three readers: the decoder
84//! picks a value kind from it, [`validate`](tree::vcard::validate) checks
85//! conformance against it, and the builder rejects illegal construction with
86//! it. A card that passes earns a
87//! [`VcardValid`](tree::vcard::validate::VcardValid) proof; validity is a
88//! runtime predicate rather than a second type, since a conformant card may
89//! still carry extensions.
90//!
91//! ## Content encodings
92//!
93//! The core transforms no content: a `QUOTED-PRINTABLE` or `BASE64` encoding
94//! and a `CHARSET` are surfaced raw with their parameters kept, so nothing is
95//! silently transcoded. Only the value grammar (escapes and folding) is
96//! resolved, because that is parsing. Decoding is opt-in, one small `no_std`
97//! crate per feature:
98//! [`quoted_printable`](tree::value::cursor::VcardValueCursor::quoted_printable)
99//! and [`charset`](tree::value::cursor::VcardValueCursor::charset) on the value
100//! cursor, [`decode_base64`](value::binary::VcardBinary::decode_base64) on the
101//! binary value.
102//!
103//! ## Cargo features
104//!
105//! - `parser` (default): the byte-faithful [`tree`] and its codec, via the
106//!   `memchr` crate. Everything under [`tree`] is gated on it; the decoded
107//!   model is always available.
108//! - `jcard`: the RFC 7095 jCard codec on the decoded model
109//!   ([`to_jcard`](vcard::Vcard::to_jcard) /
110//!   [`from_jcard`](vcard::Vcard::from_jcard)), via the `serde_json` crate
111//!   (`no_std`, alloc-only). Implies `parser` for the property specs.
112//! - `jscontact`: the RFC 9555 conversion between the decoded model and an RFC
113//!   9553 JSContact Card ([`to_jscontact`](vcard::Vcard::to_jscontact) /
114//!   [`from_jscontact`](vcard::Vcard::from_jscontact)). Implies `jcard`, whose
115//!   syntax carries the vCardProps / vCardParams escape hatches, and pulls no
116//!   crate of its own.
117//! - `quoted-printable` (default): decode `QUOTED-PRINTABLE` value octets, via
118//!   the `quoted_printable` crate.
119//! - `base64` (default): decode inline `BASE64` binary values, via the `base64`
120//!   crate.
121//! - `encoding` (default): transcode a foreign `CHARSET` value to text, via the
122//!   `encoding_rs` crate (the WHATWG Encoding Standard).
123
124extern crate alloc;
125
126#[cfg(feature = "jcard")]
127pub mod jcard;
128#[cfg(feature = "jscontact")]
129pub mod jscontact;
130pub mod param;
131pub mod prop;
132pub mod value;
133pub mod vcard;
134pub mod version;
135
136#[cfg(feature = "parser")]
137pub mod tree;