Compare commits
10 commits
f5055a9ac7
...
e09e4b9cb7
| Author | SHA1 | Date | |
|---|---|---|---|
| e09e4b9cb7 | |||
| acf539c683 | |||
| a199fbc7f7 | |||
| fc658009f5 | |||
| 211bf92dde | |||
| bfdf6178f4 | |||
| 16de030b8e | |||
| ac72251e0e | |||
| 1509c42734 | |||
| ca353352f8 |
4 changed files with 169 additions and 25 deletions
|
|
@ -132,27 +132,40 @@ fn handle_command(args: &Args, issues_dir: &std::path::Path) -> anyhow::Result<(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Commands::New {
|
Commands::New { description } => {
|
||||||
description: Some(description),
|
|
||||||
} => {
|
|
||||||
let mut issue = entomologist::issue::Issue::new(issues_dir)?;
|
let mut issue = entomologist::issue::Issue::new(issues_dir)?;
|
||||||
issue.set_description(description)?;
|
let r = match description {
|
||||||
println!("created new issue '{}'", issue.title());
|
Some(description) => issue.set_description(description),
|
||||||
}
|
None => issue.edit_description(),
|
||||||
|
};
|
||||||
Commands::New { description: None } => {
|
match r {
|
||||||
let mut issue = entomologist::issue::Issue::new(issues_dir)?;
|
Err(entomologist::issue::IssueError::EmptyDescription) => {
|
||||||
issue.edit_description()?;
|
println!("no new issue created");
|
||||||
println!("created new issue '{}'", issue.title());
|
return Ok(());
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
return Err(e.into());
|
||||||
|
}
|
||||||
|
Ok(()) => {
|
||||||
|
println!("created new issue '{}'", issue.title());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Commands::Edit { issue_id } => {
|
Commands::Edit { issue_id } => {
|
||||||
let mut issues =
|
let mut issues =
|
||||||
entomologist::issues::Issues::new_from_dir(std::path::Path::new(issues_dir))?;
|
entomologist::issues::Issues::new_from_dir(std::path::Path::new(issues_dir))?;
|
||||||
match issues.get_mut_issue(issue_id) {
|
match issues.get_mut_issue(issue_id) {
|
||||||
Some(issue) => {
|
Some(issue) => match issue.edit_description() {
|
||||||
issue.edit_description()?;
|
Err(entomologist::issue::IssueError::EmptyDescription) => {
|
||||||
}
|
println!("aborted issue edit");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
return Err(e.into());
|
||||||
|
}
|
||||||
|
Ok(()) => (),
|
||||||
|
},
|
||||||
None => {
|
None => {
|
||||||
return Err(anyhow::anyhow!("issue {} not found", issue_id));
|
return Err(anyhow::anyhow!("issue {} not found", issue_id));
|
||||||
}
|
}
|
||||||
|
|
@ -227,12 +240,20 @@ fn handle_command(args: &Args, issues_dir: &std::path::Path) -> anyhow::Result<(
|
||||||
return Err(anyhow::anyhow!("issue {} not found", issue_id));
|
return Err(anyhow::anyhow!("issue {} not found", issue_id));
|
||||||
};
|
};
|
||||||
let mut comment = issue.new_comment()?;
|
let mut comment = issue.new_comment()?;
|
||||||
match description {
|
let r = match description {
|
||||||
Some(description) => {
|
Some(description) => comment.set_description(description),
|
||||||
comment.set_description(description)?;
|
None => comment.edit_description(),
|
||||||
|
};
|
||||||
|
match r {
|
||||||
|
Err(entomologist::comment::CommentError::EmptyDescription) => {
|
||||||
|
println!("aborted new comment");
|
||||||
|
return Ok(());
|
||||||
}
|
}
|
||||||
None => {
|
Err(e) => {
|
||||||
comment.edit_description()?;
|
return Err(e.into());
|
||||||
|
}
|
||||||
|
Ok(()) => {
|
||||||
|
println!("created new comment {}", &comment.uuid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,8 @@ pub enum CommentError {
|
||||||
GitError(#[from] crate::git::GitError),
|
GitError(#[from] crate::git::GitError),
|
||||||
#[error("Failed to run editor")]
|
#[error("Failed to run editor")]
|
||||||
EditorError,
|
EditorError,
|
||||||
|
#[error("supplied description is empty")]
|
||||||
|
EmptyDescription,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Comment {
|
impl Comment {
|
||||||
|
|
@ -60,6 +62,9 @@ impl Comment {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_description(&mut self, description: &str) -> Result<(), CommentError> {
|
pub fn set_description(&mut self, description: &str) -> Result<(), CommentError> {
|
||||||
|
if description.len() == 0 {
|
||||||
|
return Err(CommentError::EmptyDescription);
|
||||||
|
}
|
||||||
self.description = String::from(description);
|
self.description = String::from(description);
|
||||||
let mut description_filename = std::path::PathBuf::from(&self.dir);
|
let mut description_filename = std::path::PathBuf::from(&self.dir);
|
||||||
description_filename.push("description");
|
description_filename.push("description");
|
||||||
|
|
@ -79,6 +84,7 @@ impl Comment {
|
||||||
pub fn edit_description(&mut self) -> Result<(), CommentError> {
|
pub fn edit_description(&mut self) -> Result<(), CommentError> {
|
||||||
let mut description_filename = std::path::PathBuf::from(&self.dir);
|
let mut description_filename = std::path::PathBuf::from(&self.dir);
|
||||||
description_filename.push("description");
|
description_filename.push("description");
|
||||||
|
let exists = description_filename.exists();
|
||||||
let result = std::process::Command::new("vi")
|
let result = std::process::Command::new("vi")
|
||||||
.arg(&description_filename.as_mut_os_str())
|
.arg(&description_filename.as_mut_os_str())
|
||||||
.spawn()?
|
.spawn()?
|
||||||
|
|
@ -88,8 +94,31 @@ impl Comment {
|
||||||
println!("stderr: {}", std::str::from_utf8(&result.stderr).unwrap());
|
println!("stderr: {}", std::str::from_utf8(&result.stderr).unwrap());
|
||||||
return Err(CommentError::EditorError);
|
return Err(CommentError::EditorError);
|
||||||
}
|
}
|
||||||
crate::git::git_commit_file(&description_filename)?;
|
if description_filename.exists() && description_filename.metadata()?.len() > 0 {
|
||||||
self.read_description()?;
|
crate::git::add_file(&description_filename)?;
|
||||||
|
} else {
|
||||||
|
// User saved an empty file, which means they changed their
|
||||||
|
// mind and no longer want to edit the description.
|
||||||
|
if exists {
|
||||||
|
crate::git::restore_file(&description_filename)?;
|
||||||
|
}
|
||||||
|
return Err(CommentError::EmptyDescription);
|
||||||
|
}
|
||||||
|
if crate::git::worktree_is_dirty(&self.dir.to_string_lossy())? {
|
||||||
|
crate::git::commit(
|
||||||
|
&description_filename.parent().unwrap(),
|
||||||
|
&format!(
|
||||||
|
"new description for comment {}",
|
||||||
|
description_filename
|
||||||
|
.parent()
|
||||||
|
.unwrap()
|
||||||
|
.file_name()
|
||||||
|
.unwrap()
|
||||||
|
.to_string_lossy()
|
||||||
|
),
|
||||||
|
)?;
|
||||||
|
self.read_description()?;
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
69
src/git.rs
69
src/git.rs
|
|
@ -20,9 +20,24 @@ pub struct Worktree {
|
||||||
|
|
||||||
impl Drop for Worktree {
|
impl Drop for Worktree {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
let _result = std::process::Command::new("git")
|
let result = std::process::Command::new("git")
|
||||||
.args(["worktree", "remove", &self.path.path().to_string_lossy()])
|
.args([
|
||||||
|
"worktree",
|
||||||
|
"remove",
|
||||||
|
"--force",
|
||||||
|
&self.path.path().to_string_lossy(),
|
||||||
|
])
|
||||||
.output();
|
.output();
|
||||||
|
match result {
|
||||||
|
Err(e) => {
|
||||||
|
println!("failed to run git: {:#?}", e);
|
||||||
|
}
|
||||||
|
Ok(result) => {
|
||||||
|
if !result.status.success() {
|
||||||
|
println!("failed to remove git worktree: {:#?}", result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -91,6 +106,56 @@ pub fn git_branch_exists(branch: &str) -> Result<bool, GitError> {
|
||||||
return Ok(result.status.success());
|
return Ok(result.status.success());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn worktree_is_dirty(dir: &str) -> Result<bool, GitError> {
|
||||||
|
// `git status --porcelain` prints a terse list of files added or
|
||||||
|
// modified (both staged and not), and new untracked files. So if
|
||||||
|
// says *anything at all* it means the worktree is dirty.
|
||||||
|
let result = std::process::Command::new("git")
|
||||||
|
.args(["status", "--porcelain", "--untracked-files=no"])
|
||||||
|
.current_dir(dir)
|
||||||
|
.output()?;
|
||||||
|
return Ok(result.stdout.len() > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_file(file: &std::path::Path) -> Result<(), GitError> {
|
||||||
|
let result = std::process::Command::new("git")
|
||||||
|
.args(["add", &file.to_string_lossy()])
|
||||||
|
.current_dir(file.parent().unwrap())
|
||||||
|
.output()?;
|
||||||
|
if !result.status.success() {
|
||||||
|
println!("stdout: {}", std::str::from_utf8(&result.stdout).unwrap());
|
||||||
|
println!("stderr: {}", std::str::from_utf8(&result.stderr).unwrap());
|
||||||
|
return Err(GitError::Oops);
|
||||||
|
}
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn restore_file(file: &std::path::Path) -> Result<(), GitError> {
|
||||||
|
let result = std::process::Command::new("git")
|
||||||
|
.args(["restore", &file.to_string_lossy()])
|
||||||
|
.current_dir(file.parent().unwrap())
|
||||||
|
.output()?;
|
||||||
|
if !result.status.success() {
|
||||||
|
println!("stdout: {}", std::str::from_utf8(&result.stdout).unwrap());
|
||||||
|
println!("stderr: {}", std::str::from_utf8(&result.stderr).unwrap());
|
||||||
|
return Err(GitError::Oops);
|
||||||
|
}
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn commit(dir: &std::path::Path, msg: &str) -> Result<(), GitError> {
|
||||||
|
let result = std::process::Command::new("git")
|
||||||
|
.args(["commit", "-m", msg])
|
||||||
|
.current_dir(dir)
|
||||||
|
.output()?;
|
||||||
|
if !result.status.success() {
|
||||||
|
println!("stdout: {}", std::str::from_utf8(&result.stdout).unwrap());
|
||||||
|
println!("stderr: {}", std::str::from_utf8(&result.stderr).unwrap());
|
||||||
|
return Err(GitError::Oops);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn git_commit_file(file: &std::path::Path) -> Result<(), GitError> {
|
pub fn git_commit_file(file: &std::path::Path) -> Result<(), GitError> {
|
||||||
let mut git_dir = std::path::PathBuf::from(file);
|
let mut git_dir = std::path::PathBuf::from(file);
|
||||||
git_dir.pop();
|
git_dir.pop();
|
||||||
|
|
|
||||||
33
src/issue.rs
33
src/issue.rs
|
|
@ -47,6 +47,8 @@ pub enum IssueError {
|
||||||
GitError(#[from] crate::git::GitError),
|
GitError(#[from] crate::git::GitError),
|
||||||
#[error("Failed to run editor")]
|
#[error("Failed to run editor")]
|
||||||
EditorError,
|
EditorError,
|
||||||
|
#[error("supplied description is empty")]
|
||||||
|
EmptyDescription,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromStr for State {
|
impl FromStr for State {
|
||||||
|
|
@ -195,6 +197,9 @@ impl Issue {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_description(&mut self, description: &str) -> Result<(), IssueError> {
|
pub fn set_description(&mut self, description: &str) -> Result<(), IssueError> {
|
||||||
|
if description.len() == 0 {
|
||||||
|
return Err(IssueError::EmptyDescription);
|
||||||
|
}
|
||||||
self.description = String::from(description);
|
self.description = String::from(description);
|
||||||
let mut description_filename = std::path::PathBuf::from(&self.dir);
|
let mut description_filename = std::path::PathBuf::from(&self.dir);
|
||||||
description_filename.push("description");
|
description_filename.push("description");
|
||||||
|
|
@ -214,6 +219,7 @@ impl Issue {
|
||||||
pub fn edit_description(&mut self) -> Result<(), IssueError> {
|
pub fn edit_description(&mut self) -> Result<(), IssueError> {
|
||||||
let mut description_filename = std::path::PathBuf::from(&self.dir);
|
let mut description_filename = std::path::PathBuf::from(&self.dir);
|
||||||
description_filename.push("description");
|
description_filename.push("description");
|
||||||
|
let exists = description_filename.exists();
|
||||||
let result = std::process::Command::new("vi")
|
let result = std::process::Command::new("vi")
|
||||||
.arg(&description_filename.as_mut_os_str())
|
.arg(&description_filename.as_mut_os_str())
|
||||||
.spawn()?
|
.spawn()?
|
||||||
|
|
@ -223,8 +229,31 @@ impl Issue {
|
||||||
println!("stderr: {}", std::str::from_utf8(&result.stderr).unwrap());
|
println!("stderr: {}", std::str::from_utf8(&result.stderr).unwrap());
|
||||||
return Err(IssueError::EditorError);
|
return Err(IssueError::EditorError);
|
||||||
}
|
}
|
||||||
crate::git::git_commit_file(&description_filename)?;
|
if description_filename.exists() && description_filename.metadata()?.len() > 0 {
|
||||||
self.read_description()?;
|
crate::git::add_file(&description_filename)?;
|
||||||
|
} else {
|
||||||
|
// User saved an empty file, which means they changed their
|
||||||
|
// mind and no longer want to edit the description.
|
||||||
|
if exists {
|
||||||
|
crate::git::restore_file(&description_filename)?;
|
||||||
|
}
|
||||||
|
return Err(IssueError::EmptyDescription);
|
||||||
|
}
|
||||||
|
if crate::git::worktree_is_dirty(&self.dir.to_string_lossy())? {
|
||||||
|
crate::git::commit(
|
||||||
|
&description_filename.parent().unwrap(),
|
||||||
|
&format!(
|
||||||
|
"new description for issue {}",
|
||||||
|
description_filename
|
||||||
|
.parent()
|
||||||
|
.unwrap()
|
||||||
|
.file_name()
|
||||||
|
.unwrap()
|
||||||
|
.to_string_lossy()
|
||||||
|
),
|
||||||
|
)?;
|
||||||
|
self.read_description()?;
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue