commit - e79ffd7b92c4807a481f6ad3d27605e484f4f58d
commit + 01ba7a35eb3e4796a5d0cdf8a5d72c53d4a7c3e4
blob - 38e4d1b8f5e505c581996508721002c641872c81
blob + e6891cd04ab7707206157794d5d2314001bfaa1a
--- src/lib.rs
+++ src/lib.rs
/// let input = "2023-01-05 sample task";
/// let res = parse_from_str(input)?;
/// assert_eq!(res, vec![Task::new(Completion::Incomplete, 0, Some(NaiveDate::from_ymd_opt(2023, 1, 5).unwrap()), "sample task")]);
-/// assert_eq!(res.iter().map(Task::tags).collect::<Vec<&Vec<Tag>>>(), vec![&vec![]]);
+/// assert_eq!(res.iter().map(Task::tags).collect::<Vec<Vec<Tag>>>(), vec![vec![]]);
/// # Ok::<(), TodoError>(())
/// ```
///
blob - f7807f7a2f69ab415d0e9463f3b32ef1bafe4f90
blob + 430004c8b9e8287caac59bd1211e1cdfd9fb36e5
--- src/parser.rs
+++ src/parser.rs
"a perfectly normal task +testing @home due:2023-01-02"
)
);
- assert_eq!(t1.tags(), &tags1);
+ assert_eq!(t1.tags(), tags1);
let (_ ,t2) = task("x 2023-01-02 2023-01-01 a completed task with completion date +testing @home due:2023-01-02 @place +todotxt-parser").unwrap();
let tags2 = vec![
t2,
Task::new(Completion::Complete(Some(NaiveDate::from_ymd_opt(2023, 01, 02).unwrap())), 0, Some(NaiveDate::from_ymd_opt(2023, 01, 01).unwrap()), "a completed task with completion date +testing @home due:2023-01-02 @place +todotxt-parser")
);
- assert_eq!(t2.tags(), &tags2);
+ assert_eq!(t2.tags(), tags2);
}
#[test]
Task::new(Completion::Complete(Some(NaiveDate::from_ymd_opt(2023, 01, 02).unwrap())), 0, Some(NaiveDate::from_ymd_opt(2023, 01, 01).unwrap()), "a completed task with completion date +testing @home due:2023-01-02 @place +todotxt-parser and recurring every rec:+4m 4 months strictly")
]
);
- assert_eq!(ts[0].tags(), &tags1);
- assert_eq!(ts[1].tags(), &tags2);
+ assert_eq!(ts[0].tags(), tags1);
+ assert_eq!(ts[1].tags(), tags2);
}
#[test]
blob - d0dd3c3b174c908a313e24cb9f73e95a9a0614d4
blob + 38241164113bfce4b50fd85ba3c75ae01d2f058f
--- src/types.rs
+++ src/types.rs
use crate::parser;
use chrono::naive::NaiveDate;
use nom::Finish;
-use std::num::NonZeroU8;
+use std::{borrow::Cow, num::NonZeroU8};
/// A Tag in a Task.
#[non_exhaustive]
/// Date of creation.
pub creation_date: Option<NaiveDate>,
/// Description of the [`Task`].
- // `description` can't be public as it is closely tied to `tags`. Changing one means changing
- // the other. Access and modification should be done through separate methods.
- description: &'a str,
- /// Associated [`Tag`]s.
- ///
- /// For a list of supported tags, see [`Tag`].
- // `tags` can't be public as it is closely tied to `description`. Changing one means changing
- // the other. Access and modification should be done through separate methods.
- tags: Vec<Tag<'a>>,
+ description: Cow<'a, str>,
}
impl<'a> Task<'a> {
/// # use chrono::NaiveDate;
/// let raw_task = "x (A) 2016-05-20 2016-04-30 measure space for +chapelShelving @chapel due:2016-05-30";
/// let task = Task::try_from(raw_task)?;
- /// assert_eq!(task.tags(), &vec![
+ /// assert_eq!(task.tags(), vec![
/// Tag::Project("chapelShelving"),
/// Tag::Context("chapel"),
/// Tag::Due(NaiveDate::from_ymd_opt(2016, 5, 30).unwrap())
creation_date: Option<NaiveDate>,
description: &'a str,
) -> Self {
- let tags = parser::description_tags(description)
- .map(|(_, res)| res)
- .unwrap_or_default();
-
Self {
completion,
priority,
creation_date,
- description,
- tags,
+ description: Cow::from(description),
}
}
#[must_use]
+ pub const fn has_changed(&self) -> bool {
+ matches!(self.description, Cow::Owned(_))
+ }
+
+ #[must_use]
pub const fn completion(&self) -> &Completion {
&self.completion
}
- pub fn set_completion(&mut self, completion: Completion) {
- self.completion = completion;
- }
-
#[must_use]
pub const fn priority(&self) -> u8 {
self.priority
}
- pub fn set_priority(&mut self, priority: u8) {
- self.priority = priority;
- }
-
#[must_use]
pub const fn creation_date(&self) -> Option<NaiveDate> {
self.creation_date
}
- pub fn set_creation_date(&mut self, creation_date: Option<NaiveDate>) {
- self.creation_date = creation_date;
+ #[must_use]
+ pub fn description(&'a self) -> &'a str {
+ self.description.as_ref()
}
- #[must_use]
- pub const fn description(&self) -> &'a str {
- self.description
+ pub fn set_description(&mut self, description: String) {
+ self.description = Cow::from(description);
}
- pub fn set_description(&mut self, description: &'a str) {
- let tags = parser::description_tags(description)
+ #[must_use]
+ pub fn tags(&self) -> Vec<Tag> {
+ parser::description_tags(self.description.as_ref())
.map(|(_, res)| res)
- .unwrap_or_default();
- self.description = description;
- self.tags = tags;
+ .unwrap_or_default()
}
- #[must_use]
- pub const fn tags(&self) -> &Vec<Tag> {
- &self.tags
- }
-
/// Return the projects found in the description of the task.
#[must_use]
- pub fn projects(&self) -> Vec<&'a str> {
- self.tags
- .iter()
+ pub fn projects(&'a self) -> Vec<&'a str> {
+ self.tags()
+ .into_iter()
.filter_map(|t| {
if let Tag::Project(p) = t {
- Some(*p)
+ Some(p)
} else {
None
}
/// Return the contexts found in the description of the task.
#[must_use]
- pub fn contexts(&self) -> Vec<&'a str> {
- self.tags
- .iter()
+ pub fn contexts(&'a self) -> Vec<&'a str> {
+ self.tags()
+ .into_iter()
.filter_map(|t| {
if let Tag::Context(c) = t {
- Some(*c)
+ Some(c)
} else {
None
}
/// Return the due date of the task, if there is one.
#[must_use]
- pub fn due(&self) -> Option<&NaiveDate> {
- self.tags
- .iter()
+ pub fn due(&self) -> Option<NaiveDate> {
+ self.tags()
+ .into_iter()
.find_map(|t| if let Tag::Due(d) = t { Some(d) } else { None })
}
/// Return the recurring rule of the task, if there is one.
#[must_use]
- pub fn rec(&self) -> Option<&Recurring> {
- self.tags
- .iter()
+ pub fn rec(&self) -> Option<Recurring> {
+ self.tags()
+ .into_iter()
.find_map(|t| if let Tag::Rec(r) = t { Some(r) } else { None })
}
}
}
};
- let tags = parser::description_tags(description)
- .finish()
- .map_err(|_| TodoError::ParsingError("description tags"))?
- .1;
-
Ok(Task {
completion: (completed, completion_date).try_into()?,
priority,
creation_date,
- description,
- tags,
+ description: description.into(),
})
}
}
completion: Completion::Incomplete,
priority: 0,
creation_date: None,
- description: "sample task +project rec:1y",
- tags: vec![Tag::Project("project"), Tag::Rec(rec.clone())],
+ description: Cow::from("sample task +project rec:1y"),
};
- assert_eq!(task.rec(), Some(&rec));
+ assert_eq!(task.rec(), Some(rec));
}
}
blob - 33bed8d1a6b05432b25add2ec1484285a2d4aa09
blob + 95c393ca1113f99f48105505fa56230299a75a51
--- tests/test.rs
+++ tests/test.rs
let input =
"2023-01-03 write tests for todotxt-parser +todotxt-parser @workstation due:2023-01-04";
let ts = parse_from_str(&input).unwrap();
- let tags1 = vec![
+ let tags = vec![vec![
Tag::Project("todotxt-parser"),
Tag::Context("workstation"),
Tag::Due(NaiveDate::from_ymd_opt(2023, 1, 4).unwrap()),
- ];
- let tags = vec![&tags1];
+ ]];
assert_eq!(
ts,
"2023-01-03 write tests for todotxt-parser +todotxt-parser @workstation due:2023-01-04\n\
x 2023-01-02 grocery shopping +groceries @supermarket due:2023-01-03";
let ts = parse_from_str(input).unwrap();
- let tags1 = vec![
- Tag::Project("todotxt-parser"),
- Tag::Context("workstation"),
- Tag::Due(NaiveDate::from_ymd_opt(2023, 1, 4).unwrap()),
+ let tags = vec![
+ vec![
+ Tag::Project("todotxt-parser"),
+ Tag::Context("workstation"),
+ Tag::Due(NaiveDate::from_ymd_opt(2023, 1, 4).unwrap()),
+ ],
+ vec![
+ Tag::Project("groceries"),
+ Tag::Context("supermarket"),
+ Tag::Due(NaiveDate::from_ymd_opt(2023, 1, 3).unwrap()),
+ ],
];
- let tags2 = vec![
- Tag::Project("groceries"),
- Tag::Context("supermarket"),
- Tag::Due(NaiveDate::from_ymd_opt(2023, 1, 3).unwrap()),
- ];
- let tags = vec![&tags1, &tags2];
assert_eq!(
ts,
blob - 69a61ac9e58924aa1b196262ccfc2ae3fdb240cd
blob + 16a05dcca83a4a55bc6d9ee3e5d3ed449c0bd86d
--- tests/types.rs
+++ tests/types.rs
"measure space for +chapelShelving @chapel due:2016-05-30"
)
);
- assert_eq!(res.tags(), &tags);
+ assert_eq!(res.tags(), tags);
}