imdl 0.1.14

📦 A 40' shipping container for the internet
Documentation
use crate::common::*;

#[derive(Deserialize, Serialize, Debug, PartialEq, Clone, Ord, PartialOrd, Eq)]
#[serde(transparent)]
pub(crate) struct FilePath {
  components: Vec<String>,
}

impl FilePath {
  pub(crate) fn from_relative_path(path: &Path) -> Result<FilePath, Error> {
    let mut components = Vec::new();

    for component in path.components() {
      match component {
        path::Component::Normal(os) => {
          if let Some(unicode) = os.to_str() {
            components.push(unicode.to_owned());
          } else {
            return Err(Error::PathDecode {
              path: path.to_owned(),
              component: PathBuf::from(component.as_os_str()),
            });
          }
        }
        _ => {
          return Err(Error::PathComponent {
            path: path.to_owned(),
            component: PathBuf::from(component.as_os_str()),
          })
        }
      }
    }

    if components.is_empty() {
      return Err(Error::internal("FilePath::from_relative_path: empty path"));
    }

    Ok(FilePath { components })
  }

  pub(crate) fn name(&self) -> &str {
    &self.components[self.components.len() - 1]
  }

  pub(crate) fn components(&self) -> &[String] {
    &self.components
  }

  pub(crate) fn absolute(&self, root: &Path) -> PathBuf {
    let mut absolute = root.to_owned();
    for component in &self.components {
      absolute.push(component);
    }
    absolute
  }

  #[cfg(test)]
  pub(crate) fn from_components(components: &[&str]) -> FilePath {
    let components: Vec<String> = components.iter().copied().map(ToOwned::to_owned).collect();
    assert!(!components.is_empty());
    FilePath { components }
  }
}

impl Display for FilePath {
  fn fmt(&self, f: &mut Formatter) -> fmt::Result {
    for (i, component) in self.components.iter().enumerate() {
      if i > 0 {
        write!(f, "/")?;
      }
      write!(f, "{component}")?;
    }
    Ok(())
  }
}