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 as_str(&self) -> &str {
58 self.0.as_str()
59 }
60
61 pub fn from_trusted(path: &str) -> Path {
63 Path(Utf8UnixPathBuf::from(path))
64 }
65
66 pub fn new(path: &str) -> Result<Path, PathError> {
67 let buf = Utf8UnixPathBuf::from(path);
68 if !buf.is_absolute() {
69 return Err(PathError::NotAbsolute);
70 }
71
72 if buf.normalize() != buf {
73 return Err(PathError::NotCanonic);
74 }
75 Ok(Path(buf))
76 }
77
78 pub fn starts_with(&self, other: &Path) -> bool {
79 self.0.starts_with(&other.0)
80 }
81
82 pub fn ancestors(&self) -> impl Iterator<Item = Path> + '_ {
83 self.0.ancestors().map(|p| Path(p.to_owned()))
84 }
85
86 pub fn parent(&self) -> Option<Path> {
87 self.0.parent().map(|p| Path(p.to_owned()))
88 }
89
90 pub fn name(&self) -> Option<&str> {
91 self.0.file_name()
92 }
93
94 pub fn buf(&self) -> &Utf8UnixPathBuf {
95 &self.0
96 }
97}
98
99impl TryFrom<&str> for Path {
100 type Error = PathError;
101
102 fn try_from(value: &str) -> Result<Self, Self::Error> {
103 Self::new(value)
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
115impl TryFrom<String> for Path {
116 type Error = PathError;
117
118 fn try_from(value: String) -> Result<Self, Self::Error> {
119 value.as_str().try_into()
120 }
121}
122
123pub fn user_agent() -> &'static str {
127 concat!("icechunk-rust-", env!("CARGO_PKG_VERSION"))
128}
129
130pub const fn user_agent_product() -> &'static str {
134 concat!("icechunk/", env!("CARGO_PKG_VERSION"))
135}