1use crate::common::report::prepare_report_message_and_divert;
20use std::borrow::Cow;
21use std::ffi::CString;
22use std::ffi::NulError;
23use thiserror::Error;
24use yash_env::Env;
25use yash_env::path::Path;
26use yash_env::semantics::Field;
27use yash_env::source::Location;
28use yash_env::source::pretty::{Report, ReportType, Snippet};
29#[cfg(doc)]
30use yash_env::stack::Stack;
31use yash_env::system::{Chdir, Errno, Fcntl, Isatty, Write};
32
33#[derive(Debug, Clone, Eq, Error, PartialEq)]
35pub enum Error {
36 #[error("path contains a nul byte")]
38 NulByteInPath,
39
40 #[error(transparent)]
42 SystemError(#[from] Errno),
43}
44
45impl From<NulError> for Error {
46 fn from(_: NulError) -> Self {
47 Self::NulByteInPath
48 }
49}
50
51pub fn chdir<T: Chdir>(env: &mut Env<T>, path: &Path) -> Result<(), Error> {
52 let c_path = CString::new(path.as_unix_str().as_bytes())?;
53 Ok(env.system.chdir(&c_path)?)
54}
55
56#[must_use = "returned message should be printed"]
65pub fn failure_message<S: Isatty>(
66 env: &Env<S>,
67 operand: Option<&Field>,
68 path: &Path,
69 error: &Error,
70) -> (String, yash_env::semantics::Result) {
71 let location = operand
72 .or_else(|| env.stack.current_builtin().map(|builtin| &builtin.name))
73 .map(|field| Cow::Borrowed(&field.origin))
74 .unwrap_or_else(|| Cow::Owned(Location::dummy("")));
75 let label = Cow::Owned(format!("{path:?}: {error}"));
76 let mut report = Report::new();
77 report.r#type = ReportType::Error;
78 report.title = "cannot change the working directory".into();
79 report.snippets = Snippet::with_primary_span(&location, label);
80 prepare_report_message_and_divert(env, report)
81}
82
83pub async fn report_failure<S>(
90 env: &mut Env<S>,
91 operand: Option<&Field>,
92 path: &Path,
93 error: &Error,
94) -> crate::Result
95where
96 S: Fcntl + Isatty + Write,
97{
98 let (message, divert) = failure_message(env, operand, path, error);
99 env.system.print_error(&message).await;
100 crate::Result::with_exit_status_and_divert(super::EXIT_STATUS_CHDIR_ERROR, divert)
101}