From 8319a4f118d5735c33e377b71d7cb689d2cdffb8 Mon Sep 17 00:00:00 2001 From: sigil-03 Date: Fri, 18 Jul 2025 16:20:17 -0600 Subject: [PATCH] add dependency API / fix dependency representation / dependency management via CLI --- src/bin/ent/main.rs | 45 ++++++++++++++++++++++++++- src/issue.rs | 74 +++++++++++++++++++++++++++++++++++++++------ 2 files changed, 109 insertions(+), 10 deletions(-) diff --git a/src/bin/ent/main.rs b/src/bin/ent/main.rs index 1698954..4118ed3 100644 --- a/src/bin/ent/main.rs +++ b/src/bin/ent/main.rs @@ -99,6 +99,12 @@ enum Commands { issue_id: String, done_time: Option, }, + + /// get or add a dependency to the issue + Depend { + issue_id: String, + dependency_id: Option, + }, } fn handle_command( @@ -510,6 +516,43 @@ fn handle_command( }, }; } + + Commands::Depend { + issue_id, + dependency_id, + } => match dependency_id { + Some(dep_id) => { + let ent_db = entomologist::database::make_issues_database( + issues_database_source, + entomologist::database::IssuesDatabaseAccess::ReadWrite, + )?; + let mut issues = entomologist::issues::Issues::new_from_dir(&ent_db.dir)?; + if issues.issues.contains_key(dep_id) { + if let Some(issue) = issues.issues.get_mut(issue_id) { + issue.add_dependency(dep_id.clone())?; + } else { + Err(anyhow::anyhow!("issue {} not found", issue_id))?; + }; + } else { + Err(anyhow::anyhow!("dependency {} not found", dep_id))?; + }; + } + None => { + let ent_db = entomologist::database::read_issues_database(issues_database_source)?; + + let Some(issue) = ent_db.issues.get(issue_id) else { + Err(anyhow::anyhow!("issue {} not found", issue_id))? + }; + println!("DEPENDENCIES:"); + if let Some(list) = &issue.dependencies { + for dependency in list { + println!("{}", dependency); + } + } else { + println!("NONE"); + } + } + }, } Ok(()) @@ -531,7 +574,7 @@ fn main() -> anyhow::Result<()> { (Some(_), Some(_)) => { return Err(anyhow::anyhow!( "don't specify both `--issues-dir` and `--issues-branch`" - )) + )); } }; diff --git a/src/issue.rs b/src/issue.rs index bc3d959..363e2b7 100644 --- a/src/issue.rs +++ b/src/issue.rs @@ -62,6 +62,12 @@ pub enum IssueError { StdioIsNotTerminal, #[error("Failed to parse issue ID")] IdError, + #[error("Dependency not found")] + DepNotFound, + #[error("Dependency already exists")] + DepExists, + #[error("Self-dependency not allowed")] + DepSelf, } impl FromStr for State { @@ -128,15 +134,8 @@ impl Issue { std::fs::read_to_string(direntry.path())?.trim(), )?; done_time = Some(raw_done_time.into()); - } else if file_name == "dependencies" { - let dep_strings = std::fs::read_to_string(direntry.path())?; - let deps: Vec = dep_strings - .lines() - .map(|dep| IssueHandle::from(dep)) - .collect(); - if deps.len() > 0 { - dependencies = Some(deps); - } + } else if file_name == "dependencies" && direntry.metadata()?.is_dir() { + dependencies = Self::read_dependencies(&direntry.path())?; } else if file_name == "tags" { let contents = std::fs::read_to_string(direntry.path())?; tags = contents @@ -199,6 +198,23 @@ impl Issue { Ok(()) } + fn read_dependencies(dir: &std::path::Path) -> Result>, IssueError> { + let mut dependencies: Option> = None; + for direntry in dir.read_dir()? { + if let Ok(direntry) = direntry { + match &mut dependencies { + Some(deps) => { + deps.push(direntry.file_name().into_string().unwrap()); + } + None => { + dependencies = Some(vec![direntry.file_name().into_string().unwrap()]); + } + } + } + } + Ok(dependencies) + } + /// Add a new Comment to the Issue. Commits. pub fn add_comment( &mut self, @@ -392,6 +408,46 @@ impl Issue { } return false; } + + pub fn add_dependency(&mut self, dep: IssueHandle) -> Result<(), IssueError> { + if self.id == dep { + Err(IssueError::DepSelf)?; + } + match &mut self.dependencies { + Some(v) => v.push(dep.clone()), + None => self.dependencies = Some(vec![dep.clone()]), + } + let mut dir = std::path::PathBuf::from(&self.dir); + dir.push("dependencies"); + if !dir.exists() { + std::fs::create_dir(&dir)?; + } + + dir.push(dep.clone()); + + if !dir.exists() { + std::fs::File::create(&dir)?; + self.commit(&format!("add dep {} to issue {}", dep, self.id))?; + } else { + Err(IssueError::DepExists)?; + } + Ok(()) + } + + pub fn remove_dependency(&mut self, dep: IssueHandle) -> Result<(), IssueError> { + match &mut self.dependencies { + Some(v) => { + if let Some(i) = v.iter().position(|d| d == &dep) { + v.remove(i); + } else { + Err(IssueError::DepNotFound)?; + } + } + None => Err(IssueError::DepNotFound)?, + } + self.commit(&format!("remove dep {} from issue {}", dep, self.id))?; + Ok(()) + } } // This is the internal/private API of Issue.