delta-gitu 0.1.15

An experimental fork of gitu (a git client inspired by Magit), published as a library for delta to embed
Documentation
use std::{fmt::Display, io, string};

#[derive(Debug)]
pub enum Error {
    StashList(git2::Error),
    ReadLog(git2::Error),
    OpenRepo(git2::Error),
    Term(io::Error),
    Config(Box<figment::Error>),
    EditConfig(String),
    Bindings {
        bad_key_bindings: Vec<String>,
    },
    FileWatcher(notify::Error),
    ReadRebaseStatusFile(io::Error),
    ReadRebaseTodo(io::Error),
    ReadPipedInput(io::Error),
    PagerWithoutInput,
    WriteRebaseTodo(io::Error),
    ReadBranchName(io::Error),
    BranchNameUtf8(Utf8Error),
    GitDiff(io::Error),
    GitDiffUtf8(string::FromUtf8Error),
    GitShow(io::Error),
    GitShowUtf8(string::FromUtf8Error),
    GitShowMeta(git2::Error),
    NotOnBranch,
    GetHead(git2::Error),
    CurrentBranchName(git2::Error),
    GetCurrentBranchUpstream(git2::Error),
    GetCurrentBranchUpstreamUtf8(Utf8Error),
    RemoteNameUtf8(Utf8Error),
    CannotDeleteCurrentBranch,
    BranchNameRequired,
    IsBranchMerged(git2::Error),
    GetRemote(git2::Error),
    ReadGitConfig(git2::Error),
    ReadGitConfigUtf8(Utf8Error),
    DeleteGitConfig(git2::Error),
    SetGitConfig(git2::Error),
    RemoteHasNoName,
    ReadOid(git2::Error),
    ArgMustBePositiveNumber,
    ArgInvalidRegex(regex::Error),
    Clipboard(arboard::Error),
    FindGitRev(git2::Error),
    NoEditorSet,
    GitStatus(git2::Error),
    CmdAlreadyRunning,
    StashWorkTreeEmpty,
    CouldntAwaitCmd(io::Error),
    NoRepoWorkdir,
    SpawnCmd(io::Error),
    /// A command that failed: as it would be typed in a shell, its exit code
    /// (none if a signal ended it), and what it wrote to stderr.
    CmdBadExit(String, Option<i32>, String),
    CouldntReadCmdOutput(io::Error),
    ListGitReferences(git2::Error),
    OpenLogFile(io::Error),
    PromptAborted,
    ReadPromptHistory(io::Error),
    WritePromptHistory(io::Error),
    NoMoreEvents,
    CannotSpinoffCurrentBranch,
    SpinoffBranchExists(String),
    DoesBranchExist(git2::Error),
    GetBranchName(git2::Error),
    BaseCommitOid,
    UpstreamCommitOid,
    GitBlame(io::Error),
    NoSearchMatch(String),
    NoPreviousSearch,
    InvalidSearchRegex(regex::Error),
    EditedCommandQuotes,
    EditedCommandFixed,
    EditedCommandWrites(String),
    GitRefusedTheCommand(String),
}

impl std::error::Error for Error {}

