From 0e60bcedaf38fecabc38e99369a74338a1ef86ca Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Sat, 5 Jun 2021 20:57:28 -0400 Subject: [PATCH] hide the implementation of Tag --- taskchampion/src/task/mod.rs | 2 -- taskchampion/src/task/tag.rs | 50 +++++++++++++++++++++++++++-------- taskchampion/src/task/task.rs | 19 ++++++------- 3 files changed, 49 insertions(+), 22 deletions(-) diff --git a/taskchampion/src/task/mod.rs b/taskchampion/src/task/mod.rs index 6548bf404..ecb755c29 100644 --- a/taskchampion/src/task/mod.rs +++ b/taskchampion/src/task/mod.rs @@ -13,6 +13,4 @@ pub use status::Status; pub use tag::{Tag, INVALID_TAG_CHARACTERS}; pub use task::{Task, TaskMut}; -use tag::SyntheticTag; - pub type Timestamp = DateTime; diff --git a/taskchampion/src/task/tag.rs b/taskchampion/src/task/tag.rs index 2a60a66c9..d8689e6af 100644 --- a/taskchampion/src/task/tag.rs +++ b/taskchampion/src/task/tag.rs @@ -10,7 +10,11 @@ use std::str::FromStr; /// This definition is based on [that of /// TaskWarrior](https://github.com/GothenburgBitFactory/taskwarrior/blob/663c6575ceca5bd0135ae884879339dac89d3142/src/Lexer.cpp#L146-L164). #[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)] -pub enum Tag { +pub struct Tag(TagInner); + +/// Inner type to hide the implementation +#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)] +pub(super) enum TagInner { User(String), Synthetic(SyntheticTag), } @@ -18,7 +22,7 @@ pub enum Tag { pub const INVALID_TAG_CHARACTERS: &str = "+-*/(<>^! %=~"; impl Tag { - fn from_str(value: &str) -> Result { + pub fn from_str(value: &str) -> Result { fn err(value: &str) -> Result { anyhow::bail!("invalid tag {:?}", value) } @@ -26,7 +30,7 @@ impl Tag { // first, look for synthetic tags if value.chars().all(|c| c.is_ascii_uppercase()) { if let Ok(st) = SyntheticTag::from_str(value) { - return Ok(Self::Synthetic(st)); + return Ok(Self(TagInner::Synthetic(st))); } // all uppercase, but not a valid synthetic tag return err(value); @@ -46,7 +50,29 @@ impl Tag { { return err(value); } - Ok(Self::User(String::from(value))) + Ok(Self(TagInner::User(String::from(value)))) + } + + /// True if this tag is a synthetic tag + pub fn is_synthetic(&self) -> bool { + if let TagInner::Synthetic(_) = self.0 { + true + } else { + false + } + } + + /// True if this tag is a user-provided tag (not synthetic) + pub fn is_user(&self) -> bool { + !self.is_synthetic() + } + + pub(super) fn inner(&self) -> &TagInner { + &self.0 + } + + pub(super) fn from_inner(inner: TagInner) -> Self { + Self(inner) } } @@ -68,22 +94,24 @@ impl TryFrom<&String> for Tag { impl fmt::Display for Tag { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::User(s) => s.fmt(f), - Self::Synthetic(st) => st.as_ref().fmt(f), + match &self.0 { + TagInner::User(s) => s.fmt(f), + TagInner::Synthetic(st) => st.as_ref().fmt(f), } } } impl AsRef for Tag { fn as_ref(&self) -> &str { - match self { - Self::User(s) => s.as_ref(), - Self::Synthetic(st) => st.as_ref(), + match &self.0 { + TagInner::User(s) => s.as_ref(), + TagInner::Synthetic(st) => st.as_ref(), } } } +/// A synthetic tag, represented as an `enum`. This type is used directly by +/// [`taskchampion::task::task`] for efficiency. #[derive( Debug, Clone, @@ -97,7 +125,7 @@ impl AsRef for Tag { strum_macros::EnumIter, )] #[strum(serialize_all = "SCREAMING_SNAKE_CASE")] -pub enum SyntheticTag { +pub(super) enum SyntheticTag { Waiting, Active, } diff --git a/taskchampion/src/task/task.rs b/taskchampion/src/task/task.rs index c3678f730..9695247cf 100644 --- a/taskchampion/src/task/task.rs +++ b/taskchampion/src/task/task.rs @@ -1,4 +1,5 @@ -use super::{Status, SyntheticTag, Tag}; +use super::tag::{SyntheticTag, TagInner}; +use super::{Status, Tag}; use crate::replica::Replica; use crate::storage::TaskMap; use chrono::prelude::*; @@ -100,9 +101,9 @@ impl Task { /// Check if this task has the given tag pub fn has_tag(&self, tag: &Tag) -> bool { - match tag { - Tag::User(s) => self.taskmap.contains_key(&format!("tag.{}", s)), - Tag::Synthetic(st) => self.has_synthetic_tag(st), + match tag.inner() { + TagInner::User(s) => self.taskmap.contains_key(&format!("tag.{}", s)), + TagInner::Synthetic(st) => self.has_synthetic_tag(st), } } @@ -124,7 +125,7 @@ impl Task { .chain( SyntheticTag::iter() .filter(move |st| self.has_synthetic_tag(st)) - .map(|st| Tag::Synthetic(st)), + .map(|st| Tag::from_inner(TagInner::Synthetic(st))), ) } @@ -203,7 +204,7 @@ impl<'r> TaskMut<'r> { /// Add a tag to this task. Does nothing if the tag is already present. pub fn add_tag(&mut self, tag: &Tag) -> anyhow::Result<()> { - if let Tag::Synthetic(_) = tag { + if tag.is_synthetic() { anyhow::bail!("Synthetic tags cannot be modified"); } self.set_string(format!("tag.{}", tag), Some("".to_owned())) @@ -211,7 +212,7 @@ impl<'r> TaskMut<'r> { /// Remove a tag from this task. Does nothing if the tag is not present. pub fn remove_tag(&mut self, tag: &Tag) -> anyhow::Result<()> { - if let Tag::Synthetic(_) = tag { + if tag.is_synthetic() { anyhow::bail!("Synthetic tags cannot be modified"); } self.set_string(format!("tag.{}", tag), None) @@ -301,12 +302,12 @@ mod test { /// Create a user tag, without checking its validity fn utag(name: &'static str) -> Tag { - Tag::User(name.into()) + Tag::from_inner(TagInner::User(name.into())) } /// Create a synthetic tag fn stag(synth: SyntheticTag) -> Tag { - Tag::Synthetic(synth) + Tag::from_inner(TagInner::Synthetic(synth)) } #[test]