orgize/elements/
clock.rs

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/// Clock Element
15#[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 Clock
21    Closed {
22        /// Time start
23        start: Datetime<'a>,
24        /// Time end
25        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        /// Clock duration
31        duration: Cow<'a, str>,
32        /// Numbers of blank lines between the clock line and next non-blank
33        /// line or buffer's end
34        post_blank: usize,
35    },
36    /// Running Clock
37    Running {
38        /// Time start
39        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        /// Numbers of blank lines between the clock line and next non-blank
45        /// line or buffer's end
46        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    /// Returns `true` if the clock is running.
87    pub fn is_running(&self) -> bool {
88        match self {
89            Clock::Closed { .. } => false,
90            Clock::Running { .. } => true,
91        }
92    }
93
94    /// Returns `true` if the clock is closed.
95    pub fn is_closed(&self) -> bool {
96        match self {
97            Clock::Closed { .. } => true,
98            Clock::Running { .. } => false,
99        }
100    }
101
102    /// Returns clock duration, or `None` if it's running.
103    pub fn duration(&self) -> Option<&str> {
104        match self {
105            Clock::Closed { duration, .. } => Some(duration),
106            Clock::Running { .. } => None,
107        }
108    }
109
110    /// Constructs a timestamp from the clock.
111    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}