use crate::common::arrange_message_and_divert;
use std::borrow::Cow;
use std::ffi::CString;
use std::ffi::NulError;
use thiserror::Error;
use yash_env::path::Path;
use yash_env::semantics::ExitStatus;
use yash_env::semantics::Field;
#[cfg(doc)]
use yash_env::stack::Stack;
use yash_env::system::Errno;
#[cfg(doc)]
use yash_env::system::SharedSystem;
use yash_env::Env;
use yash_env::System;
use yash_syntax::source::pretty::Annotation;
use yash_syntax::source::pretty::AnnotationType;
use yash_syntax::source::pretty::Message;
use yash_syntax::source::Location;
#[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(env: &mut Env, path: &Path) -> Result<(), Error> {
let c_path = CString::new(path.as_unix_str().as_bytes())?;
Ok(env.system.chdir(&c_path)?)
}
#[must_use]
pub fn failure_message(
env: &Env,
operand: Option<&Field>,
path: &Path,
error: &Error,
) -> (String, yash_env::semantics::Result) {
let label = Cow::Owned(format!("{path:?}: {error}"));
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 error = Annotation::new(AnnotationType::Error, label, &location);
let message = Message {
r#type: AnnotationType::Error,
title: "cannot change the working directory".into(),
annotations: vec![error],
footers: vec![],
};
arrange_message_and_divert(env, message)
}
pub async fn report_failure(
env: &mut Env,
operand: Option<&Field>,
path: &Path,
error: &Error,
) -> crate::Result {
let (message, divert) = failure_message(env, operand, path, error);
env.system.print_error(&message).await;
crate::Result::with_exit_status_and_divert(ExitStatus::FAILURE, divert)
}