Skip to content
Merged
2 changes: 1 addition & 1 deletion src/bin/bender.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

fn main() {
if let Err(e) = bender::cli::main() {
bender::errorln!("{}", e);
eprintln!("{e:?}");
std::process::exit(1);
}
}
118 changes: 58 additions & 60 deletions src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,20 @@ use dunce::canonicalize;
use clap::builder::styling::{AnsiColor, Effects, Styles};
use clap::{ArgAction, CommandFactory, Parser, Subcommand, value_parser};
use indexmap::IndexSet;
use miette::{Context as _, IntoDiagnostic as _};
use serde_yaml_ng;
use tokio::runtime::Runtime;

use crate::Result;
use crate::cmd;
use crate::cmd::fusesoc::FusesocArgs;
use crate::config::{
Config, Manifest, Merge, PartialConfig, PrefixPaths, Validate, ValidationContext,
};
use crate::diagnostic::{Diagnostics, Warnings};
use crate::error::*;
use crate::lockfile::*;
use crate::sess::{Session, SessionArenas, SessionIo};
use crate::{bail, err};
use crate::{fmt_path, fmt_pkg, stageln};

#[derive(Parser, Debug)]
Expand Down Expand Up @@ -181,11 +183,12 @@ pub fn main() -> Result<()> {
// the -d/--dir switch, or by searching upwards in the file system
// hierarchy.
let root_dir: PathBuf = match &cli.dir {
Some(d) => canonicalize(d).map_err(|cause| {
Error::chain(format!("Failed to canonicalize path {:?}.", d), cause)
})?,
None => find_package_root(Path::new("."))
.map_err(|cause| Error::chain("Cannot find root directory of package.", cause))?,
Some(d) => canonicalize(d)
.into_diagnostic()
.wrap_err_with(|| format!("Failed to canonicalize path {:?}.", d))?,
None => {
find_package_root(Path::new(".")).wrap_err("Cannot find root directory of package.")?
}
};
log::debug!("root dir {:?}", root_dir);

Expand Down Expand Up @@ -254,7 +257,7 @@ pub fn main() -> Result<()> {

// Checkout if we are running update or package path does not exist yet
if matches!(cli.command, Commands::Update(_)) || !pkg_path.exists() {
let rt = Runtime::new()?;
let rt = Runtime::new().into_diagnostic()?;
rt.block_on(io.checkout(sess.dependency_with_name(pkg_name)?, false, &[]))?;
}

Expand All @@ -267,33 +270,28 @@ pub fn main() -> Result<()> {
// Check if there is something at the destination path that needs to be
// removed.
if path.exists() {
let meta = path.symlink_metadata().map_err(|cause| {
Error::chain(
format!("Failed to read metadata of path {:?}.", path),
cause,
)
})?;
let meta = path
.symlink_metadata()
.into_diagnostic()
.wrap_err_with(|| format!("Failed to read metadata of path {:?}.", path))?;
if !meta.file_type().is_symlink() {
Warnings::SkippingPackageLink(pkg_name.clone(), path.clone()).emit();
continue;
}
if path.read_link().map(|d| d != pkg_path).unwrap_or(true) {
log::debug!("removing existing link {:?}", path);
remove_symlink_dir(path).map_err(|cause| {
Error::chain(
format!("Failed to remove symlink at path {:?}.", path),
cause,
)
remove_symlink_dir(path).wrap_err_with(|| {
format!("Failed to remove symlink at path {:?}.", path)
})?;
}
}

// Create the symlink if there is nothing at the destination.
if !path.exists() {
if let Some(parent) = path.parent() {
std::fs::create_dir_all(parent).map_err(|cause| {
Error::chain(format!("Failed to create directory {:?}.", parent), cause)
})?;
std::fs::create_dir_all(parent)
.into_diagnostic()
.wrap_err_with(|| format!("Failed to create directory {:?}.", parent))?;
}
let previous_dir = match path.parent() {
Some(parent) => {
Expand All @@ -303,13 +301,10 @@ pub fn main() -> Result<()> {
}
None => None,
};
symlink_dir(&pkg_path, path).map_err(|cause| {
Error::chain(
format!(
"Failed to create symlink to {:?} at path {:?}.",
pkg_path, path
),
cause,
symlink_dir(&pkg_path, path).wrap_err_with(|| {
format!(
"Failed to create symlink to {:?} at path {:?}.",
pkg_path, path
)
})?;
if let Some(d) = previous_dir {
Expand Down Expand Up @@ -346,7 +341,7 @@ pub fn main() -> Result<()> {
Commands::Plugin(args) => {
let (plugin_name, plugin_args) = args
.split_first()
.ok_or_else(|| Error::new("No command specified.".to_string()))?;
.ok_or_else(|| err!("No command specified."))?;
execute_plugin(&sess, plugin_name, plugin_args)
}
Commands::Completion(_) | Commands::Init | Commands::Clean(_) => {
Expand All @@ -357,22 +352,22 @@ pub fn main() -> Result<()> {

#[cfg(unix)]
pub fn symlink_dir(p: &Path, q: &Path) -> Result<()> {
Ok(std::os::unix::fs::symlink(p, q)?)
std::os::unix::fs::symlink(p, q).into_diagnostic()
}

#[cfg(windows)]
pub fn symlink_dir(p: &Path, q: &Path) -> Result<()> {
Ok(std::os::windows::fs::symlink_dir(p, q)?)
std::os::windows::fs::symlink_dir(p, q).into_diagnostic()
}

#[cfg(unix)]
pub fn remove_symlink_dir(path: &Path) -> Result<()> {
Ok(std::fs::remove_file(path)?)
std::fs::remove_file(path).into_diagnostic()
}

#[cfg(windows)]
pub fn remove_symlink_dir(path: &Path) -> Result<()> {
Ok(std::fs::remove_dir(path)?)
std::fs::remove_dir(path).into_diagnostic()
}

/// Find the root directory of a package.
Expand All @@ -384,7 +379,8 @@ fn find_package_root(from: &Path) -> Result<PathBuf> {

// Canonicalize the path. This will resolve any intermediate links.
let mut path = canonicalize(from)
.map_err(|cause| Error::chain(format!("Failed to canonicalize path {:?}.", from), cause))?;
.into_diagnostic()
.wrap_err_with(|| format!("Failed to canonicalize path {:?}.", from))?;
log::debug!("canonicalized to {:?}", path);

// Look up the device at the current path. This information will then be
Expand All @@ -405,10 +401,10 @@ fn find_package_root(from: &Path) -> Result<PathBuf> {

// Abort if we have reached the filesystem root.
if !path.pop() {
return Err(Error::new(format!(
bail!(
"No manifest (`Bender.yml` file) found. Stopped searching at filesystem root {:?}.",
path
)));
);
}

// Abort if we have crossed the filesystem boundary.
Expand All @@ -417,17 +413,15 @@ fn find_package_root(from: &Path) -> Result<PathBuf> {
let rdev: Option<_> = metadata(&path).map(|m| m.dev()).ok();
log::debug!("rdev = {:?}", rdev);
if rdev != limit_rdev {
return Err(Error::new(format!(
bail!(
"No manifest (`Bender.yml` file) found. Stopped searching at filesystem boundary {:?}.",
path
)));
);
}
}
}

Err(Error::new(
"No manifest (`Bender.yml` file) found. Reached maximum number of search steps.",
))
bail!("No manifest (`Bender.yml` file) found. Reached maximum number of search steps.")
}

/// Read a package manifest from a file.
Expand All @@ -436,14 +430,16 @@ pub fn read_manifest(path: &Path) -> Result<Manifest> {
use std::fs::File;
log::debug!("reading manifest {:?}", path);
let file = File::open(path)
.map_err(|cause| Error::chain(format!("Cannot open manifest {:?}.", path), cause))?;
.into_diagnostic()
.wrap_err_with(|| format!("Cannot open manifest {:?}.", path))?;
let partial: PartialManifest = serde_yaml_ng::from_reader(file)
.map_err(|cause| Error::chain(format!("Syntax error in manifest {:?}.", path), cause))?;
.into_diagnostic()
.wrap_err_with(|| format!("Syntax error in manifest {:?}.", path))?;
partial
.prefix_paths(path.parent().unwrap())
.map_err(|cause| Error::chain(format!("Error in manifest prefixing {:?}.", path), cause))?
.wrap_err_with(|| format!("Error in manifest prefixing {:?}.", path))?
.validate(&ValidationContext::default())
.map_err(|cause| Error::chain(format!("Error in manifest {:?}.", path), cause))
.wrap_err_with(|| format!("Error in manifest {:?}.", path))
}

/// Load a configuration by traversing a directory hierarchy upwards.
Expand All @@ -455,7 +451,8 @@ fn load_config(from: &Path, warn_config_loaded: bool) -> Result<Config> {

// Canonicalize the path. This will resolve any intermediate links.
let mut path = canonicalize(from)
.map_err(|cause| Error::chain(format!("Failed to canonicalize path {:?}.", from), cause))?;
.into_diagnostic()
.wrap_err_with(|| format!("Failed to canonicalize path {:?}.", from))?;
log::debug!("canonicalized to {:?}", path);

// Look up the device at the current path. This information will then be
Expand Down Expand Up @@ -522,7 +519,7 @@ fn load_config(from: &Path, warn_config_loaded: bool) -> Result<Config> {
// Validate the configuration.
let mut out = out
.validate(&ValidationContext::default())
.map_err(|cause| Error::chain("Invalid configuration:", cause))?;
.wrap_err("Invalid configuration:")?;

out.overrides = out
.overrides
Expand All @@ -541,9 +538,11 @@ fn maybe_load_config(path: &Path, warn_config_loaded: bool) -> Result<Option<Par
return Ok(None);
}
let file = File::open(path)
.map_err(|cause| Error::chain(format!("Cannot open config {:?}.", path), cause))?;
.into_diagnostic()
.wrap_err_with(|| format!("Cannot open config {:?}.", path))?;
let partial: PartialConfig = serde_yaml_ng::from_reader(file)
.map_err(|cause| Error::chain(format!("Syntax error in config {:?}.", path), cause))?;
.into_diagnostic()
.wrap_err_with(|| format!("Syntax error in config {:?}.", path))?;
if warn_config_loaded {
Warnings::UsingConfigForOverride {
path: path.to_path_buf(),
Expand All @@ -558,14 +557,14 @@ fn execute_plugin(sess: &Session, plugin: &str, args: &[String]) -> Result<()> {
log::debug!("execute plugin `{}`", plugin);

// Obtain a list of declared plugins.
let runtime = Runtime::new()?;
let runtime = Runtime::new().into_diagnostic()?;
let io = SessionIo::new(sess);
let plugins = runtime.block_on(io.plugins(false))?;

// Lookup the requested plugin and complain if it does not exist.
let plugin = match plugins.get(plugin) {
Some(p) => p,
None => return Err(Error::new(format!("Unknown command `{}`.", plugin))),
None => bail!("Unknown command `{}`.", plugin),
};
log::debug!("found plugin {:#?}", plugin);

Expand All @@ -575,25 +574,24 @@ fn execute_plugin(sess: &Session, plugin: &str, args: &[String]) -> Result<()> {
cmd.env(
"BENDER",
std::env::current_exe()
.map_err(|cause| Error::chain("Failed to determine current executable.", cause))?,
.into_diagnostic()
.wrap_err("Failed to determine current executable.")?,
);
cmd.env(
"BENDER_CALL_DIR",
std::env::current_dir()
.map_err(|cause| Error::chain("Failed to determine current directory.", cause))?,
.into_diagnostic()
.wrap_err("Failed to determine current directory.")?,
);
cmd.env("BENDER_MANIFEST_DIR", sess.root);
cmd.current_dir(sess.root);
cmd.args(args);

log::debug!("executing plugin {:#?}", cmd);
let stat = cmd.status().map_err(|cause| {
Error::chain(
format!(
"Unable to spawn process for plugin `{}`. Command was {:#?}.",
plugin.name, cmd
),
cause,
let stat = cmd.status().into_diagnostic().wrap_err_with(|| {
format!(
"Unable to spawn process for plugin `{}`. Command was {:#?}.",
plugin.name, cmd
)
})?;

Expand Down
5 changes: 3 additions & 2 deletions src/cmd/audit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,13 @@ use std::io::Write;

use clap::Args;
use futures::future::join_all;
use miette::IntoDiagnostic as _;
use semver::VersionReq;
use tabwriter::TabWriter;
use tokio::runtime::Runtime;

use crate::Result;
use crate::cmd::parents::get_parent_array;
use crate::error::*;
use crate::sess::{DependencyVersions, Session, SessionIo};

/// Get information about version conflicts and possible updates.
Expand All @@ -34,7 +35,7 @@ pub struct AuditArgs {

/// Execute the `audit` subcommand.
pub fn run(sess: &Session, args: &AuditArgs) -> Result<()> {
let rt = Runtime::new()?;
let rt = Runtime::new().into_diagnostic()?;
let io = SessionIo::new(sess);

let binding = sess.packages().clone();
Expand Down
5 changes: 3 additions & 2 deletions src/cmd/checkout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@
//! The `checkout` subcommand.

use clap::Args;
use miette::IntoDiagnostic as _;
use tokio::runtime::Runtime;

use crate::error::*;
use crate::Result;
use crate::fmt_dim;
use crate::infoln;
use crate::sess::{Session, SessionIo};
Expand All @@ -27,7 +28,7 @@ pub fn run(sess: &Session, args: &CheckoutArgs) -> Result<()> {

/// Execute a checkout (for the `checkout` subcommand).
pub fn run_plain(sess: &Session, force: bool, update_list: &[String]) -> Result<()> {
let rt = Runtime::new()?;
let rt = Runtime::new().into_diagnostic()?;
let io = SessionIo::new(sess);
let start_time = std::time::Instant::now();
let _srcs = rt.block_on(io.sources(force, update_list))?;
Expand Down
Loading