Skip to content

Commit

Permalink
Merge pull request #568 from epage/error
Browse files Browse the repository at this point in the history
refactor: Be more like cargo in error reporting
  • Loading branch information
epage authored Oct 19, 2022
2 parents 61dc0a7 + b3935f5 commit 5d27962
Show file tree
Hide file tree
Showing 20 changed files with 195 additions and 339 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ env_logger = "0.9"
globset = { version = "0.4.9", default-features = false }
dunce = "1.0.3"
trycmd = "0.14.0"
anyhow = "1.0.65"

[dev-dependencies]
assert_fs = "1.0"
Expand Down
2 changes: 1 addition & 1 deletion src/bin/cargo-release.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ fn main() {
error::exit(res)
}

fn run() -> Result<(), error::ProcessError> {
fn run() -> Result<(), error::CliError> {
let Command::Release(ref release_matches) = Command::parse();

let mut builder = get_logging(release_matches.logging.log_level());
Expand Down
44 changes: 20 additions & 24 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::path::{Path, PathBuf};

use serde::{Deserialize, Serialize};

use crate::error::FatalError;
use crate::error::CargoResult;
use crate::ops::cargo;

#[derive(Debug, Clone, Default, Serialize, Deserialize)]
Expand Down Expand Up @@ -438,7 +438,7 @@ struct CargoMetadata {
pub fn load_workspace_config(
args: &ConfigArgs,
ws_meta: &cargo_metadata::Metadata,
) -> Result<Config, FatalError> {
) -> CargoResult<Config> {
let mut release_config = Config {
is_workspace: 1 < ws_meta.workspace_members.len(),
..Default::default()
Expand Down Expand Up @@ -478,7 +478,7 @@ pub fn load_package_config(
args: &ConfigArgs,
ws_meta: &cargo_metadata::Metadata,
pkg: &cargo_metadata::Package,
) -> Result<Config, FatalError> {
) -> CargoResult<Config> {
let manifest_path = pkg.manifest_path.as_std_path();

let is_workspace = 1 < ws_meta.workspace_members.len();
Expand Down Expand Up @@ -673,39 +673,39 @@ impl PushArgs {
}
}

fn get_pkg_config_from_manifest(manifest_path: &Path) -> Result<Option<Config>, FatalError> {
fn get_pkg_config_from_manifest(manifest_path: &Path) -> CargoResult<Option<Config>> {
if manifest_path.exists() {
let m = std::fs::read_to_string(manifest_path).map_err(FatalError::from)?;
let c: CargoManifest = toml_edit::easy::from_str(&m).map_err(FatalError::from)?;
let m = std::fs::read_to_string(manifest_path)?;
let c: CargoManifest = toml_edit::easy::from_str(&m)?;

Ok(c.package.and_then(|p| p.into_config()))
} else {
Ok(None)
}
}

fn get_ws_config_from_manifest(manifest_path: &Path) -> Result<Option<Config>, FatalError> {
fn get_ws_config_from_manifest(manifest_path: &Path) -> CargoResult<Option<Config>> {
if manifest_path.exists() {
let m = std::fs::read_to_string(manifest_path).map_err(FatalError::from)?;
let c: CargoManifest = toml_edit::easy::from_str(&m).map_err(FatalError::from)?;
let m = std::fs::read_to_string(manifest_path)?;
let c: CargoManifest = toml_edit::easy::from_str(&m)?;

Ok(c.workspace.and_then(|p| p.into_config()))
} else {
Ok(None)
}
}

fn get_config_from_file(file_path: &Path) -> Result<Option<Config>, FatalError> {
fn get_config_from_file(file_path: &Path) -> CargoResult<Option<Config>> {
if file_path.exists() {
let c = std::fs::read_to_string(file_path).map_err(FatalError::from)?;
let config = toml_edit::easy::from_str(&c).map_err(FatalError::from)?;
let c = std::fs::read_to_string(file_path)?;
let config = toml_edit::easy::from_str(&c)?;
Ok(Some(config))
} else {
Ok(None)
}
}

pub fn resolve_custom_config(file_path: &Path) -> Result<Option<Config>, FatalError> {
pub fn resolve_custom_config(file_path: &Path) -> CargoResult<Option<Config>> {
get_config_from_file(file_path)
}

Expand All @@ -716,7 +716,7 @@ pub fn resolve_custom_config(file_path: &Path) -> Result<Option<Config>, FatalEr
/// 2. $HOME/.config/cargo-release/release.toml
/// 3. $(workspace)/release.toml
/// 3. $(workspace)/Cargo.toml
pub fn resolve_workspace_config(workspace_root: &Path) -> Result<Config, FatalError> {
pub fn resolve_workspace_config(workspace_root: &Path) -> CargoResult<Config> {
let mut config = Config::default();

// User-local configuration from home directory.
Expand Down Expand Up @@ -764,7 +764,7 @@ pub fn resolve_workspace_config(workspace_root: &Path) -> Result<Config, FatalEr
///
/// `$(crate)/Cargo.toml` is a way to differentiate configuration for the root crate and the
/// workspace.
pub fn resolve_config(workspace_root: &Path, manifest_path: &Path) -> Result<Config, FatalError> {
pub fn resolve_config(workspace_root: &Path, manifest_path: &Path) -> CargoResult<Config> {
let mut config = resolve_workspace_config(workspace_root)?;

// Crate config
Expand All @@ -783,24 +783,20 @@ pub fn resolve_config(workspace_root: &Path, manifest_path: &Path) -> Result<Con
Ok(config)
}

pub fn resolve_overrides(
workspace_root: &Path,
manifest_path: &Path,
) -> Result<Config, FatalError> {
pub fn resolve_overrides(workspace_root: &Path, manifest_path: &Path) -> CargoResult<Config> {
let mut release_config = Config::default();

// the publish flag in cargo file
let manifest = std::fs::read_to_string(manifest_path).map_err(FatalError::from)?;
let manifest: CargoManifest = toml_edit::easy::from_str(&manifest).map_err(FatalError::from)?;
let manifest = std::fs::read_to_string(manifest_path)?;
let manifest: CargoManifest = toml_edit::easy::from_str(&manifest)?;
if let Some(package) = manifest.package.as_ref() {
let publish = match package.publish.as_ref() {
Some(MaybeWorkspace::Defined(publish)) => *publish,
Some(MaybeWorkspace::Workspace(workspace)) => {
if workspace.workspace {
let workspace = workspace_root.join("Cargo.toml");
let workspace = std::fs::read_to_string(workspace).map_err(FatalError::from)?;
let workspace: CargoManifest =
toml_edit::easy::from_str(&workspace).map_err(FatalError::from)?;
let workspace = std::fs::read_to_string(workspace)?;
let workspace: CargoManifest = toml_edit::easy::from_str(&workspace)?;
workspace
.workspace
.as_ref()
Expand Down
163 changes: 25 additions & 138 deletions src/error.rs
Original file line number Diff line number Diff line change
@@ -1,49 +1,47 @@
use std::env::VarError;
use std::io::Error as IOError;
use std::path::PathBuf;
use std::str::Utf8Error;
use std::string::FromUtf8Error;

use cargo_metadata::Error as CargoMetaError;
use quick_error::quick_error;
use regex::Error as RegexError;
use semver::Error as SemVerError;
use toml_edit::easy::de::Error as TomlDeError;
use toml_edit::easy::ser::Error as TomlSerError;
use toml_edit::TomlError as TomlEditError;

#[derive(Debug)]
pub struct ProcessError {
error: Option<FatalError>,
pub struct CliError {
error: Option<anyhow::Error>,
code: i32,
}

impl ProcessError {
impl CliError {
pub fn silent(code: i32) -> Self {
Self { error: None, code }
}

pub fn message(e: impl Into<FatalError>) -> Self {
pub fn message(e: impl Into<anyhow::Error>) -> Self {
Self {
error: Some(e.into()),
code: 101,
}
}
}

impl<E: Into<FatalError>> From<E> for ProcessError {
fn from(error: E) -> Self {
Self::message(error)
}
macro_rules! process_error_from {
($from:ty) => {
impl From<$from> for CliError {
fn from(error: $from) -> Self {
Self::message(error)
}
}
};
}

impl From<i32> for ProcessError {
process_error_from!(anyhow::Error);
process_error_from!(std::io::Error);
process_error_from!(semver::Error);
process_error_from!(ignore::Error);
process_error_from!(crates_index::Error);
process_error_from!(cargo_metadata::Error);
process_error_from!(toml_edit::ser::Error);

impl From<i32> for CliError {
fn from(code: i32) -> Self {
Self::silent(code)
}
}

impl std::fmt::Display for ProcessError {
impl std::fmt::Display for CliError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
if let Some(error) = self.error.as_ref() {
error.fmt(f)
Expand All @@ -54,13 +52,13 @@ impl std::fmt::Display for ProcessError {
}

/// Report any error message and exit.
pub fn exit(result: Result<(), ProcessError>) -> ! {
pub fn exit(result: Result<(), CliError>) -> ! {
let code = report(result);
std::process::exit(code)
}

/// Report, delegating exiting to the caller.
pub fn report(result: Result<(), ProcessError>) -> i32 {
pub fn report(result: Result<(), CliError>) -> i32 {
match result {
Ok(()) => 0,
Err(err) => {
Expand All @@ -75,115 +73,4 @@ pub fn report(result: Result<(), ProcessError>) -> i32 {
}
}

quick_error! {
#[derive(Debug)]
pub enum FatalError {
IOError(err: IOError) {
from()
source(err)
display("IO Error: {}", err)
}
FileNotFound(filename: PathBuf){
display("Unable to find file {} to perform replace", filename.display())
}
InvalidTomlData(err: TomlDeError) {
display("Invalid TOML file format: {}", err)
from()
source(err)
}
InvalidTomlFileFormat(err: TomlSerError) {
display("Invalid TOML file format: {}", err)
from()
source(err)
}
InvalidTomlEditFileFormat(err: TomlEditError) {
display("Invalid TOML file format: {}", err)
from()
source(err)
}
InvalidCargoMetaFileFormat(err: CargoMetaError) {
display("Invalid Cargo file format: {}", err)
from()
source(err)
}
InvalidCargoFileFormat(msg: String) {
display("Invalid TOML file format: {}", msg)
}
InvalidCargoConfigKeys {
display("Invalid cargo-release config item found")
}
SemVerError(err: SemVerError) {
from()
source(err)
display("SemVerError {}", err)
}
IgnoreError(err: ignore::Error) {
from()
source(err)
display("ignore-pattern {}", err)
}
Utf8Error(err: Utf8Error) {
from()
source(err)
display("Utf8Error {}", err)
}
FromUtf8Error(err: FromUtf8Error) {
from()
source(err)
display("FromUtf8Error {}", err)
}
IndexError(err: crates_index::Error) {
from()
source(err)
display("{}", err)
}
Git2Error(err: git2::Error) {
from()
source(err)
display("{}", err)
}
NoPackage {
display("No package in manifest file")
}
PackageListFailed(manifest: std::path::PathBuf, err: String) {
display("Failed to get package content for {}: {}", manifest.display(), err)
}
InvalidReleaseLevel(level: String) {
display("Unsupported release level {}, only major, minor and patch are supported", level)
}
UnsupportedPrereleaseVersionScheme {
display("This version scheme is not supported by cargo-release. Use format like `pre`, `dev` or `alpha.1` for prerelease symbol")
}
UnsupportedVersionReq(req: String) {
display("Support for modifying {} is currently unsupported", req)
}
ReplacerConfigError {
display("Insufficient replacer config: file, search and replace are required.")
}
ReplacerRegexError(err: RegexError) {
from()
source(err)
display("RegexError {}", err)
}
ReplacerMinError(pattern: String, req: usize, actual: usize) {
display("For `{}`, at least {} replacements expected, found {}", pattern, req, actual)
}
ReplacerMaxError(pattern: String, req: usize, actual: usize) {
display("For `{}`, at most {} replacements expected, found {}", pattern, req, actual)
}
VarError(err: VarError) {
from()
source(err)
display("Environment Variable Error: {}", err)
}
GitBinError {
display("git is not found. git is required for cargo-release workflow.")
}
PublishTimeoutError {
display("Timeout waiting for crate to be published.")
}
DependencyVersionConflict {
display("Dependency is configured to conflict with new version")
}
}
}
pub type CargoResult<T> = anyhow::Result<T>;
Loading

0 comments on commit 5d27962

Please sign in to comment.