1use std::borrow::Cow;
2
3use nom::{
4 bytes::complete::tag,
5 character::complete::{char, digit1, space0},
6 combinator::recognize,
7 sequence::separated_pair,
8 IResult,
9};
10
11use crate::elements::timestamp::{parse_inactive, Datetime, Timestamp};
12use crate::parse::combinators::{blank_lines_count, eol};
13
14#[cfg_attr(test, derive(PartialEq))]
16#[cfg_attr(feature = "ser", derive(serde::Serialize))]
17#[cfg_attr(feature = "ser", serde(untagged))]
18#[derive(Debug, Clone)]
19pub enum Clock<'a> {
20 Closed {
22 start: Datetime<'a>,
24 end: Datetime<'a>,
26 #[cfg_attr(feature = "ser", serde(skip_serializing_if = "Option::is_none"))]
27 repeater: Option<Cow<'a, str>>,
28 #[cfg_attr(feature = "ser", serde(skip_serializing_if = "Option::is_none"))]
29 delay: Option<Cow<'a, str>>,
30 duration: Cow<'a, str>,
32 post_blank: usize,
35 },
36 Running {
38 start: Datetime<'a>,
40 #[cfg_attr(feature = "ser", serde(skip_serializing_if = "Option::is_none"))]
41 repeater: Option<Cow<'a, str>>,
42 #[cfg_attr(feature = "ser", serde(skip_serializing_if = "Option::is_none"))]
43 delay: Option<Cow<'a, str>>,
44 post_blank: usize,
47 },
48}
49
50impl Clock<'_> {
51 pub(crate) fn parse(input: &str) -> Option<(&str, Clock)> {
52 parse_internal(input).ok()
53 }
54
55 pub fn into_onwed(self) -> Clock<'static> {
56 match self {
57 Clock::Closed {
58 start,
59 end,
60 repeater,
61 delay,
62 duration,
63 post_blank,
64 } => Clock::Closed {
65 start: start.into_owned(),
66 end: end.into_owned(),
67 repeater: repeater.map(Into::into).map(Cow::Owned),
68 delay: delay.map(Into::into).map(Cow::Owned),
69 duration: duration.into_owned().into(),
70 post_blank,
71 },
72 Clock::Running {
73 start,
74 repeater,
75 delay,
76 post_blank,
77 } => Clock::Running {
78 start: start.into_owned(),
79 repeater: repeater.map(Into::into).map(Cow::Owned),
80 delay: delay.map(Into::into).map(Cow::Owned),
81 post_blank,
82 },
83 }
84 }
85
86 pub fn is_running(&self) -> bool {
88 match self {
89 Clock::Closed { .. } => false,
90 Clock::Running { .. } => true,
91 }
92 }
93
94 pub fn is_closed(&self) -> bool {
96 match self {
97 Clock::Closed { .. } => true,
98 Clock::Running { .. } => false,
99 }
100 }
101
102 pub fn duration(&self) -> Option<&str> {
104 match self {
105 Clock::Closed { duration, .. } => Some(duration),
106 Clock::Running { .. } => None,
107 }
108 }
109
110 pub fn value(&self) -> Timestamp {
112 match &*self {
113 Clock::Closed {
114 start,
115 end,
116 repeater,
117 delay,
118 ..
119 } => Timestamp::InactiveRange {
120 start: start.clone(),
121 end: end.clone(),
122 repeater: repeater.clone(),
123 delay: delay.clone(),
124 },
125 Clock::Running {
126 start,
127 repeater,
128 delay,
129 ..
130 } => Timestamp::Inactive {
131 start: start.clone(),
132 repeater: repeater.clone(),
133 delay: delay.clone(),
134 },
135 }
136 }
137}
138
139fn parse_internal(input: &str) -> IResult<&str, Clock, ()> {
140 let (input, _) = space0(input)?;
141 let (input, _) = tag("CLOCK:")(input)?;
142 let (input, _) = space0(input)?;
143 let (input, timestamp) = parse_inactive(input)?;
144
145 match timestamp {
146 Timestamp::InactiveRange {
147 start,
148 end,
149 repeater,
150 delay,
151 } => {
152 let (input, _) = space0(input)?;
153 let (input, _) = tag("=>")(input)?;
154 let (input, _) = space0(input)?;
155 let (input, duration) = recognize(separated_pair(digit1, char(':'), digit1))(input)?;
156 let (input, _) = eol(input)?;
157 let (input, blank) = blank_lines_count(input)?;
158 Ok((
159 input,
160 Clock::Closed {
161 start,
162 end,
163 repeater,
164 delay,
165 duration: duration.into(),
166 post_blank: blank,
167 },
168 ))
169 }
170 Timestamp::Inactive {
171 start,
172 repeater,
173 delay,
174 } => {
175 let (input, _) = eol(input)?;
176 let (input, blank) = blank_lines_count(input)?;
177 Ok((
178 input,
179 Clock::Running {
180 start,
181 repeater,
182 delay,
183 post_blank: blank,
184 },
185 ))
186 }
187 _ => unreachable!(
188 "`parse_inactive` only returns `Timestamp::InactiveRange` or `Timestamp::Inactive`."
189 ),
190 }
191}
192
193#[test]
194fn parse() {
195 assert_eq!(
196 Clock::parse("CLOCK: [2003-09-16 Tue 09:39]"),
197 Some((
198 "",
199 Clock::Running {
200 start: Datetime {
201 year: 2003,
202 month: 9,
203 day: 16,
204 dayname: "Tue".into(),
205 hour: Some(9),
206 minute: Some(39)
207 },
208 repeater: None,
209 delay: None,
210 post_blank: 0,
211 }
212 ))
213 );
214 assert_eq!(
215 Clock::parse("CLOCK: [2003-09-16 Tue 09:39]--[2003-09-16 Tue 10:39] => 1:00\n\n"),
216 Some((
217 "",
218 Clock::Closed {
219 start: Datetime {
220 year: 2003,
221 month: 9,
222 day: 16,
223 dayname: "Tue".into(),
224 hour: Some(9),
225 minute: Some(39)
226 },
227 end: Datetime {
228 year: 2003,
229 month: 9,
230 day: 16,
231 dayname: "Tue".into(),
232 hour: Some(10),
233 minute: Some(39)
234 },
235 repeater: None,
236 delay: None,
237 duration: "1:00".into(),
238 post_blank: 1,
239 }
240 ))
241 );
242}