1use crate::aim::Prefix;
7use nom::{
8 IResult, Parser, branch::alt, character::complete::{char, digit1, multispace0, one_of}, combinator::{map, opt, value}, error::{Error, ErrorKind}, sequence::{delimited, preceded}
9};
10use std::sync::Arc;
11
12#[derive(Debug, PartialEq, Clone)]
16pub enum Item {
17 Value(Prefix, Arc<str>),
19
20 Task(Prefix, Option<bool>, Arc<str>),
22}
23
24fn parse_contents(input: &str) -> IResult<&str, ()> {
25 let (input, _) = multispace0(input)?;
26 if input.is_empty() {
27 Err(nom::Err::Error(Error::new(input, ErrorKind::Fail)))
28 } else {
29 Ok((input, ()))
30 }
31}
32fn parse_ordered(input: &str) -> IResult<&str, Prefix> {
33 preceded(
34 (multispace0, digit1, char('.')),
35 value(Prefix::Ordered, multispace0)
36 ).parse(input)
37}
38
39fn parse_unordered(input: &str) -> IResult<&str, Prefix> {
40 preceded(
41 (multispace0, char('-')),
42 value(Prefix::Unordered, multispace0)
43 ).parse(input)
44}
45
46fn parse_checkbox(input: &str) -> IResult<&str, Option<bool>> {
47 map(
48 preceded(
49 multispace0,
50 delimited(
51 char('['),
52 opt(preceded(multispace0, one_of("Xx+-"))),
53 preceded(multispace0, char(']')),
54 )
55 ),
56 |status_char| match status_char {
57 Some('X') | Some('x') | Some('+') => Some(true),
58 Some('-') => Some(false),
59 _ => None,
60 }
61 ).parse(input)
62}
63
64fn parse_inline_task(input: &str) -> IResult<&str, (Option<bool>, Arc<str>)> {
65 let (input, status) = parse_checkbox(input)?;
66 let (input, _) = multispace0(input)?;
67 let content = input.trim_end().to_string();
68 if content.is_empty() {
69 return Err(nom::Err::Error(Error::new(input, ErrorKind::Fail)));
70 }
71 Ok(("", (status, Arc::from(content))))
72}
73
74pub fn parse_inline_item(input: &str) -> IResult<&str, Item> {
78 if let Ok((remaining, (status, value))) = parse_inline_task(input) {
80 return Ok((remaining, Item::Task(Prefix::None, status, value)));
81 }
82 let (input, _) = parse_contents(input)?;
83 Ok((
84 input,
85 Item::Value(Prefix::None, Arc::from(input.trim_end())),
86 ))
87}
88
89fn parse_task(input: &str) -> IResult<&str, (Prefix, Option<bool>, Arc<str>)> {
90 let (input, prefix) = opt(alt((parse_ordered, parse_unordered))).parse(input)?;
91 let prefix = prefix.unwrap_or(Prefix::None);
92 let (input, _) = multispace0(input)?;
93 let (input, status) = parse_checkbox(input)?;
94 let (input, _) = parse_contents(input)?;
95
96 Ok((input, (prefix, status, Arc::from(input.trim_end()))))
97}
98
99fn parse_value(input: &str) -> IResult<&str, (Prefix, Arc<str>)> {
100 let (input, prefix) = opt(alt((parse_ordered, parse_unordered))).parse(input)?;
101 let prefix = prefix.unwrap_or(Prefix::None);
102 let (input, _) = parse_contents(input)?;
103
104 Ok((input, (prefix, Arc::from(input.trim_end()))))
105}
106
107pub fn parse_item(input: &str) -> IResult<&str, Item> {
111 if let Ok((remaining, (prefix, status, value))) = parse_task(input) {
113 return Ok((remaining, Item::Task(prefix, status, value)));
114 }
115
116 let (input, (prefix, value)) = parse_value(input)?;
118 Ok((input, Item::Value(prefix, value)))
119}