use super::Mode;
use thiserror::Error;
use yash_env::Env;
use yash_env::source::pretty::{Report, ReportType};
use yash_env::system::{Errno, Fstat, GetCwd};
#[derive(Debug, Clone, Eq, Error, PartialEq)]
pub enum Error {
#[error(transparent)]
SystemError(Errno),
}
impl Error {
#[must_use]
pub fn to_report(&self) -> Report<'_> {
let mut report = Report::new();
report.r#type = ReportType::Error;
report.title = format!("cannot determine working directory: {self}").into();
report
}
}
impl<'a> From<&'a Error> for Report<'a> {
#[inline]
fn from(error: &'a Error) -> Self {
error.to_report()
}
}
pub type Result = std::result::Result<String, Error>;
pub fn compute<S>(env: &Env<S>, mode: Mode) -> Result
where
S: Fstat + GetCwd,
{
match mode {
Mode::Logical => {
if let Some(pwd) = env.get_pwd_if_correct() {
return Ok(format!("{pwd}\n"));
}
}
Mode::Physical => (),
}
let mut cwd = env
.system
.getcwd()
.map_err(Error::SystemError)?
.into_unix_string()
.into_string()
.map_err(|_| Error::SystemError(Errno::EILSEQ))?;
cwd.push('\n');
Ok(cwd)
}
#[cfg(test)]
mod tests {
use super::*;
use std::cell::RefCell;
use std::rc::Rc;
use yash_env::VirtualSystem;
use yash_env::path::PathBuf;
use yash_env::system::r#virtual::FileBody;
use yash_env::system::r#virtual::Inode;
use yash_env::variable::PWD;
use yash_env::variable::Scope::Global;
fn env_with_symlink_to_dir() -> Env<VirtualSystem> {
let system = VirtualSystem::new();
let mut state = system.state.borrow_mut();
state
.file_system
.save(
"/foo/bar/dir",
Rc::new(RefCell::new(Inode {
body: FileBody::Directory {
files: Default::default(),
},
permissions: Default::default(),
})),
)
.unwrap();
state
.file_system
.save(
"/foo/link",
Rc::new(RefCell::new(Inode {
body: FileBody::Symlink {
target: "bar/dir".into(),
},
permissions: Default::default(),
})),
)
.unwrap();
drop(state);
system
.current_process_mut()
.chdir(PathBuf::from("/foo/bar/dir"));
Env::with_system(system)
}
#[test]
fn logical_with_correct_pwd() {
let mut env = env_with_symlink_to_dir();
env.variables
.get_or_new(PWD, Global)
.assign("/foo/link", None)
.unwrap();
let result = compute(&env, Mode::Logical).unwrap();
assert_eq!(result, "/foo/link\n");
}
#[test]
fn logical_with_wrong_pwd() {
let mut env = env_with_symlink_to_dir();
env.variables
.get_or_new(PWD, Global)
.assign("/foo/./link", None)
.unwrap();
let result = compute(&env, Mode::Logical).unwrap();
assert_eq!(result, "/foo/bar/dir\n");
}
#[test]
fn physical() {
let mut env = env_with_symlink_to_dir();
env.variables
.get_or_new(PWD, Global)
.assign("/foo/link", None)
.unwrap();
let result = compute(&env, Mode::Physical).unwrap();
assert_eq!(result, "/foo/bar/dir\n");
}
}