subtasks
This commit is contained in:
parent
3bb095ca45
commit
cd97c18fef
|
|
@ -61,8 +61,8 @@ fn main() {
|
||||||
let arena = Arena::new();
|
let arena = Arena::new();
|
||||||
let root = parse_todo_file(&file, &arena);
|
let root = parse_todo_file(&file, &arena);
|
||||||
|
|
||||||
//println!("{:#?}", root);
|
println!("{:#?}", root);
|
||||||
//println!("=======================================================");
|
println!("=======================================================");
|
||||||
|
|
||||||
let sections = &cfg.sections.unwrap();
|
let sections = &cfg.sections.unwrap();
|
||||||
let groups = extract_secitons(root, sections);
|
let groups = extract_secitons(root, sections);
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,8 @@ pub struct TaskGroup {
|
||||||
pub struct Task {
|
pub struct Task {
|
||||||
pub status: Status,
|
pub status: Status,
|
||||||
pub text: String,
|
pub text: String,
|
||||||
|
level: u8,
|
||||||
|
pub subtasks: Option<Vec<Task>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
|
|
@ -24,7 +26,7 @@ pub enum Status {
|
||||||
Empty,
|
Empty,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum TaskErorr {
|
pub enum TaskError {
|
||||||
ParsingError(&'static str),
|
ParsingError(&'static str),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -39,6 +41,42 @@ impl Task {
|
||||||
}
|
}
|
||||||
text
|
text
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn extract_text<'a>(node: &'a AstNode<'a>) -> Result<String, TaskError> {
|
||||||
|
let data_ref = node.data.borrow();
|
||||||
|
if let NodeValue::Text(contents) = &data_ref.value {
|
||||||
|
Ok(contents.to_string())
|
||||||
|
} else {
|
||||||
|
Err(TaskError::ParsingError("Could not get text from element"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn extract_text_from_task<'a>(node: &'a AstNode<'a>) -> Result<String, TaskError> {
|
||||||
|
let mut text = String::new();
|
||||||
|
let data_ref = node.data.borrow();
|
||||||
|
if let NodeValue::Paragraph = data_ref.value {
|
||||||
|
for child in node.children() {
|
||||||
|
let child_data_ref = child.data.borrow();
|
||||||
|
let t = match &child_data_ref.borrow().value {
|
||||||
|
NodeValue::Text(contents) => contents.clone(),
|
||||||
|
NodeValue::Emph if child.first_child().is_some() => {
|
||||||
|
format!("*{}*", Self::extract_text(child.first_child().unwrap())?)
|
||||||
|
}
|
||||||
|
NodeValue::Strong if child.first_child().is_some() => {
|
||||||
|
format!("**{}**", Self::extract_text(child.first_child().unwrap())?)
|
||||||
|
}
|
||||||
|
NodeValue::SoftBreak => {
|
||||||
|
format!("\n{}", " ".repeat(data_ref.sourcepos.start.column))
|
||||||
|
}
|
||||||
|
_ => "".into(),
|
||||||
|
};
|
||||||
|
text.push_str(&t);
|
||||||
|
}
|
||||||
|
Ok(text)
|
||||||
|
} else {
|
||||||
|
Err(TaskError::ParsingError("First child is not Paragraph"))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToString for Task {
|
impl ToString for Task {
|
||||||
|
|
@ -48,25 +86,68 @@ impl ToString for Task {
|
||||||
Status::Todo(ch) => ch,
|
Status::Todo(ch) => ch,
|
||||||
Status::Empty => ' ',
|
Status::Empty => ' ',
|
||||||
};
|
};
|
||||||
format!(" - [{}] {}\n", ch, self.text.trim())
|
|
||||||
|
let subtasks = if let Some(subtasks) = &self.subtasks {
|
||||||
|
let mut text = subtasks
|
||||||
|
.iter()
|
||||||
|
.map(|task| task.to_string())
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join("\n");
|
||||||
|
text.insert(0, '\n');
|
||||||
|
text.trim_end().to_string()
|
||||||
|
} else {
|
||||||
|
"".into()
|
||||||
|
};
|
||||||
|
|
||||||
|
format!(
|
||||||
|
" - [{}] {}{}\n",
|
||||||
|
ch,
|
||||||
|
self.text.trim(),
|
||||||
|
subtasks.replace("\n", "\n ")
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> TryFrom<&'a AstNode<'a>> for Task {
|
impl<'a> TryFrom<&'a AstNode<'a>> for Task {
|
||||||
type Error = TaskErorr;
|
type Error = TaskError;
|
||||||
fn try_from(node: &'a AstNode<'a>) -> Result<Self, Self::Error> {
|
fn try_from(node: &'a AstNode<'a>) -> Result<Self, Self::Error> {
|
||||||
let data_ref = &node.data.borrow();
|
let data_ref = &node.data.borrow();
|
||||||
if let NodeValue::TaskItem(ch) = data_ref.value {
|
if let NodeValue::TaskItem(ch) = data_ref.value {
|
||||||
let text = Self::find_text(node);
|
let text = Self::extract_text_from_task(
|
||||||
|
node.first_child()
|
||||||
|
.ok_or(TaskError::ParsingError("No childern of node found"))?,
|
||||||
|
)?;
|
||||||
let status = match ch {
|
let status = match ch {
|
||||||
Some(c) if c == 'x' || c == 'X' => Status::Done(c),
|
Some(c) if c == 'x' || c == 'X' => Status::Done(c),
|
||||||
Some(c) => Status::Todo(c),
|
Some(c) => Status::Todo(c),
|
||||||
_ => Status::Empty,
|
_ => Status::Empty,
|
||||||
};
|
};
|
||||||
|
let subtasks = node
|
||||||
Ok(Self { status, text })
|
.children()
|
||||||
|
.filter_map(|child| {
|
||||||
|
if let NodeValue::List(_) = child.data.borrow().value {
|
||||||
|
Some(child)
|
||||||
} else {
|
} else {
|
||||||
Err(TaskErorr::ParsingError(
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.map(|child| {
|
||||||
|
child
|
||||||
|
.children()
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(|item_node| Task::try_from(item_node).ok())
|
||||||
|
.collect()
|
||||||
|
})
|
||||||
|
.reduce(|a: Vec<Task>, b: Vec<Task>| [a, b].concat());
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
status,
|
||||||
|
text,
|
||||||
|
subtasks,
|
||||||
|
level: 1,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Err(TaskError::ParsingError(
|
||||||
"Node being parsed is not a TaskItem",
|
"Node being parsed is not a TaskItem",
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
@ -95,7 +176,7 @@ impl ToString for TaskGroup {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> TryFrom<&'a AstNode<'a>> for TaskGroup {
|
impl<'a> TryFrom<&'a AstNode<'a>> for TaskGroup {
|
||||||
type Error = TaskErorr;
|
type Error = TaskError;
|
||||||
fn try_from(node: &'a AstNode<'a>) -> Result<Self, Self::Error> {
|
fn try_from(node: &'a AstNode<'a>) -> Result<Self, Self::Error> {
|
||||||
let node_ref = &node.data.borrow();
|
let node_ref = &node.data.borrow();
|
||||||
if let NodeValue::Heading(heading) = node_ref.value {
|
if let NodeValue::Heading(heading) = node_ref.value {
|
||||||
|
|
@ -104,21 +185,21 @@ impl<'a> TryFrom<&'a AstNode<'a>> for TaskGroup {
|
||||||
let first_child = if let Some(child) = first_child_ref.borrow() {
|
let first_child = if let Some(child) = first_child_ref.borrow() {
|
||||||
child
|
child
|
||||||
} else {
|
} else {
|
||||||
return Err(TaskErorr::ParsingError("Node has no children"));
|
return Err(TaskError::ParsingError("Node has no children"));
|
||||||
};
|
};
|
||||||
|
|
||||||
let data_ref = &first_child.data.borrow();
|
let data_ref = &first_child.data.borrow();
|
||||||
let name = if let NodeValue::Text(value) = &data_ref.value {
|
let name = if let NodeValue::Text(value) = &data_ref.value {
|
||||||
value.to_string()
|
value.to_string()
|
||||||
} else {
|
} else {
|
||||||
return Err(TaskErorr::ParsingError(
|
return Err(TaskError::ParsingError(
|
||||||
"Could not get title from heading node",
|
"Could not get title from heading node",
|
||||||
));
|
));
|
||||||
};
|
};
|
||||||
|
|
||||||
let next_sib = node
|
let next_sib = node
|
||||||
.next_sibling()
|
.next_sibling()
|
||||||
.ok_or(TaskErorr::ParsingError("Empty section at end of file"))?;
|
.ok_or(TaskError::ParsingError("Empty section at end of file"))?;
|
||||||
|
|
||||||
if let NodeValue::List(_list_meta) = next_sib.data.borrow().value {
|
if let NodeValue::List(_list_meta) = next_sib.data.borrow().value {
|
||||||
let tasks = next_sib
|
let tasks = next_sib
|
||||||
|
|
@ -129,12 +210,12 @@ impl<'a> TryFrom<&'a AstNode<'a>> for TaskGroup {
|
||||||
|
|
||||||
Ok(TaskGroup { name, tasks, level })
|
Ok(TaskGroup { name, tasks, level })
|
||||||
} else {
|
} else {
|
||||||
Err(TaskErorr::ParsingError(
|
Err(TaskError::ParsingError(
|
||||||
"Next sibling of node is not a list",
|
"Next sibling of node is not a list",
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Err(TaskErorr::ParsingError("Node is not a section heading"))
|
Err(TaskError::ParsingError("Node is not a section heading"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue