diff --git a/lib/src/archive/entry.rs b/lib/src/archive/entry.rs index 988f23c2..bb7b544e 100644 --- a/lib/src/archive/entry.rs +++ b/lib/src/archive/entry.rs @@ -4,9 +4,10 @@ mod meta; mod name; mod options; mod read; +mod reference; mod write; -pub use self::{builder::*, header::*, meta::*, name::*, options::*}; +pub use self::{builder::*, header::*, meta::*, name::*, options::*, reference::*}; use self::{private::*, read::*, write::*}; use crate::{ chunk::{ diff --git a/lib/src/archive/entry/reference.rs b/lib/src/archive/entry/reference.rs new file mode 100644 index 00000000..6fcc1a21 --- /dev/null +++ b/lib/src/archive/entry/reference.rs @@ -0,0 +1,87 @@ +use crate::util::try_to_string; +use std::borrow::Cow; +use std::path::{Component, Path}; + +/// A UTF-8 encoded entry reference. +/// +/// ## Examples +/// ``` +/// use libpna::EntryReference; +/// +/// assert_eq!("uer/bin", EntryReference::try_from("uer/bin").unwrap().as_str()); +/// assert_eq!("/user/bin", EntryReference::try_from("/user/bin").unwrap().as_str()); +/// assert_eq!("/user/bin", EntryReference::try_from("/user/bin/").unwrap().as_str()); +/// assert_eq!("../user/bin", EntryReference::try_from("../user/bin/").unwrap().as_str()); +/// assert_eq!("/", EntryReference::try_from("/").unwrap().as_str()); +/// ``` +#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)] +pub struct EntryReference(String); + +impl EntryReference { + fn new(path: &Path) -> Result { + let has_root = path.has_root(); + let mut components = path.components(); + if has_root { + components.next(); + }; + let p = components + .map(|it| match it { + Component::Prefix(p) => try_to_string(p.as_os_str()), + Component::RootDir => unreachable!(), + Component::CurDir => Ok(Cow::from(".")), + Component::ParentDir => Ok(Cow::from("..")), + Component::Normal(n) => try_to_string(n), + }) + .collect::, _>>() + .map_err(|_| ())?; + let mut s = p.join("/"); + if has_root { + s.insert(0, '/'); + }; + Ok(Self(s)) + } + + /// Extracts a string slice containing the entire [EntryReference]. + /// ## Examples + /// Basic usage: + /// ``` + /// use libpna::EntryReference; + /// let r = EntryReference::try_from("foo").unwrap(); + /// + /// assert_eq!("foo", r.as_str()); + /// ``` + #[inline] + pub fn as_str(&self) -> &str { + self.0.as_str() + } +} + +impl TryFrom<&str> for EntryReference { + type Error = (); + + /// ## Examples + /// ``` + /// use libpna::EntryReference; + /// + /// assert_eq!(EntryReference::try_from("/path/with/root"), EntryReference::try_from("/path/with/root")); + /// ``` + fn try_from(value: &str) -> Result { + Self::new(value.as_ref()) + } +} + +impl TryFrom<&Path> for EntryReference { + type Error = (); + + /// ## Examples + /// ``` + /// use std::path::Path; + /// use libpna::EntryReference; + /// + /// let p = Path::new("path/to/file"); + /// assert_eq!(EntryReference::try_from(p), EntryReference::try_from("path/to/file")); + /// ``` + fn try_from(value: &Path) -> Result { + Self::new(value) + } +}