diff --git a/src/issue.rs b/src/issue.rs index 222f474..06f959f 100644 --- a/src/issue.rs +++ b/src/issue.rs @@ -48,10 +48,6 @@ pub enum IssueError { ChronoParseError(#[from] chrono::format::ParseError), #[error("Failed to parse issue")] IssueParseError, - #[error("invalid escape character {escape:?} in tag file {filename:?}")] - TagInvalidEscape { escape: String, filename: String }, - #[error("invalid trailing escape character ',' in tag file {filename:?}")] - TagTrailingEscape { filename: String }, #[error("Failed to parse state")] StateParseError, #[error("Failed to run git")] @@ -141,7 +137,13 @@ impl Issue { } else if file_name == "dependencies" && direntry.metadata()?.is_dir() { dependencies = Self::read_dependencies(&direntry.path())?; } else if file_name == "tags" { - tags = Self::read_tags(&direntry)?; + let contents = std::fs::read_to_string(direntry.path())?; + tags = contents + .lines() + .filter(|s| s.len() > 0) + .map(|tag| String::from(tag.trim())) + .collect(); + tags.sort(); } else if file_name == "comments" && direntry.metadata()?.is_dir() { Self::read_comments(&mut comments, &direntry.path())?; } else { @@ -523,82 +525,12 @@ impl Issue { Ok(()) } - fn read_tags(tags_direntry: &std::fs::DirEntry) -> Result, IssueError> { - if !tags_direntry.metadata()?.is_dir() { - eprintln!("issue has old-style tags file"); - return Err(IssueError::IssueParseError); - } - let mut tags = Vec::::new(); - for direntry in tags_direntry.path().read_dir()? { - if let Ok(direntry) = direntry { - let tag = Issue::tag_from_filename(&direntry.file_name().to_string_lossy())?; - tags.push(tag); - } - } - tags.sort(); - Ok(tags) - } - - /// Perform un-escape on a filename to make it into a tag: - /// ",0" => "," - /// ",1" => "/" - fn tag_from_filename(filename: &str) -> Result { - let mut tag = String::new(); - let mut token_iter = filename.split(','); - let Some(start) = token_iter.next() else { - return Err(IssueError::StdIoError(std::io::Error::from( - std::io::ErrorKind::NotFound, - ))); - }; - tag.push_str(start); - for token in token_iter { - match token.chars().nth(0) { - Some('0') => { - tag.push(','); - tag.push_str(&token[1..]); - } - Some('1') => { - tag.push('/'); - tag.push_str(&token[1..]); - } - Some(bogus) => { - return Err(IssueError::TagInvalidEscape { - escape: String::from(bogus), - filename: String::from(filename), - }); - } - None => { - return Err(IssueError::TagTrailingEscape { - filename: String::from(filename), - }); - } - } - } - Ok(tag) - } - - // Perform escape on a tag to make it into a filename: - // "," => ",0" - // "/" => ",1" - fn tag_to_filename(tag: &str) -> String { - let mut filename = tag.replace(",", ",0"); - filename = filename.replace("/", ",1"); - return filename; - } - fn commit_tags(&self, commit_message: &str) -> Result<(), IssueError> { - let mut tags_dir_name = self.dir.clone(); - tags_dir_name.push("tags"); - match std::fs::remove_dir_all(&tags_dir_name) { - Err(e) if e.kind() == std::io::ErrorKind::NotFound => (), - Err(e) => return Err(e.into()), - Ok(_) => (), - } - std::fs::create_dir(&tags_dir_name)?; + let mut tags_filename = self.dir.clone(); + tags_filename.push("tags"); + let mut tags_file = std::fs::File::create(&tags_filename)?; for tag in &self.tags { - let mut tag_filename = tags_dir_name.clone(); - tag_filename.push(Issue::tag_to_filename(tag)); - std::fs::File::create(&tag_filename)?; + writeln!(tags_file, "{}", tag)?; } self.commit(commit_message)?; Ok(()) @@ -619,100 +551,6 @@ mod tests { use super::*; use pretty_assertions::assert_eq; - #[test] - fn tag_from_filename_0() { - assert_eq!( - Issue::tag_from_filename("hello").unwrap(), - String::from("hello") - ); - } - - #[test] - fn tag_from_filename_1() { - assert_eq!( - Issue::tag_from_filename("hello,0world").unwrap(), - String::from("hello,world") - ); - } - - #[test] - fn tag_from_filename_2() { - assert_eq!( - Issue::tag_from_filename("hello,1world").unwrap(), - String::from("hello/world") - ); - } - - #[test] - fn tag_from_filename_3() { - assert_eq!( - Issue::tag_from_filename(",0hello,1world,0").unwrap(), - String::from(",hello/world,") - ); - } - - #[test] - fn tag_from_filename_4() { - // std::io::Error does not impl PartialEq :-( - let filename = "hello,"; - match Issue::tag_from_filename(filename) { - Ok(tag) => panic!( - "tag_from_filename() accepted invalid input {:?} and returned {:?}", - filename, tag - ), - Err(_e) => (), - } - } - - #[test] - fn tag_from_filename_5() { - // std::io::Error does not impl PartialEq :-( - let filename = "hello,world"; - match Issue::tag_from_filename(filename) { - Ok(tag) => panic!( - "tag_from_filename() accepted invalid input {:?} and returned {:?}", - filename, tag - ), - Err(_e) => (), - } - } - - #[test] - fn tag_to_filename_0() { - let tag = "hello"; - assert_eq!(Issue::tag_to_filename(tag), "hello"); - } - - #[test] - fn tag_to_filename_1() { - let tag = "hello,"; - assert_eq!(Issue::tag_to_filename(tag), "hello,0"); - } - - #[test] - fn tag_to_filename_2() { - let tag = "/hello"; - assert_eq!(Issue::tag_to_filename(tag), ",1hello"); - } - - #[test] - fn tag_to_filename_3() { - let tag = "hello/bye,boo"; - assert_eq!(Issue::tag_to_filename(tag), "hello,1bye,0boo"); - } - - #[test] - fn tag_to_filename_4() { - let tag = ",,,///,,,"; - assert_eq!(Issue::tag_to_filename(tag), ",0,0,0,1,1,1,0,0,0"); - } - - #[test] - fn tag_to_filename_5() { - let tag = ",0,0,1,1"; - assert_eq!(Issue::tag_to_filename(tag), ",00,00,01,01"); - } - #[test] fn read_issue_0() { let issue_dir = std::path::Path::new("test/0000/3943fc5c173fdf41c0a22251593cd476/"); @@ -726,11 +564,6 @@ mod tests { done_time: None, tags: Vec::::from([ String::from("TAG2"), - String::from("bird/wing"), - String::from("bird/wing/feather"), - String::from("deer,antler"), - String::from("deer,antler,tassle"), - String::from("hop,scotch/shoe"), String::from("i-am-also-a-tag"), String::from("tag1"), ]), diff --git a/src/issues.rs b/src/issues.rs index 49d721d..d3c57c0 100644 --- a/src/issues.rs +++ b/src/issues.rs @@ -128,11 +128,6 @@ mod tests { done_time: None, tags: Vec::::from([ String::from("TAG2"), - String::from("bird/wing"), - String::from("bird/wing/feather"), - String::from("deer,antler"), - String::from("deer,antler,tassle"), - String::from("hop,scotch/shoe"), String::from("i-am-also-a-tag"), String::from("tag1"), ]), diff --git a/test/0000/3943fc5c173fdf41c0a22251593cd476/tags b/test/0000/3943fc5c173fdf41c0a22251593cd476/tags new file mode 100644 index 0000000..04e82a6 --- /dev/null +++ b/test/0000/3943fc5c173fdf41c0a22251593cd476/tags @@ -0,0 +1,3 @@ +tag1 +TAG2 +i-am-also-a-tag diff --git a/test/0000/3943fc5c173fdf41c0a22251593cd476/tags/TAG2 b/test/0000/3943fc5c173fdf41c0a22251593cd476/tags/TAG2 deleted file mode 100644 index e69de29..0000000 diff --git a/test/0000/3943fc5c173fdf41c0a22251593cd476/tags/bird,1wing b/test/0000/3943fc5c173fdf41c0a22251593cd476/tags/bird,1wing deleted file mode 100644 index e69de29..0000000 diff --git a/test/0000/3943fc5c173fdf41c0a22251593cd476/tags/bird,1wing,1feather b/test/0000/3943fc5c173fdf41c0a22251593cd476/tags/bird,1wing,1feather deleted file mode 100644 index e69de29..0000000 diff --git a/test/0000/3943fc5c173fdf41c0a22251593cd476/tags/deer,0antler b/test/0000/3943fc5c173fdf41c0a22251593cd476/tags/deer,0antler deleted file mode 100644 index e69de29..0000000 diff --git a/test/0000/3943fc5c173fdf41c0a22251593cd476/tags/deer,0antler,0tassle b/test/0000/3943fc5c173fdf41c0a22251593cd476/tags/deer,0antler,0tassle deleted file mode 100644 index e69de29..0000000 diff --git a/test/0000/3943fc5c173fdf41c0a22251593cd476/tags/hop,0scotch,1shoe b/test/0000/3943fc5c173fdf41c0a22251593cd476/tags/hop,0scotch,1shoe deleted file mode 100644 index e69de29..0000000 diff --git a/test/0000/3943fc5c173fdf41c0a22251593cd476/tags/i-am-also-a-tag b/test/0000/3943fc5c173fdf41c0a22251593cd476/tags/i-am-also-a-tag deleted file mode 100644 index e69de29..0000000 diff --git a/test/0000/3943fc5c173fdf41c0a22251593cd476/tags/tag1 b/test/0000/3943fc5c173fdf41c0a22251593cd476/tags/tag1 deleted file mode 100644 index e69de29..0000000 diff --git a/tools/update-tags-encoding b/tools/update-tags-encoding deleted file mode 100755 index 03b866e..0000000 --- a/tools/update-tags-encoding +++ /dev/null @@ -1,84 +0,0 @@ -#!/bin/bash -# -# Check out the specified branch (or `entomologist-data` if not specified) -# in a temporary worktree. -# -# For each issue with a `tags` file: -# -# replace the old-style tags file with a new-style tags dir -# -# git commit - -set -e -#set -x - -function escape_tag() { - TAG="$1" - TAG=$(echo "${TAG}" | sed -re 's/,/,0/g') - TAG=$(echo "${TAG}" | sed -re 's/\//,1/g') - echo "${TAG}" -} - -BRANCH="" - -if [[ -n "$1" ]] && [[ -d "$1" ]]; then - echo "updating ent db in directory '$1'" - pushd "$1" -else - if [[ -n "$1" ]]; then - # better be a branch - BRANCH="$1" - else - BRANCH="entomologist-data" - fi - echo "updating ent db in branch '${BRANCH}'" - WORKTREE_DIR=$(mktemp --directory) - git worktree add "${WORKTREE_DIR}" "${BRANCH}" - pushd "${WORKTREE_DIR}" > /dev/null -fi - -# Now our current working directory is the ent db that we're supposed -# to update. -# -# If $BRANCH is empty, we're in a directory not tracked by git and we -# just change the files. -# -# If $BRANCH is not empty, we're in a git worktree of the branch we're -# supposed to change, so we commit as we go. - -for ISSUE_ID in $(find . -maxdepth 1 -type d -regextype posix-extended -regex '\./[0-9a-f]{32}'); do - ISSUE_ID=$(basename "${ISSUE_ID}") - if ! [[ -f "${ISSUE_ID}/tags" ]]; then - continue - fi - - pushd "${ISSUE_ID}" > /dev/null - - echo "${ISSUE_ID} has tags:" - TAGS=$(cat tags) - echo "${TAGS}" - rm tags - - if [[ -n "${BRANCH}" ]]; then - git rm -f tags - fi - - mkdir tags - for TAG in ${TAGS}; do - TAG=$(escape_tag "${TAG}") - touch "tags/${TAG}" - done - - if [[ -n "${BRANCH}" ]]; then - git add tags - git commit -m "issue ${ISSUE_ID}: update tags to new format" - fi - - popd > /dev/null -done - -popd > /dev/null - -if [[ -n "${BRANCH}" ]]; then - git worktree remove "${WORKTREE_DIR}" -fi