Skip to content

Commit

Permalink
Simplify error message for missing repository (#151)
Browse files Browse the repository at this point in the history
This simplifies the error message by showing a custom message instead of
the message from the underlying git error.
  • Loading branch information
spenserblack authored Sep 1, 2023
1 parent 786a55b commit cf6e412
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 19 deletions.
16 changes: 13 additions & 3 deletions gengo/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ use super::generated::Generated;
use super::vendored::Vendored;
use super::Analyzers;
use super::Gengo;
use super::{Error, ErrorKind};
use git2::ErrorCode;
use git2::Repository;
use std::error::Error;
use std::error::Error as ErrorTrait;
use std::path::Path;

/// Builds a new `Gengo` instance.
Expand Down Expand Up @@ -47,8 +49,16 @@ impl<P: AsRef<Path>> Builder<P> {
self
}

pub fn build(self) -> Result<Gengo, Box<dyn Error>> {
let repository = Repository::discover(self.repository_path)?;
pub fn build(self) -> Result<Gengo, Box<dyn ErrorTrait>> {
let repository = match Repository::discover(self.repository_path) {
Ok(r) => r,
Err(e) => match e.code() {
ErrorCode::NotFound => {
return Err(Box::new(Error::with_source(ErrorKind::NoRepository, e)));
}
_ => return Err(Box::new(e)),
},
};
let analyzers = self.analyzers.unwrap_or_default();
let read_limit = self.read_limit.unwrap_or(Self::DEFAULT_READ_LIMIT);
let documentation = Documentation::new();
Expand Down
67 changes: 67 additions & 0 deletions gengo/src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
use std::error::Error as ErrorTrait;
use std::fmt;

macro_rules! error_kind {
($($name:ident, $message:literal),*) => {
/// The kind of error that occurred.
#[derive(Debug)]
#[non_exhaustive]
pub enum ErrorKind {
$(
$name,
)*
}

impl fmt::Display for ErrorKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
$(
Self::$name => write!(f, $message),
)*
}
}
}
};
}

error_kind!(NoRepository, "no repository found");

impl ErrorTrait for ErrorKind {}

#[derive(Debug)]
pub struct Error {
kind: ErrorKind,
source: Option<Box<dyn ErrorTrait>>,
}

impl Error {
pub fn new(kind: ErrorKind) -> Self {
Self { kind, source: None }
}

pub fn with_source<E>(kind: ErrorKind, source: E) -> Self
where
E: ErrorTrait + 'static,
{
Self {
kind,
source: Some(Box::new(source)),
}
}

pub fn kind(&self) -> &ErrorKind {
&self.kind
}
}

impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", &self.kind)
}
}

impl ErrorTrait for Error {
fn source(&self) -> Option<&(dyn ErrorTrait + 'static)> {
self.source.as_ref().map(|s| s.as_ref())
}
}
27 changes: 11 additions & 16 deletions gengo/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,24 +9,27 @@ pub use analysis::Analysis;
pub use analysis::Iter as AnalysisIter;
pub use builder::Builder;
use documentation::Documentation;
pub use error::{Error, ErrorKind};
use generated::Generated;
use git2::{AttrCheckFlags, AttrValue, Blob, Commit, ObjectType, Repository, Tree};
use glob::MatchOptions;
use indexmap::IndexMap;
pub use languages::analyzer::Analyzers;
use languages::Category;
pub use languages::Language;
use std::error::Error;
use std::path::{Path, PathBuf};
use vendored::Vendored;

pub mod analysis;
mod builder;
mod documentation;
mod error;
mod generated;
pub mod languages;
mod vendored;

type Result<T, E = Box<dyn std::error::Error>> = std::result::Result<T, E>;

/// Shared match options for consistent behavior.
const GLOB_MATCH_OPTIONS: MatchOptions = MatchOptions {
case_sensitive: true,
Expand All @@ -49,14 +52,14 @@ impl Gengo {
const ATTR_CHECK_FLAGS: [AttrCheckFlags; 2] =
[AttrCheckFlags::NO_SYSTEM, AttrCheckFlags::INDEX_THEN_FILE];
/// Resolves a revision to a commit.
fn rev(&self, rev: &str) -> Result<Commit, Box<dyn Error>> {
fn rev(&self, rev: &str) -> Result<Commit> {
let reference = self.repository.revparse_single(rev)?;
let commit = reference.peel_to_commit()?;
Ok(commit)
}

/// Analyzes each file in the repository at the given revision.
pub fn analyze(&self, rev: &str) -> Result<Analysis, Box<dyn Error>> {
pub fn analyze(&self, rev: &str) -> Result<Analysis> {
let mut results = IndexMap::new();
let commit = self.rev(rev)?;
let tree = commit.tree()?;
Expand All @@ -69,7 +72,7 @@ impl Gengo {
root: &str,
tree: &Tree,
results: &mut IndexMap<PathBuf, Entry>,
) -> Result<(), Box<dyn Error>> {
) -> Result<()> {
for entry in tree.iter() {
let kind = entry.kind();
// HACK Skip submodules. Might want to refactor this later.
Expand Down Expand Up @@ -107,7 +110,7 @@ impl Gengo {
filepath: P,
blob: &Blob,
results: &mut IndexMap<PathBuf, Entry>,
) -> Result<(), Box<dyn Error>> {
) -> Result<()> {
let contents = blob.content();

let lang_override = self
Expand Down Expand Up @@ -175,7 +178,7 @@ impl Gengo {
self.vendored.is_vendored(filepath, contents)
}

fn get_attr<P: AsRef<Path>>(&self, path: P, attr: &str) -> Result<AttrValue, Box<dyn Error>> {
fn get_attr<P: AsRef<Path>>(&self, path: P, attr: &str) -> Result<AttrValue> {
let flags = Self::ATTR_CHECK_FLAGS
.into_iter()
.reduce(|a, b| a | b)
Expand All @@ -185,11 +188,7 @@ impl Gengo {
Ok(attr)
}

fn get_boolean_attr<P: AsRef<Path>>(
&self,
path: P,
attr: &str,
) -> Result<Option<bool>, Box<dyn Error>> {
fn get_boolean_attr<P: AsRef<Path>>(&self, path: P, attr: &str) -> Result<Option<bool>> {
let attr = self.get_attr(path, attr)?;
let attr = match attr {
AttrValue::True => Some(true),
Expand All @@ -201,11 +200,7 @@ impl Gengo {
Ok(attr)
}

fn get_str_attr<P: AsRef<Path>>(
&self,
path: P,
attr: &str,
) -> Result<Option<String>, Box<dyn Error>> {
fn get_str_attr<P: AsRef<Path>>(&self, path: P, attr: &str) -> Result<Option<String>> {
let attr = self.get_attr(path, attr)?;
let attr = match attr {
AttrValue::String(s) => Some(s),
Expand Down

0 comments on commit cf6e412

Please sign in to comment.