use crate::common::report::prepare_report_message_and_divert;
use std::borrow::Cow;
use std::ffi::CString;
use std::ffi::NulError;
use thiserror::Error;
use yash_env::Env;
use yash_env::path::Path;
use yash_env::semantics::Field;
use yash_env::source::Location;
use yash_env::source::pretty::{Report, ReportType, Snippet};
#[cfg(doc)]
use yash_env::stack::Stack;
#[cfg(doc)]
use yash_env::system::SharedSystem;
use yash_env::system::{Chdir, Errno, Fcntl, Isatty, Write};
#[derive(Debug, Clone, Eq, Error, PartialEq)]
pub enum Error {
#[error("path contains a nul byte")]
NulByteInPath,
#[error(transparent)]
SystemError(#[from] Errno),
}
impl From<NulError> for Error {
fn from(_: NulError) -> Self {
Self::NulByteInPath
}
}
pub fn chdir<T: Chdir>(env: &mut Env<T>, path: &Path) -> Result<(), Error> {
let c_path = CString::new(path.as_unix_str().as_bytes())?;
Ok(env.system.chdir(&c_path)?)
}
#[must_use = "returned message should be printed"]
pub fn failure_message<S: Isatty>(
env: &Env<S>,
operand: Option<&Field>,
path: &Path,
error: &Error,
) -> (String, yash_env::semantics::Result) {
let location = operand
.or_else(|| env.stack.current_builtin().map(|builtin| &builtin.name))
.map(|field| Cow::Borrowed(&field.origin))
.unwrap_or_else(|| Cow::Owned(Location::dummy("")));
let label = Cow::Owned(format!("{path:?}: {error}"));
let mut report = Report::new();
report.r#type = ReportType::Error;
report.title = "cannot change the working directory".into();
report.snippets = Snippet::with_primary_span(&location, label);
prepare_report_message_and_divert(env, report)
}
pub async fn report_failure<S>(
env: &mut Env<S>,
operand: Option<&Field>,
path: &Path,
error: &Error,
) -> crate::Result
where
S: Fcntl + Isatty + Write,
{
let (message, divert) = failure_message(env, operand, path, error);
env.system.print_error(&message).await;
crate::Result::with_exit_status_and_divert(super::EXIT_STATUS_CHDIR_ERROR, divert)
}