edtf_core/lib.rs
1//! EDTF (Extended Date/Time Format, ISO 8601-2:2019 Annex A) parsing,
2//! validation, level classification, calendar bounds, three-valued temporal
3//! relations, value enumeration, and canonical formatting — conformance
4//! levels 0–2, complete.
5//!
6//! `#![no_std]` (requires `alloc`), zero runtime dependencies; JSON support
7//! behind the optional `serde` feature.
8//!
9//! ```
10//! use edtf_core::{Bound, Edtf};
11//!
12//! assert!(edtf_core::is_valid("1985-04-12")); // level 0
13//! assert!(edtf_core::is_valid("2004-06~-11")); // level 2 group qualification
14//! assert!(!edtf_core::is_valid("1985-02-30")); // no such calendar day
15//!
16//! let d = Edtf::parse("1985-04-12?").unwrap();
17//! assert_eq!(d.level(), 1);
18//! assert!(d.is_uncertain());
19//!
20//! // Every expression maps to earliest/latest calendar-day bounds:
21//! let decade = Edtf::parse("156X").unwrap().bounds();
22//! assert_eq!(
23//! format!(
24//! "{}",
25//! match decade.earliest {
26//! Bound::Date(d) => d,
27//! _ => panic!(),
28//! }
29//! ),
30//! "1560-01-01"
31//! );
32//! assert_eq!(
33//! format!(
34//! "{}",
35//! match decade.latest {
36//! Bound::Date(d) => d,
37//! _ => panic!(),
38//! }
39//! ),
40//! "1569-12-31"
41//! );
42//!
43//! // Display renders the canonical (spec-preferred) form:
44//! let messy = Edtf::parse("?2004-?06-?11").unwrap();
45//! assert_eq!(messy.to_string(), "2004-06-11?");
46//!
47//! // Three-valued comparison under uncertainty (see docs/spec-notes.md D23):
48//! use edtf_core::Relation;
49//! let a = Edtf::parse("1985~").unwrap();
50//! let b = Edtf::parse("199X").unwrap();
51//! assert_eq!(a.relation(&b).definite(), Some(Relation::Before));
52//!
53//! // Enumerate the values an expression denotes (see D24-D29):
54//! let set = Edtf::parse("{1667,1668,1670..1672}").unwrap();
55//! let years: Vec<String> = set.values().unwrap().map(|v| v.to_string()).collect();
56//! assert_eq!(years, ["1667", "1668", "1670", "1671", "1672"]);
57//! ```
58//!
59//! The grammar and every validation decision are documented with ISO section
60//! citations in `docs/spec-notes.md` at the repository root.
61#![no_std]
62
63extern crate alloc;
64
65mod bounds;
66mod display;
67mod enumerate;
68mod parser;
69mod relation;
70mod types;
71
72pub use bounds::{Bound, BoundDate, Bounds};
73pub use enumerate::{Unenumerable, Values};
74pub use relation::{Modality, Relation, Relations};
75pub use types::{
76 Date, DateField, DateTime, Edtf, Interval, IntervalEndpoint, ParseError, Precision, Qualifier,
77 Set, SetElement, SetKind, Time, TimeShift, Year, YearKind,
78};
79
80/// Returns true if `input` is a valid EDTF string (levels 0–2).
81#[must_use]
82pub fn is_valid(input: &str) -> bool {
83 Edtf::parse(input).is_ok()
84}
85
86/// Parse `input` and return its minimum EDTF conformance level (0, 1 or 2),
87/// or `None` if it is not valid EDTF.
88#[must_use]
89pub fn level(input: &str) -> Option<u8> {
90 Edtf::parse(input).ok().map(|e| e.level())
91}