use crate::enums::{InvalidTendrilError, TendrilMode};
use crate::path_ext::{PathExt, UniPath};
use std::ffi::OsStr;
use std::path::{Component, Path, PathBuf};
#[cfg(test)]
pub(crate) mod tests;
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct Tendril {
local: PathBuf,
local_abs: PathBuf,
remote: UniPath,
pub mode: TendrilMode,
}
impl Tendril {
fn new(
td_repo: impl AsRef<UniPath>,
local: PathBuf,
remote: UniPath,
mode: TendrilMode,
) -> Result<Tendril, InvalidTendrilError> {
if local.components().any(|c| c == Component::ParentDir) {
return Err(InvalidTendrilError::InvalidLocal);
}
let mut local_sub_comps = local.components();
if match local_sub_comps.next() {
Some(Component::Normal(c))
if Self::is_forbidden_dir(c) => true,
Some(Component::RootDir | Component::CurDir) => match local_sub_comps.next() {
Some(Component::Normal(c)) if Self::is_forbidden_dir(c) => true,
None => true,
_ => false,
}
None => true,
_ => false,
}
{
return Err(InvalidTendrilError::InvalidLocal);
}
if Self::is_recursive(td_repo.as_ref().inner(), remote.inner()) {
return Err(InvalidTendrilError::Recursion)
}
#[cfg(not(windows))]
let local_abs = td_repo
.as_ref()
.inner()
.join_raw(&local)
.into();
#[cfg(windows)]
let local_abs = td_repo
.as_ref()
.inner()
.join_raw(&local)
.replace_dir_seps()
.into();
Ok(Tendril { local, local_abs, remote, mode })
}
#[cfg(any(test, feature = "_test_utils"))]
pub fn new_expose(
td_repo: impl AsRef<UniPath>,
local: PathBuf,
remote: UniPath,
mode: TendrilMode,
) -> Result<Tendril, InvalidTendrilError> {
Tendril::new(td_repo.as_ref(), local, remote, mode)
}
pub fn local_abs(&self) -> &Path {
&self.local_abs
}
pub fn remote(&self) -> &UniPath {
&self.remote
}
fn is_forbidden_dir(path_comp: &OsStr) -> bool {
match path_comp.to_string_lossy().to_lowercase().trim() {
".tendrils" => true,
#[cfg(windows)] ".tendrils." => true,
"" => true,
_ => false,
}
}
fn is_recursive(td_repo: &Path, remote: &Path) -> bool {
td_repo == remote
|| td_repo.ancestors().any(|p| p == remote)
|| remote.ancestors().any(|p| p == td_repo)
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct RawTendril {
pub local: String,
pub remote: String,
pub mode: TendrilMode,
pub profiles: Vec<String>,
}
impl RawTendril {
#[cfg(any(test, feature = "_test_utils"))]
pub fn new(local: &str) -> RawTendril {
RawTendril {
local: local.to_string(),
remote: "".to_string(),
mode: TendrilMode::CopyOverwrite,
profiles: vec![],
}
}
pub(crate) fn resolve<'a>(
&'a self,
td_repo: &'a UniPath,
) -> Result<Tendril, InvalidTendrilError> {
Tendril::new(
td_repo,
PathBuf::from(&self.local),
UniPath::from(PathBuf::from(&self.remote)),
self.mode.clone(),
)
}
}