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