1use alloc::{string::String, vec::Vec};
13use core::fmt::{self, Display, Formatter, Write as _};
14
15use crate::types::{
16 Date, DateField, DateTime, Edtf, Interval, IntervalEndpoint, Qualifier, Set, SetElement,
17 SetKind, Time, TimeShift, Year, YearKind,
18};
19
20impl Display for Edtf {
21 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
22 match self {
23 Self::Date(d) => d.fmt(f),
24 Self::DateTime(dt) => dt.fmt(f),
25 Self::Interval(iv) => iv.fmt(f),
26 Self::Set(s) => s.fmt(f),
27 }
28 }
29}
30
31fn qual_symbol(q: Qualifier) -> char {
32 match (q.uncertain, q.approximate) {
33 (true, true) => '%',
34 (true, false) => '?',
35 (false, true) => '~',
36 (false, false) => unreachable!("caller checks is_qualified"),
37 }
38}
39
40fn year_body(year: &Year) -> String {
41 let mut s = String::new();
42 match year.kind {
43 YearKind::Standard { negative, digits } => {
44 if negative {
45 s.push('-');
46 }
47 for d in digits {
48 match d {
49 Some(v) => s.push((b'0' + v) as char),
50 None => s.push('X'),
51 }
52 }
53 },
54 YearKind::Big { value } => {
55 let _ = write!(s, "Y{value}");
56 },
57 YearKind::Exponential {
58 significand,
59 exponent,
60 } => {
61 let _ = write!(s, "Y{significand}E{exponent}");
62 },
63 }
64 if let Some(p) = year.significant_digits {
65 let _ = write!(s, "S{p}");
66 }
67 s
68}
69
70fn field_body(f: DateField) -> String {
71 let mut s = String::new();
72 for d in f.digits {
73 match d {
74 Some(v) => s.push((b'0' + v) as char),
75 None => s.push('X'),
76 }
77 }
78 s
79}
80
81impl Display for Date {
82 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
83 let mut parts: Vec<(String, Qualifier)> = Vec::new();
84 parts.push((year_body(&self.year), self.year.qualifier));
85 if let Some(m) = &self.month {
86 parts.push((field_body(*m), m.qualifier));
87 }
88 if let Some(d) = &self.day {
89 parts.push((field_body(*d), d.qualifier));
90 }
91
92 let all_equal = parts.iter().all(|(_, q)| *q == parts[0].1);
93 if all_equal {
94 let q = parts[0].1;
96 for (i, (body, _)) in parts.iter().enumerate() {
97 if i > 0 {
98 f.write_char('-')?;
99 }
100 f.write_str(body)?;
101 }
102 if q.is_qualified() {
103 f.write_char(qual_symbol(q))?;
104 }
105 return Ok(());
106 }
107
108 let q0 = parts[0].1;
113 let prefix_end = if q0.is_qualified() {
114 let mut end = 0;
115 while end + 1 < parts.len() && parts[end + 1].1 == q0 {
116 end += 1;
117 }
118 Some(end)
119 } else {
120 None
121 };
122 for (i, (body, q)) in parts.iter().enumerate() {
123 if i > 0 {
124 f.write_char('-')?;
125 }
126 let in_prefix = prefix_end.is_some_and(|end| i <= end);
127 if !in_prefix && q.is_qualified() {
128 f.write_char(qual_symbol(*q))?;
129 }
130 f.write_str(body)?;
131 if prefix_end == Some(i) {
132 f.write_char(qual_symbol(q0))?;
133 }
134 }
135 Ok(())
136 }
137}
138
139impl Display for Time {
140 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
141 write!(f, "{:02}:{:02}:{:02}", self.hour, self.minute, self.second)?;
142 match self.shift {
143 None => Ok(()),
144 Some(TimeShift::Utc) => f.write_char('Z'),
145 Some(TimeShift::Offset {
146 minutes,
147 hours_only,
148 }) => {
149 let sign = if minutes < 0 { '-' } else { '+' };
150 let mag = minutes.unsigned_abs();
151 let (h, m) = (mag / 60, mag % 60);
152 if hours_only && m == 0 {
155 write!(f, "{sign}{h:02}")
156 } else {
157 write!(f, "{sign}{h:02}:{m:02}")
158 }
159 },
160 }
161 }
162}
163
164impl Display for DateTime {
165 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
166 write!(f, "{}T{}", self.date, self.time)
167 }
168}
169
170impl Display for Interval {
171 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
172 fn endpoint(f: &mut Formatter<'_>, e: &IntervalEndpoint) -> fmt::Result {
173 match e {
174 IntervalEndpoint::Unknown => Ok(()),
175 IntervalEndpoint::Open => f.write_str(".."),
176 IntervalEndpoint::Date(d) => d.fmt(f),
177 IntervalEndpoint::OnOrBefore(d) => write!(f, "..{d}"),
178 IntervalEndpoint::OnOrAfter(d) => write!(f, "{d}.."),
179 }
180 }
181 endpoint(f, &self.start)?;
182 f.write_char('/')?;
183 endpoint(f, &self.end)
184 }
185}
186
187impl Display for Set {
188 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
189 let (open, close) = match self.kind {
190 SetKind::AllMembers => ('{', '}'),
191 SetKind::OneMember => ('[', ']'),
192 };
193 f.write_char(open)?;
194 for (i, e) in self.elements.iter().enumerate() {
195 if i > 0 {
196 f.write_char(',')?;
197 }
198 match e {
199 SetElement::Date(d) => d.fmt(f)?,
200 SetElement::OnOrBefore(d) => write!(f, "..{d}")?,
201 SetElement::OnOrAfter(d) => write!(f, "{d}..")?,
202 SetElement::Range(a, b) => write!(f, "{a}..{b}")?,
203 }
204 }
205 f.write_char(close)
206 }
207}