hide the implementation of Tag

This commit is contained in:
Dustin J. Mitchell 2021-06-05 20:57:28 -04:00
parent ff23c9148b
commit 0e60bcedaf
3 changed files with 49 additions and 22 deletions

View file

@ -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<Utc>;

View file

@ -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<Tag, anyhow::Error> {
pub fn from_str(value: &str) -> Result<Tag, anyhow::Error> {
fn err(value: &str) -> Result<Tag, anyhow::Error> {
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<str> 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<str> for Tag {
strum_macros::EnumIter,
)]
#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
pub enum SyntheticTag {
pub(super) enum SyntheticTag {
Waiting,
Active,
}

View file

@ -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]