impl Display for Error {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Error::StashList(e) => f.write_fmt(format_args!("Couldn't list stash: {e}")),
            Error::ReadLog(e) => f.write_fmt(format_args!("Couldn't read log: {e}")),
            Error::ReadPipedInput(e) => {
                f.write_fmt(format_args!("Couldn't read the piped input: {e}"))
            }
            Error::PagerWithoutInput => {
                f.write_str("--pager expects a patch on stdin, e.g. `git show | gitu --pager`")
            }
            Error::ReadRebaseTodo(e) => {
                f.write_fmt(format_args!("Couldn't read the rebase todo list: {e}"))
            }
            Error::WriteRebaseTodo(e) => {
                f.write_fmt(format_args!("Couldn't write the rebase todo list: {e}"))
            }
            Error::OpenRepo(e) => match e.code() {
                git2::ErrorCode::NotFound => f.write_str("No .git found in the current directory"),
                _ => f.write_fmt(format_args!("Couldn't open repo: {e:?}")),
            },
            Error::Term(e) => f.write_fmt(format_args!("Terminal error: {}", e)),
            Error::Config(e) => f.write_fmt(format_args!("Configuration error: {e}")),
            Error::EditConfig(e) => {
                f.write_fmt(format_args!("Couldn't save to the config file: {e}"))
            }
            Error::Bindings { bad_key_bindings } => {
                let mut error_string = String::from("Errors while parsing key bindings:");
                for item in bad_key_bindings {
                    error_string.push_str(&format!("\n{item}"));
                }
                f.write_fmt(format_args!("{error_string}"))
            }
            Error::FileWatcher(e) => f.write_fmt(format_args!("File watcher error: {e}")),
            Error::ReadRebaseStatusFile(e) => {
                f.write_fmt(format_args!("Couldn't read rebase status file: {e}"))
            }
            Error::ReadBranchName(e) => f.write_fmt(format_args!("Couldn't read branch name: {e}")),
            Error::BranchNameUtf8(_e) => f.write_str("Branch name error"),
            Error::GitDiff(e) => f.write_fmt(format_args!("Git diff error: {e}")),
            Error::GitDiffUtf8(e) => {
                f.write_fmt(format_args!("Git diff output is not valid UTF-8: {e}"))
            }
            Error::GitShow(e) => f.write_fmt(format_args!("Git show error: {e}")),
            Error::GitShowUtf8(e) => {
                f.write_fmt(format_args!("Git show output is not valid UTF-8: {e}"))
            }
            Error::GitShowMeta(e) => f.write_fmt(format_args!("Git show metadata error: {e}")),
            Error::NotOnBranch => f.write_str("Head is not a branch"),
            Error::GetHead(e) => f.write_fmt(format_args!("Couldn't get HEAD: {e}")),
            Error::CurrentBranchName(e) => {
                f.write_fmt(format_args!("Couldn't get current branch name: {e}"))
            }
            Error::GetCurrentBranchUpstream(e) => {
                f.write_fmt(format_args!("Couldn't get current branch upstream: {e}"))
            }
            Error::GetCurrentBranchUpstreamUtf8(_e) => {
                f.write_str("Current branch upstream is not valid UTF-8")
            }
            Error::RemoteNameUtf8(_e) => f.write_str("Remote name is not valid UTF-8"),
            Error::CannotDeleteCurrentBranch => f.write_str("Cannot delete current branch"),
            Error::BranchNameRequired => f.write_str("Branch name required"),
            Error::IsBranchMerged(e) => {
                f.write_fmt(format_args!("Couldn't check if branch is merged: {e}"))
            }
            Error::GetRemote(e) => f.write_fmt(format_args!("Couldn't get remote: {e}")),
            Error::ReadGitConfig(e) => f.write_fmt(format_args!("Couldn't read git config: {e}")),
            Error::ReadGitConfigUtf8(_e) => f.write_str("Git config is not valid UTF-8"),
            Error::DeleteGitConfig(e) => {
                f.write_fmt(format_args!("Couldn't delete git config: {e}"))
            }
            Error::SetGitConfig(e) => f.write_fmt(format_args!("Couldn't set git config: {e}")),
            Error::RemoteHasNoName => f.write_str("Remote has no name"),
            Error::ReadOid(e) => f.write_fmt(format_args!("Couldn't read OID: {e}")),
            Error::ArgMustBePositiveNumber => f.write_str("Value must be a number greater than 0"),
            Error::ArgInvalidRegex(e) => f.write_fmt(format_args!("Invalid regex: {e}")),
            Error::Clipboard(e) => f.write_fmt(format_args!("Clipboard error: {e}")),
            Error::FindGitRev(e) => f.write_fmt(format_args!("Couldn't find git revision: {e}")),
            Error::NoEditorSet => f.write_fmt(format_args!(
                "No editor environment variable set ({})",
                crate::ops::show::EDITOR_VARS.join(", ")
            )),
            Error::GitStatus(e) => f.write_fmt(format_args!("Git status error: {e}")),
            Error::CmdAlreadyRunning => f.write_str("A command is already running"),
            Error::StashWorkTreeEmpty => f.write_str("Cannot stash: working tree is empty"),
            Error::CouldntAwaitCmd(e) => f.write_fmt(format_args!("Couldn't await command: {e}")),
            Error::NoRepoWorkdir => f.write_str("No repository working directory"),
            Error::SpawnCmd(e) => f.write_fmt(format_args!("Failed to spawn command: {e}")),
            // The command on a line of its own, so that it can be copied whole.
            Error::CmdBadExit(command, code, stderr) => {
                writeln!(f, "{command}")?;
                match code {
                    Some(code) => write!(f, "exit code: {code}")?,
                    None => write!(f, "ended by a signal")?,
                }
                let stderr = stderr.trim_end();
                if !stderr.is_empty() {
                    write!(f, "\nstderr:\n{stderr}")?;
                }
                Ok(())
            }
            Error::CouldntReadCmdOutput(e) => {
                f.write_fmt(format_args!("Couldn't read command output: {e}"))
            }
            Error::ListGitReferences(e) => {
                f.write_fmt(format_args!("Couldn't list git references: {e}"))
            }
            Error::OpenLogFile(e) => f.write_fmt(format_args!("Couldn't open log file: {e}")),
            Error::PromptAborted => f.write_str("Aborted"),
            Error::ReadPromptHistory(e) => {
                f.write_fmt(format_args!("Couldn't read prompt history: {e}"))
            }
            Error::WritePromptHistory(e) => {
                f.write_fmt(format_args!("Couldn't write prompt history: {e}"))
            }
            Error::NoMoreEvents => unimplemented!(),
            Error::CannotSpinoffCurrentBranch => f.write_str("Cannot spin-off current branch"),
            Error::SpinoffBranchExists(new_branch_name) => f.write_fmt(format_args!(
                "Cannot spin-off {new_branch_name}. It already exists"
            )),
            Error::DoesBranchExist(e) => {
                f.write_fmt(format_args!("Couldn't check if branch exists: {}", e))
            }
            Error::GetBranchName(e) => f.write_fmt(format_args!("Couldn't get branch name: {}", e)),
            Error::BaseCommitOid => f.write_str("Could not resolve OID of base commit"),
            Error::UpstreamCommitOid => {
                f.write_str("Could not resolve OID of upstream branch commit")
            }
            Error::GitBlame(e) => f.write_fmt(format_args!("Git blame error: {e}")),
            Error::NoSearchMatch(query) => f.write_fmt(format_args!("No match: {query}")),
            Error::NoPreviousSearch => f.write_str("No previous search"),
            Error::InvalidSearchRegex(e) => f.write_fmt(format_args!("Invalid search: {e}")),
            Error::EditedCommandQuotes => f.write_str("Unbalanced quotes"),
            Error::EditedCommandFixed => f.write_str(
                "Only git's arguments can be edited, not the program or git's own options",
            ),
            Error::EditedCommandWrites(subcommand) => f.write_fmt(format_args!(
                "gitu re-runs this command on every refresh, so it must only report: {subcommand}"
            )),
            Error::GitRefusedTheCommand(stderr) => f.write_str(stderr),
        }
    }
}

#[derive(Debug)]
pub enum Utf8Error {
    Str(std::str::Utf8Error),
    String(string::FromUtf8Error),
}

impl std::error::Error for Utf8Error {}

impl Display for Utf8Error {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.write_str("not valid UTF-8")
    }
}