1pub mod error;
2
3use core::fmt;
4use std::fmt::{Debug, Display};
5
6use serde::{Deserialize, Serialize};
7use serde_with::{TryFromInto, serde_as};
8use thiserror::Error;
9use typed_path::Utf8UnixPathBuf;
10
11#[doc(hidden)]
12pub mod sealed {
13 pub trait Sealed {}
14}
15
16pub use error::{ICError, ICResultExt};
17
18pub const DEFAULT_BRANCH: &str = "main";
19
20#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone, Hash, PartialOrd, Ord)]
21pub struct ETag(pub String);
22
23#[serde_as]
25#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Serialize, Deserialize)]
26pub struct Path(#[serde_as(as = "TryFromInto<String>")] Utf8UnixPathBuf);
27
28impl Debug for Path {
31 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
32 Display::fmt(self, f)
33 }
34}
35
36impl Display for Path {
37 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
38 write!(f, "{}", self.0)
39 }
40}
41
42#[derive(Debug, Clone, Error, PartialEq, Eq)]
44#[non_exhaustive]
45pub enum PathError {
46 #[error("path must start with a `/` character")]
47 NotAbsolute,
48 #[error(r#"path must be cannonic, cannot include "." or "..""#)]
49 NotCanonic,
50}
51
52impl Path {
53 pub fn root() -> Path {
54 Path(Utf8UnixPathBuf::from("/".to_string()))
55 }
56
57 pub fn from_trusted(path: &str) -> Path {
59 Path(Utf8UnixPathBuf::from(path))
60 }
61
62 pub fn new(path: &str) -> Result<Path, PathError> {
63 let buf = Utf8UnixPathBuf::from(path);
64 if !buf.is_absolute() {
65 return Err(PathError::NotAbsolute);
66 }
67
68 if buf.normalize() != buf {
69 return Err(PathError::NotCanonic);
70 }
71 Ok(Path(buf))
72 }
73
74 pub fn starts_with(&self, other: &Path) -> bool {
75 self.0.starts_with(&other.0)
76 }
77
78 pub fn ancestors(&self) -> impl Iterator<Item = Path> + '_ {
79 self.0.ancestors().map(|p| Path(p.to_owned()))
80 }
81
82 pub fn name(&self) -> Option<&str> {
83 self.0.file_name()
84 }
85
86 pub fn buf(&self) -> &Utf8UnixPathBuf {
87 &self.0
88 }
89}
90
91impl TryFrom<&str> for Path {
92 type Error = PathError;
93
94 fn try_from(value: &str) -> Result<Self, Self::Error> {
95 Self::new(value)
96 }
97}
98
99impl TryFrom<&String> for Path {
100 type Error = PathError;
101
102 fn try_from(value: &String) -> Result<Self, Self::Error> {
103 value.as_str().try_into()
104 }
105}
106
107impl TryFrom<String> for Path {
108 type Error = PathError;
109
110 fn try_from(value: String) -> Result<Self, Self::Error> {
111 value.as_str().try_into()
112 }
113}
114
115pub fn user_agent() -> &'static str {
119 concat!("icechunk-rust-", env!("CARGO_PKG_VERSION"))
120}