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