use crate::error::{Error, ErrorKind, Result};
use std::fs::canonicalize;
use std::path::Path;
use nix::unistd::{getgid, getuid};
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]
pub struct Builtins {
args: Vec<String>,
dir: String,
path: String,
user: UserInfo,
check_mode: bool,
}
#[derive(Serialize, Deserialize)]
struct UserInfo {
uid: u32,
gid: u32,
}
impl Builtins {
pub fn new(args: Vec<String>, path: &Path, check_mode: bool) -> Result<Self> {
let dir = Builtins::get_dir(path)?;
let file_name = path
.file_name()
.ok_or_else(|| Error::new(ErrorKind::NotFound, "Script filename not found"))?;
let dir_path = Path::new(&dir);
let canonical_path = dir_path.join(file_name);
let canonical = canonical_path.to_str().ok_or_else(|| {
Error::new(
ErrorKind::InvalidData,
"Script path cannot be represented as UTF-8",
)
})?;
Ok(Builtins {
args,
dir,
path: canonical.to_owned(),
user: UserInfo {
uid: u32::from(getuid()),
gid: u32::from(getgid()),
},
check_mode,
})
}
fn get_dir(path: &Path) -> Result<String> {
let parent_path_original = path
.parent()
.ok_or_else(|| Error::new(ErrorKind::NotFound, "Script parent dir not found"))?;
let parent_path = if parent_path_original.to_str() == Some("") {
Path::new(".")
} else {
parent_path_original
};
trace!("parent_path: {parent_path:?}");
let dir = canonicalize(parent_path)?
.to_str()
.ok_or_else(|| {
Error::new(
ErrorKind::InvalidData,
"Script parent dir cannot be represented as UTF-8",
)
})?
.to_owned();
trace!("dir: {dir:?}");
Ok(if dir.is_empty() { ".".to_owned() } else { dir })
}
pub fn update(&self, path: &Path) -> Result<Self> {
Builtins::new(self.args.clone(), path, self.check_mode)
}
}
#[cfg(test)]
mod tests {
use super::*;
use tempfile::tempdir;
#[test]
fn test_builtin_new() {
let builtins = Builtins::new(vec![], Path::new("/example.rh"), false).unwrap();
assert_eq!(builtins.args.len(), 0);
assert_eq!(builtins.path, "/example.rh".to_owned());
assert_eq!(builtins.dir, "/".to_owned());
assert!(!builtins.check_mode);
}
#[test]
fn test_builtin_check_mode() {
let builtins = Builtins::new(vec![], Path::new("/example.rh"), true).unwrap();
assert!(builtins.check_mode);
}
#[test]
fn test_builtin_same_dir() {
let dir = tempdir().unwrap();
let dir_path = dir.path();
let file_path = dir_path.join("example.rh");
let builtins = Builtins::new(vec![], file_path.as_ref(), false).unwrap();
assert_eq!(
builtins.dir,
canonicalize(dir_path).unwrap().to_str().unwrap().to_owned()
);
}
}