use pest::Parser;
use pest_derive::Parser;
use std::boxed::Box;
use std::convert::From;
use thiserror::Error;
#[derive(Parser)]
#[grammar = "todotxt.pest"]
pub struct TodotxtParser;
#[derive(Error, Debug)]
pub enum ParseError {
#[error("Pest parsing error: {0}")]
PestError(Box<pest::error::Error<Rule>>),
}
impl From<pest::error::Error<Rule>> for ParseError {
fn from(e: pest::error::Error<Rule>) -> Self {
ParseError::PestError(Box::new(e))
}
}
#[derive(Debug, PartialEq, Default)]
pub struct Task {
pub is_completed: bool,
pub priority: Option<char>,
pub creation_date: Option<String>,
pub due_date: Option<String>,
pub projects: Vec<String>,
pub contexts: Vec<String>,
pub description_parts: Vec<String>,
}
pub fn parse_line(line: &str) -> Result<Task, ParseError> {
let mut pairs = TodotxtParser::parse(Rule::task_line, line)?;
let task_line_pairs = pairs.next().unwrap().into_inner();
let mut task = Task::default();
for pair in task_line_pairs {
match pair.as_rule() {
Rule::completion => {
task.is_completed = true;
}
Rule::priority => {
let tag = pair.into_inner().next().unwrap().as_str();
task.priority = tag.chars().nth(1);
}
Rule::creation_date => {
let date_str = pair.into_inner().next().unwrap().as_str();
task.creation_date = Some(date_str.to_string());
}
Rule::project => {
task.projects.push(pair.as_str().to_string());
}
Rule::context => {
task.contexts.push(pair.as_str().to_string());
}
Rule::due_date => {
let full_tag = pair.as_str();
let date_part = full_tag.strip_prefix("due:").unwrap_or(full_tag);
task.due_date = Some(date_part.to_string());
}
Rule::text_part => {
task.description_parts.push(pair.as_str().to_string());
}
Rule::EOI => {}
_ => {}
}
}
Ok(task)
}