From 6eacb405d90eb58a2ed33ade7298afc0b3ce1243 Mon Sep 17 00:00:00 2001 From: Sebastian Kuzminsky Date: Thu, 3 Jul 2025 12:14:26 -0600 Subject: [PATCH] WIP start adding Issue --- Cargo.toml | 1 + src/issue.rs | 109 ++++++++++++++++++ src/lib.rs | 2 +- .../description | 4 + .../title | 1 + .../state | 1 + .../title | 1 + 7 files changed, 118 insertions(+), 1 deletion(-) create mode 100644 src/issue.rs create mode 100644 test/0000/3943fc5c173fdf41c0a22251593cd476d96e6c9f/description create mode 100644 test/0000/3943fc5c173fdf41c0a22251593cd476d96e6c9f/title create mode 100644 test/0000/7792b063eef6d33e7da5dc1856750c149ba678c6/state create mode 100644 test/0000/7792b063eef6d33e7da5dc1856750c149ba678c6/title diff --git a/Cargo.toml b/Cargo.toml index ba3bd7b..905a9b5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,3 +4,4 @@ version = "0.1.0" edition = "2024" [dependencies] +thiserror = "2.0.11" diff --git a/src/issue.rs b/src/issue.rs new file mode 100644 index 0000000..6dde005 --- /dev/null +++ b/src/issue.rs @@ -0,0 +1,109 @@ +use std::str::FromStr; + +#[derive(Debug, PartialEq, serde::Deserialize)] +/// These are the states an issue can be in. +pub enum State { + New, + Backlog, + InProgress, + Done, + WontDo, +} + +#[derive(Debug, PartialEq)] +pub struct Issue { + pub title: String, + pub description: Option, + pub state: State, +} + +#[derive(Debug, thiserror::Error)] +pub enum ReadIssueError { + #[error(transparent)] + StdIoError(#[from] std::io::Error), + #[error("Failed to parse issue")] + IssueParseError, +} + +impl FromStr for State { + type Err = ReadIssueError; + fn from_str(s: &str) -> Result { + let s = s.to_lowercase(); + if s == "new" { + Ok(State::New) + } else if s == "backlog" { + Ok(State::Backlog) + } else if s == "inprogress" { + Ok(State::InProgress) + } else if s == "done" { + Ok(State::Done) + } else if s == "wontdo" { + Ok(State::WontDo) + } else { + Err(ReadIssueError::IssueParseError) + } + } +} + +impl Issue { + pub fn new_from_dir(dir: &std::path::Path) -> Result { + let mut title: Option = None; + let mut description: Option = None; + let mut state = State::New; // default state, if not specified in the issue + + for direntry in dir.read_dir()? { + if let Ok(direntry) = direntry { + let file_name = direntry.file_name(); + if file_name == "title" { + title = Some(std::fs::read_to_string(direntry.path())?.trim().into()); + } else if file_name == "description" { + description = Some(std::fs::read_to_string(direntry.path())?); + } else if file_name == "state" { + let state_string = std::fs::read_to_string(direntry.path())?; + state = State::from_str(state_string.trim())?; + } else { + println!("ignoring unknown file in issue directory: {:?}", file_name); + } + } + } + + if title == None { + return Err(ReadIssueError::IssueParseError); + } + + Ok(Self { + title: title.unwrap(), + description: description, + state: state, + }) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn read_issue_0() { + let issue_dir = std::path::Path::new("test/0000/3943fc5c173fdf41c0a22251593cd476d96e6c9f/"); + let issue = Issue::new_from_dir(issue_dir).unwrap(); + let expected = Issue { + title: String::from("this is the title of my issue"), + description: Some(String::from("This is the description of my issue.\nIt is multiple lines.\n* Arbitrary contents\n* But let's use markdown by convention\n")), + state: State::New, + }; + assert_eq!(issue, expected); + } + + #[test] + fn read_issue_1() { + let issue_dir = std::path::Path::new("test/0000/7792b063eef6d33e7da5dc1856750c149ba678c6/"); + let issue = Issue::new_from_dir(issue_dir).unwrap(); + let expected = Issue { + title: String::from("minimal"), + description: None, + state: State::InProgress, + }; + assert_eq!(issue, expected); + } +} diff --git a/src/lib.rs b/src/lib.rs index 8b13789..d93d369 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1 +1 @@ - +pub mod issue; diff --git a/test/0000/3943fc5c173fdf41c0a22251593cd476d96e6c9f/description b/test/0000/3943fc5c173fdf41c0a22251593cd476d96e6c9f/description new file mode 100644 index 0000000..3db0fcf --- /dev/null +++ b/test/0000/3943fc5c173fdf41c0a22251593cd476d96e6c9f/description @@ -0,0 +1,4 @@ +This is the description of my issue. +It is multiple lines. +* Arbitrary contents +* But let's use markdown by convention diff --git a/test/0000/3943fc5c173fdf41c0a22251593cd476d96e6c9f/title b/test/0000/3943fc5c173fdf41c0a22251593cd476d96e6c9f/title new file mode 100644 index 0000000..c9c2379 --- /dev/null +++ b/test/0000/3943fc5c173fdf41c0a22251593cd476d96e6c9f/title @@ -0,0 +1 @@ +this is the title of my issue diff --git a/test/0000/7792b063eef6d33e7da5dc1856750c149ba678c6/state b/test/0000/7792b063eef6d33e7da5dc1856750c149ba678c6/state new file mode 100644 index 0000000..0737713 --- /dev/null +++ b/test/0000/7792b063eef6d33e7da5dc1856750c149ba678c6/state @@ -0,0 +1 @@ +inprogress diff --git a/test/0000/7792b063eef6d33e7da5dc1856750c149ba678c6/title b/test/0000/7792b063eef6d33e7da5dc1856750c149ba678c6/title new file mode 100644 index 0000000..dd1a932 --- /dev/null +++ b/test/0000/7792b063eef6d33e7da5dc1856750c149ba678c6/title @@ -0,0 +1 @@ +minimal