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