sqlx_postgres/types/
ltree.rs1use crate::decode::Decode;
2use crate::encode::{Encode, IsNull};
3use crate::error::BoxDynError;
4use crate::types::Type;
5use crate::{PgArgumentBuffer, PgHasArrayType, PgTypeInfo, PgValueFormat, PgValueRef, Postgres};
6use std::fmt::{self, Display, Formatter};
7use std::io::Write;
8use std::ops::Deref;
9use std::str::FromStr;
10
11#[derive(Debug, thiserror::Error)]
13#[non_exhaustive]
14pub enum PgLTreeParseError {
15    #[error("ltree label contains invalid characters")]
17    InvalidLtreeLabel,
18
19    #[error("ltree version not supported")]
21    InvalidLtreeVersion,
22}
23
24#[derive(Clone, Debug, Default, PartialEq)]
25pub struct PgLTreeLabel(String);
26
27impl PgLTreeLabel {
28    pub fn new<S>(label: S) -> Result<Self, PgLTreeParseError>
29    where
30        S: Into<String>,
31    {
32        let label = label.into();
33        if label.len() <= 256
34            && label
35                .bytes()
36                .all(|c| c.is_ascii_alphabetic() || c.is_ascii_digit() || c == b'_')
37        {
38            Ok(Self(label))
39        } else {
40            Err(PgLTreeParseError::InvalidLtreeLabel)
41        }
42    }
43}
44
45impl Deref for PgLTreeLabel {
46    type Target = str;
47
48    fn deref(&self) -> &Self::Target {
49        self.0.as_str()
50    }
51}
52
53impl FromStr for PgLTreeLabel {
54    type Err = PgLTreeParseError;
55
56    fn from_str(s: &str) -> Result<Self, Self::Err> {
57        PgLTreeLabel::new(s)
58    }
59}
60
61impl Display for PgLTreeLabel {
62    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
63        write!(f, "{}", self.0)
64    }
65}
66
67#[derive(Clone, Debug, Default, PartialEq)]
88pub struct PgLTree {
89    labels: Vec<PgLTreeLabel>,
90}
91
92impl PgLTree {
93    pub fn new() -> Self {
95        Self::default()
96    }
97
98    pub fn from_labels(labels: Vec<PgLTreeLabel>) -> Self {
100        Self { labels }
101    }
102
103    #[deprecated = "renamed to `try_from_iter()`"]
106    #[allow(clippy::should_implement_trait)]
107    pub fn from_iter<I, S>(labels: I) -> Result<Self, PgLTreeParseError>
108    where
109        String: From<S>,
110        I: IntoIterator<Item = S>,
111    {
112        let mut ltree = Self::default();
113        for label in labels {
114            ltree.push(PgLTreeLabel::new(label)?);
115        }
116        Ok(ltree)
117    }
118
119    pub fn try_from_iter<I, S>(labels: I) -> Result<Self, PgLTreeParseError>
123    where
124        S: Into<String>,
125        I: IntoIterator<Item = S>,
126    {
127        labels.into_iter().map(PgLTreeLabel::new).collect()
128    }
129
130    pub fn push(&mut self, label: PgLTreeLabel) {
132        self.labels.push(label);
133    }
134
135    pub fn pop(&mut self) -> Option<PgLTreeLabel> {
137        self.labels.pop()
138    }
139}
140
141impl From<Vec<PgLTreeLabel>> for PgLTree {
142    fn from(labels: Vec<PgLTreeLabel>) -> Self {
143        Self { labels }
144    }
145}
146
147impl FromIterator<PgLTreeLabel> for PgLTree {
148    fn from_iter<T: IntoIterator<Item = PgLTreeLabel>>(iter: T) -> Self {
149        Self {
150            labels: iter.into_iter().collect(),
151        }
152    }
153}
154
155impl IntoIterator for PgLTree {
156    type Item = PgLTreeLabel;
157    type IntoIter = std::vec::IntoIter<Self::Item>;
158
159    fn into_iter(self) -> Self::IntoIter {
160        self.labels.into_iter()
161    }
162}
163
164impl FromStr for PgLTree {
165    type Err = PgLTreeParseError;
166
167    fn from_str(s: &str) -> Result<Self, Self::Err> {
168        Ok(Self {
169            labels: s
170                .split('.')
171                .map(PgLTreeLabel::new)
172                .collect::<Result<Vec<_>, Self::Err>>()?,
173        })
174    }
175}
176
177impl Display for PgLTree {
178    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
179        let mut iter = self.labels.iter();
180        if let Some(label) = iter.next() {
181            write!(f, "{label}")?;
182            for label in iter {
183                write!(f, ".{label}")?;
184            }
185        }
186        Ok(())
187    }
188}
189
190impl Deref for PgLTree {
191    type Target = [PgLTreeLabel];
192
193    fn deref(&self) -> &Self::Target {
194        &self.labels
195    }
196}
197
198impl Type<Postgres> for PgLTree {
199    fn type_info() -> PgTypeInfo {
200        PgTypeInfo::with_name("ltree")
202    }
203}
204
205impl PgHasArrayType for PgLTree {
206    fn array_type_info() -> PgTypeInfo {
207        PgTypeInfo::with_name("_ltree")
208    }
209}
210
211impl Encode<'_, Postgres> for PgLTree {
212    fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result<IsNull, BoxDynError> {
213        buf.extend(1i8.to_le_bytes());
214        write!(buf, "{self}")?;
215
216        Ok(IsNull::No)
217    }
218}
219
220impl<'r> Decode<'r, Postgres> for PgLTree {
221    fn decode(value: PgValueRef<'r>) -> Result<Self, BoxDynError> {
222        match value.format() {
223            PgValueFormat::Binary => {
224                let bytes = value.as_bytes()?;
225                let version = i8::from_le_bytes([bytes[0]; 1]);
226                if version != 1 {
227                    return Err(Box::new(PgLTreeParseError::InvalidLtreeVersion));
228                }
229                Ok(Self::from_str(std::str::from_utf8(&bytes[1..])?)?)
230            }
231            PgValueFormat::Text => Ok(Self::from_str(value.as_str()?)?),
232        }
233    }
234}