use std::path::{self, Path, PathBuf};
use crate::{error::ErrorKind, Error};
use super::{File, FileResolver};
#[derive(Debug)]
pub struct IncludeFileResolver {
include: PathBuf,
}
impl IncludeFileResolver {
pub fn new(include: PathBuf) -> Self {
IncludeFileResolver { include }
}
}
impl FileResolver for IncludeFileResolver {
fn resolve_path(&self, path: &Path) -> Option<String> {
if let Some(relative_path) = strip_prefix(path, &self.include) {
if let Some(name) = path_to_file_name(relative_path) {
return Some(name);
}
}
None
}
fn open_file(&self, name: &str) -> Result<File, Error> {
File::open(name, &self.include.join(name))
}
}
pub(crate) fn path_to_file_name(path: &Path) -> Option<String> {
let mut name = String::new();
for component in path.components() {
match component {
std::path::Component::Normal(component) => {
if let Some(component) = component.to_str() {
if !name.is_empty() {
name.push('/');
}
name.push_str(component);
} else {
return None;
}
}
_ => return None,
}
}
if name.is_empty() {
None
} else {
Some(name)
}
}
pub(crate) fn check_shadow(
file: &str,
actual_path: Option<&Path>,
expected_path: &Path,
) -> Result<(), Error> {
if let Some(actual_path) = actual_path {
if !path_eq(actual_path, expected_path) {
return Err(Error::from_kind(ErrorKind::FileShadowed {
name: file.to_string(),
path: expected_path.to_owned(),
shadow: actual_path.to_owned(),
}));
}
}
Ok(())
}
fn strip_prefix<'a>(path: &'a Path, prefix: &Path) -> Option<&'a Path> {
Some(iter_after(path.components(), prefix.components())?.as_path())
}
fn path_eq(l: &Path, r: &Path) -> bool {
let (mut lhs, mut rhs) = (l.components(), r.components());
loop {
let (mut lhs_next, mut rhs_next) = (lhs.clone(), rhs.clone());
match (lhs_next.next(), rhs_next.next()) {
(None, None) => return true,
(Some(path::Component::CurDir), _) => {
lhs = lhs_next;
}
(_, Some(path::Component::CurDir)) => {
rhs = rhs_next;
}
(Some(ref l), Some(ref r)) if path_component_eq(l, r) => {
lhs = lhs_next;
rhs = rhs_next;
}
_ => return false,
}
}
}
fn iter_after<'a, 'b, I, J>(mut iter: I, mut prefix: J) -> Option<I>
where
I: Iterator<Item = path::Component<'a>> + Clone,
J: Iterator<Item = path::Component<'b>> + Clone,
{
loop {
let mut path_next = iter.clone();
let mut prefix_next = prefix.clone();
match (path_next.next(), prefix_next.next()) {
(Some(path::Component::CurDir), _) => {
iter = path_next;
}
(_, Some(path::Component::CurDir)) => {
prefix = prefix_next;
}
(Some(ref l), Some(ref r)) if path_component_eq(l, r) => {
iter = path_next;
prefix = prefix_next;
}
(Some(_), Some(_)) => return None,
(Some(_), None) => return Some(iter),
(None, None) => return Some(iter),
(None, Some(_)) => return None,
}
}
}
#[cfg(windows)]
fn path_component_eq(l: &path::Component, r: &path::Component) -> bool {
l.as_os_str().eq_ignore_ascii_case(r.as_os_str())
}
#[cfg(not(windows))]
fn path_component_eq(l: &path::Component, r: &path::Component) -> bool {
l == r
}