use std::path::PathBuf;
use std::str::FromStr;
use std::{error, fmt, io};
#[derive(Clone, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum OutsidePackageAction {
Workspace,
Ignore,
Error,
}
#[cfg_attr(test, mutants::skip)]
impl FromStr for OutsidePackageAction {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_lowercase().as_str() {
"workspace" => Ok(Self::Workspace),
"ignore" => Ok(Self::Ignore),
"error" => Ok(Self::Error),
_ => Err(format!(
"Invalid outside-package action: '{s}'. Valid options are: workspace, ignore, error"
)),
}
}
}
#[doc(hidden)]
#[derive(Debug)]
#[expect(
clippy::exhaustive_structs,
reason = "This is a hidden struct for internal/test use only"
)]
pub struct RunInput {
pub path: PathBuf,
pub via_env: Option<String>,
pub outside_package: OutsidePackageAction,
pub subcommand: Vec<String>,
}
#[doc(hidden)]
#[derive(Clone, Debug, Eq, PartialEq)]
#[expect(
clippy::exhaustive_enums,
reason = "This is a hidden enum for internal/test use only"
)]
pub enum RunOutcome {
PackageDetected {
package_name: String,
subcommand_succeeded: bool,
},
WorkspaceScope { subcommand_succeeded: bool },
Ignored,
}
#[doc(hidden)]
#[derive(Debug)]
#[expect(
clippy::exhaustive_enums,
reason = "This is a hidden enum for internal/test use only"
)]
pub enum RunError {
WorkspaceValidation(String),
PackageDetection(String),
OutsidePackage,
CommandExecution(io::Error),
}
impl fmt::Display for RunError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::WorkspaceValidation(msg) => write!(f, "{msg}"),
Self::PackageDetection(msg) => write!(f, "Error detecting package: {msg}"),
Self::OutsidePackage => write!(f, "Path is not in any package"),
Self::CommandExecution(e) => write!(f, "Error executing command: {e}"),
}
}
}
impl error::Error for RunError {}
#[cfg(test)]
#[cfg_attr(coverage_nightly, coverage(off))]
mod tests {
use super::*;
#[test]
fn outside_package_action_parsing() {
assert_eq!(
"workspace".parse::<OutsidePackageAction>().unwrap(),
OutsidePackageAction::Workspace
);
assert_eq!(
"Workspace".parse::<OutsidePackageAction>().unwrap(),
OutsidePackageAction::Workspace
);
assert_eq!(
"WORKSPACE".parse::<OutsidePackageAction>().unwrap(),
OutsidePackageAction::Workspace
);
assert_eq!(
"ignore".parse::<OutsidePackageAction>().unwrap(),
OutsidePackageAction::Ignore
);
assert_eq!(
"Ignore".parse::<OutsidePackageAction>().unwrap(),
OutsidePackageAction::Ignore
);
assert_eq!(
"error".parse::<OutsidePackageAction>().unwrap(),
OutsidePackageAction::Error
);
assert_eq!(
"Error".parse::<OutsidePackageAction>().unwrap(),
OutsidePackageAction::Error
);
let result = "invalid".parse::<OutsidePackageAction>();
result.unwrap_err();
}
#[test]
fn run_error_display_workspace_validation() {
let error = RunError::WorkspaceValidation("some validation error".to_string());
let display = format!("{error}");
assert!(!display.is_empty());
}
#[test]
fn run_error_display_package_detection() {
let error = RunError::PackageDetection("could not find package".to_string());
let display = format!("{error}");
assert!(!display.is_empty());
}
#[test]
fn run_error_display_outside_package() {
let error = RunError::OutsidePackage;
let display = format!("{error}");
assert!(!display.is_empty());
}
#[test]
fn run_error_display_command_execution() {
let io_error = io::Error::new(io::ErrorKind::NotFound, "command not found");
let error = RunError::CommandExecution(io_error);
let display = format!("{error}");
assert!(!display.is_empty());
}
}