refactor: git: use thiserror and cleanup errors

This commit is contained in:
Paul Campbell 2024-06-03 08:04:48 +01:00
parent 0b8e41a8ec
commit 621e599b31
11 changed files with 148 additions and 121 deletions

View file

@ -41,6 +41,7 @@ secrecy = { workspace = true }
# error handling # error handling
derive_more = { workspace = true } derive_more = { workspace = true }
derive-with = { workspace = true } derive-with = { workspace = true }
thiserror = { workspace = true }
# # file watcher # # file watcher
# inotify = { workspace = true } # inotify = { workspace = true }

View file

@ -4,21 +4,26 @@ use git_next_config as config;
pub type Result<T> = core::result::Result<T, Error>; pub type Result<T> = core::result::Result<T, Error>;
#[derive(Debug, derive_more::Display, derive_more::From)] #[derive(Debug, thiserror::Error)]
pub enum Error { pub enum Error {
Network(kxio::network::NetworkError), #[error("network: {0}")]
Network(#[from] kxio::network::NetworkError),
Fetch(git::fetch::Error), #[error("fetch: {0}")]
Fetch(#[from] git::fetch::Error),
Push(git::push::Error), #[error("push: {0}")]
Push(#[from] git::push::Error),
#[error("lock")]
Lock, Lock,
Generic(String), #[error("gix iter: {0}")]
I1(gix::reference::iter::Error), GixIter(#[from] gix::reference::iter::Error),
I2(gix::reference::iter::init::Error),
#[error("gix iter init: {0}")]
GixIterInit(#[from] gix::reference::iter::init::Error),
} }
impl std::error::Error for Error {}
pub fn reset( pub fn reset(
repository: &git::OpenRepository, repository: &git::OpenRepository,

View file

@ -39,14 +39,21 @@ pub struct Histories {
} }
pub mod log { pub mod log {
use derive_more::{Display, From};
pub type Result<T> = core::result::Result<T, Error>; pub type Result<T> = core::result::Result<T, Error>;
#[derive(Debug, Display, From)] #[derive(Debug, thiserror::Error)]
pub enum Error { pub enum Error {
#[error("gix: {0}")]
Gix(String), Gix(String),
#[error("lock")]
Lock, Lock,
} }
impl std::error::Error for Error {}
impl From<String> for Error {
fn from(e: String) -> Self {
Self::Gix(e)
}
}
} }

View file

@ -1,20 +1,20 @@
#[derive(Debug, derive_more::Display)] #[derive(Debug, thiserror::Error)]
pub enum Error { pub enum Error {
#[error("unable to open repo: {0}")]
UnableToOpenRepo(String), UnableToOpenRepo(String),
#[error("no remote found")]
NoFetchRemoteFound, NoFetchRemoteFound,
#[error("remote connect: {0}")]
Connect(String), Connect(String),
Fetch(String),
#[error("prepare: {0}")]
Prepare(String),
#[error("receive: {0}")]
Receive(String),
#[error("lock")]
Lock, Lock,
} }
impl std::error::Error for Error {}
mod gix_error {
#![cfg(not(tarpaulin_include))] // third-party library errors
use super::Error;
impl From<gix::remote::connect::Error> for Error {
fn from(gix_error: gix::remote::connect::Error) -> Self {
Self::Connect(gix_error.to_string())
}
}
}

View file

@ -1,54 +1,57 @@
// //
pub type Result<T> = core::result::Result<T, Error>; pub type Result<T> = core::result::Result<T, Error>;
#[derive(Debug, derive_more::Display)] #[derive(Debug, thiserror::Error)]
pub enum Error { pub enum Error {
#[error("lock")]
Lock, Lock,
#[display("File not found: {}", 0)] #[error("File not found: {}", 0)]
NotFound(String), NotFound(String),
#[display("Unable to parse file contents")] #[error("Unable to parse file contents")]
ParseContent, ParseContent,
#[display("Unable to decode from base64")] #[error("Unable to decode from base64")]
DecodeFromBase64, DecodeFromBase64,
#[display("Unable to decode from UTF-8")] #[error("Unable to decode from UTF-8")]
DecodeFromUtf8, DecodeFromUtf8,
#[display("Unknown file encoding: {}", 0)] #[error("Unknown file encoding: {}", 0)]
UnknownEncoding(String), UnknownEncoding(String),
#[display("Not a file: {}", 0)] #[error("Not a file: {}", 0)]
NotFile(String), NotFile(String),
#[display("Unknown error (status: {})", 0)] #[error("Unknown error (status: {})", 0)]
Unknown(String), Unknown(String),
CommitLog(crate::commit::log::Error), #[error("commit log: {0}")]
CommitLog(#[from] crate::commit::log::Error),
#[error("commit not found")]
CommitNotFound, CommitNotFound,
#[error("no tree in commit")]
NoTreeInCommit(String), NoTreeInCommit(String),
#[error("no .git-next.toml file found in repo")]
NoGitNextToml, NoGitNextToml,
#[error("find reference: {0}")]
FindReference(String), FindReference(String),
#[error("find object: {0}")]
FindObject(String), FindObject(String),
#[error("Non-UTF-8 in blob: {0}")]
NonUtf8Blob(String), NonUtf8Blob(String),
#[error("try id")]
TryId, TryId,
} }
impl std::error::Error for Error {}
impl From<crate::commit::log::Error> for Error {
fn from(value: crate::commit::log::Error) -> Self {
Self::CommitLog(value)
}
}
impl From<gix::reference::find::existing::Error> for Error { impl From<gix::reference::find::existing::Error> for Error {
fn from(value: gix::reference::find::existing::Error) -> Self { fn from(value: gix::reference::find::existing::Error) -> Self {
Self::FindReference(value.to_string()) Self::FindReference(value.to_string())

View file

@ -1,35 +1,29 @@
use derive_more::Display; //
pub type Result<T> = core::result::Result<T, Error>; pub type Result<T> = core::result::Result<T, Error>;
#[derive(Debug, Display)] #[derive(Debug, thiserror::Error)]
pub enum Error { pub enum Error {
#[display("network: {}", 0)] #[error("network")]
Network(kxio::network::NetworkError), Network(#[from] kxio::network::NetworkError),
#[error("failed to register: {0}")]
FailedToRegister(String), FailedToRegister(String),
#[error("network response was empty")]
NetworkResponseEmpty, NetworkResponseEmpty,
#[error("repo config not loaded")]
NoRepoConfig, NoRepoConfig,
#[error("failed to notify self of loaded config")]
FailedToNotifySelf(String), FailedToNotifySelf(String),
Serde(serde_json::error::Error), #[error("(de)serialisation")]
Serde(#[from] serde_json::error::Error),
#[error("unknown branch: {0}")]
UnknownBranch(String), UnknownBranch(String),
#[error("failed to list: {0}")]
FailedToList(String), FailedToList(String),
} }
impl std::error::Error for Error {}
impl From<kxio::network::NetworkError> for Error {
fn from(value: kxio::network::NetworkError) -> Self {
Self::Network(value)
}
}
impl From<serde_json::error::Error> for Error {
fn from(value: serde_json::error::Error) -> Self {
Self::Serde(value)
}
}

View file

@ -14,12 +14,16 @@ impl std::fmt::Display for Force {
} }
} }
#[derive(Debug, derive_more::From, derive_more::Display)] #[derive(Debug, thiserror::Error)]
pub enum Error { pub enum Error {
Open(Box<gix::open::Error>), #[error("gix open: {0}")]
Push, Open(#[from] Box<gix::open::Error>),
#[error("io: {0}")]
Push(#[from] std::io::Error),
#[error("lock")]
Lock, Lock,
} }
impl std::error::Error for Error {}
pub type Result<T> = core::result::Result<T, Error>; pub type Result<T> = core::result::Result<T, Error>;

View file

@ -81,19 +81,35 @@ impl From<Direction> for gix::remote::Direction {
pub type Result<T> = core::result::Result<T, Error>; pub type Result<T> = core::result::Result<T, Error>;
#[derive(Debug, derive_more::Display)] #[derive(Debug, thiserror::Error)]
pub enum Error { pub enum Error {
#[error("invalid git dir: {0}")]
InvalidGitDir(git_next_config::GitDir), InvalidGitDir(git_next_config::GitDir),
#[error("io: {0}")]
Io(std::io::Error), Io(std::io::Error),
#[error("git exec wait: {0}")]
Wait(std::io::Error), Wait(std::io::Error),
#[error("git exec spawn: {0}")]
Spawn(std::io::Error), Spawn(std::io::Error),
#[error("validation: {0}")]
Validation(String), Validation(String),
#[error("git clone: {0}")]
Clone(String), Clone(String),
#[error("open: {0}")]
Open(String), Open(String),
#[error("git fetch: {0}")]
Fetch(String), Fetch(String),
#[error("mock lock")]
MockLock, MockLock,
} }
impl std::error::Error for Error {}
mod gix_errors { mod gix_errors {
#![cfg(not(tarpaulin_include))] // third-party library errors #![cfg(not(tarpaulin_include))] // third-party library errors

View file

@ -56,17 +56,13 @@ impl super::OpenRepositoryLike for RealOpenRepository {
#[cfg(not(tarpaulin_include))] // test is on local repo - should always have remotes #[cfg(not(tarpaulin_include))] // test is on local repo - should always have remotes
return Err(git::fetch::Error::NoFetchRemoteFound); return Err(git::fetch::Error::NoFetchRemoteFound);
}; };
let prepared_fetch = remote remote
.connect(gix::remote::Direction::Fetch)? .connect(gix::remote::Direction::Fetch)
.prepare_fetch(gix::progress::Discard, Default::default()); .map_err(|gix| git::fetch::Error::Connect(gix.to_string()))?
match prepared_fetch { .prepare_fetch(gix::progress::Discard, Default::default())
Ok(fetch) => { .map_err(|gix| git::fetch::Error::Prepare(gix.to_string()))?
fetch
.receive(gix::progress::Discard, &Default::default()) .receive(gix::progress::Discard, &Default::default())
.map_err(|e| git::fetch::Error::Fetch(e.to_string()))?; .map_err(|gix| git::fetch::Error::Receive(gix.to_string()))?;
}
Err(e) => Err(git::fetch::Error::Fetch(e.to_string()))?,
}
info!("Fetch okay"); info!("Fetch okay");
Ok(()) Ok(())
} }
@ -101,32 +97,18 @@ impl super::OpenRepositoryLike for RealOpenRepository {
.map_err(|_| git::push::Error::Lock) .map_err(|_| git::push::Error::Lock)
.map(|r| r.git_dir().to_path_buf())?; .map(|r| r.git_dir().to_path_buf())?;
let ctx = gix::diff::command::Context { let ctx = gix::diff::command::Context {
git_dir: Some(git_dir.clone()), git_dir: Some(git_dir),
..Default::default() ..Default::default()
}; };
match gix::command::prepare(command.expose_secret()) gix::command::prepare(command.expose_secret())
.with_context(ctx) .with_context(ctx)
.with_shell_allow_argument_splitting() .with_shell_allow_argument_splitting()
.stdout(std::process::Stdio::null()) .stdout(std::process::Stdio::null())
.stderr(std::process::Stdio::null()) .stderr(std::process::Stdio::null())
.spawn() .spawn()?
{ .wait()?;
Ok(mut child) => match child.wait() {
Ok(_) => {
info!("Push okay");
Ok(()) Ok(())
} }
Err(err) => {
warn!(?err, ?git_dir, "Failed (wait)");
Err(git::push::Error::Push)
}
},
Err(err) => {
warn!(?err, ?git_dir, "Failed (spawn)");
Err(git::push::Error::Push)
}
}
}
fn commit_log( fn commit_log(
&self, &self,

View file

@ -48,9 +48,10 @@ pub fn validate_positions(
repo_config.branches().main(), repo_config.branches().main(),
repo_config.branches().main(), repo_config.branches().main(),
); );
return Err(git::validation::positions::Error::DevBranchNotBasedOn( return Err(git::validation::positions::Error::DevBranchNotBasedOn {
repo_config.branches().main(), dev: repo_config.branches().dev(),
)); other: repo_config.branches().main(),
});
} }
// verify that next is an ancestor of dev, and force update to it main if it isn't // verify that next is an ancestor of dev, and force update to it main if it isn't
let Some(next) = commit_histories.next.first().cloned() else { let Some(next) = commit_histories.next.first().cloned() else {
@ -78,7 +79,7 @@ pub fn validate_positions(
commit: next, commit: next,
}); });
} }
return Err(git::validation::positions::Error::BranchReset( return Err(git::validation::positions::Error::NextBranchResetRequired(
repo_config.branches().next(), repo_config.branches().next(),
)); ));
} }
@ -108,7 +109,7 @@ pub fn validate_positions(
commit: next, commit: next,
}); });
} }
return Err(git::validation::positions::Error::BranchReset( return Err(git::validation::positions::Error::NextBranchResetRequired(
repo_config.branches().next(), repo_config.branches().next(),
)); ));
} }
@ -131,9 +132,10 @@ pub fn validate_positions(
repo_config.branches().dev(), repo_config.branches().dev(),
repo_config.branches().next() repo_config.branches().next()
); );
return Err(git::validation::positions::Error::DevBranchNotBasedOn( return Err(git::validation::positions::Error::DevBranchNotBasedOn {
repo_config.branches().next(), dev: repo_config.branches().dev(),
)); // dev is not based on next other: repo_config.branches().next(),
}); // dev is not based on next
} }
let Some(dev) = commit_histories.dev.first().cloned() else { let Some(dev) = commit_histories.dev.first().cloned() else {
warn!( warn!(
@ -169,28 +171,29 @@ fn get_commit_histories(
let histories = git::commit::Histories { main, next, dev }; let histories = git::commit::Histories { main, next, dev };
Ok(histories) Ok(histories)
} }
#[derive(Debug, derive_more::Display)] #[derive(Debug, thiserror::Error)]
pub enum Error { pub enum Error {
Fetch(git::fetch::Error), #[error("fetch: {0}")]
Fetch(#[from] git::fetch::Error),
CommitLog(git::commit::log::Error), #[error("commit log: {0}")]
CommitLog(#[from] git::commit::log::Error),
#[display("Failed to Reset Branch {branch} to {commit}")] #[error("failed to reset branch '{branch}' to {commit}")]
FailedToResetBranch { FailedToResetBranch {
branch: config::BranchName, branch: config::BranchName,
commit: git::Commit, commit: git::Commit,
}, },
BranchReset(config::BranchName), #[error("next branch '{0}' needs to be reset")]
NextBranchResetRequired(config::BranchName),
#[error("branch '{0}' has no commits")]
BranchHasNoCommits(config::BranchName), BranchHasNoCommits(config::BranchName),
DevBranchNotBasedOn(config::BranchName), #[error("dev branch '{dev}' not based on branch '{other}' ")]
} DevBranchNotBasedOn {
impl std::error::Error for Error {} dev: config::BranchName,
other: config::BranchName,
impl From<git::fetch::Error> for Error { },
fn from(value: git::fetch::Error) -> Self {
Self::Fetch(value)
}
} }

View file

@ -32,23 +32,35 @@ pub fn validate_repo(
} }
type Result<T> = core::result::Result<T, Error>; type Result<T> = core::result::Result<T, Error>;
#[derive(Debug, derive_more::Display)] #[derive(Debug, thiserror::Error)]
pub enum Error { pub enum Error {
#[error("no default push remote")]
NoDefaultPushRemote, NoDefaultPushRemote,
#[error("no default fetch remote")]
NoDefaultFetchRemote, NoDefaultFetchRemote,
#[error("no url for default push remote")]
NoUrlForDefaultPushRemote, NoUrlForDefaultPushRemote,
#[error("no hostname for default push remote")]
NoHostnameForDefaultPushRemote, NoHostnameForDefaultPushRemote,
#[error("unable to open repo: {0}")]
UnableToOpenRepo(String), UnableToOpenRepo(String),
Io(std::io::Error),
#[display("MismatchDefaultPushRemote(found: {found}, expected: {expected})")] #[error("io")]
Io(#[from] std::io::Error),
#[error("MismatchDefaultPushRemote(found: {found}, expected: {expected})")]
MismatchDefaultPushRemote { MismatchDefaultPushRemote {
found: git::GitRemote, found: git::GitRemote,
expected: git::GitRemote, expected: git::GitRemote,
}, },
#[display("MismatchDefaultFetchRemote(found: {found}, expected: {expected})")]
#[error("MismatchDefaultFetchRemote(found: {found}, expected: {expected})")]
MismatchDefaultFetchRemote { MismatchDefaultFetchRemote {
found: git::GitRemote, found: git::GitRemote,
expected: git::GitRemote, expected: git::GitRemote,
}, },
} }
impl std::error::Error for Error {}