sqlx_postgres/types/
ltree.rs

1use 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/// Represents ltree specific errors
12#[derive(Debug, thiserror::Error)]
13#[non_exhaustive]
14pub enum PgLTreeParseError {
15    /// LTree labels can only contain [A-Za-z0-9_]
16    #[error("ltree label contains invalid characters")]
17    InvalidLtreeLabel,
18
19    /// LTree version not supported
20    #[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        String: From<S>,
31    {
32        let label = String::from(label);
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/// Container for a Label Tree (`ltree`) in Postgres.
68///
69/// See https://www.postgresql.org/docs/current/ltree.html
70///
71/// ### Note: Requires Postgres 13+
72///
73/// This integration requires that the `ltree` type support the binary format in the Postgres
74/// wire protocol, which only became available in Postgres 13.
75/// ([Postgres 13.0 Release Notes, Additional Modules][https://www.postgresql.org/docs/13/release-13.html#id-1.11.6.11.5.14])
76///
77/// Ideally, SQLx's Postgres driver should support falling back to text format for types
78/// which don't have `typsend` and `typrecv` entries in `pg_type`, but that work still needs
79/// to be done.
80///
81/// ### Note: Extension Required
82/// The `ltree` extension is not enabled by default in Postgres. You will need to do so explicitly:
83///
84/// ```ignore
85/// CREATE EXTENSION IF NOT EXISTS "ltree";
86/// ```
87#[derive(Clone, Debug, Default, PartialEq)]
88pub struct PgLTree {
89    labels: Vec<PgLTreeLabel>,
90}
91
92impl PgLTree {
93    /// creates default/empty ltree
94    pub fn new() -> Self {
95        Self::default()
96    }
97
98    /// creates ltree from a [Vec<PgLTreeLabel>]
99    pub fn from(labels: Vec<PgLTreeLabel>) -> Self {
100        Self { labels }
101    }
102
103    /// creates ltree from an iterator with checking labels
104    pub fn from_iter<I, S>(labels: I) -> Result<Self, PgLTreeParseError>
105    where
106        String: From<S>,
107        I: IntoIterator<Item = S>,
108    {
109        let mut ltree = Self::default();
110        for label in labels {
111            ltree.push(PgLTreeLabel::new(label)?);
112        }
113        Ok(ltree)
114    }
115
116    /// push a label to ltree
117    pub fn push(&mut self, label: PgLTreeLabel) {
118        self.labels.push(label);
119    }
120
121    /// pop a label from ltree
122    pub fn pop(&mut self) -> Option<PgLTreeLabel> {
123        self.labels.pop()
124    }
125}
126
127impl IntoIterator for PgLTree {
128    type Item = PgLTreeLabel;
129    type IntoIter = std::vec::IntoIter<Self::Item>;
130
131    fn into_iter(self) -> Self::IntoIter {
132        self.labels.into_iter()
133    }
134}
135
136impl FromStr for PgLTree {
137    type Err = PgLTreeParseError;
138
139    fn from_str(s: &str) -> Result<Self, Self::Err> {
140        Ok(Self {
141            labels: s
142                .split('.')
143                .map(|s| PgLTreeLabel::new(s))
144                .collect::<Result<Vec<_>, Self::Err>>()?,
145        })
146    }
147}
148
149impl Display for PgLTree {
150    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
151        let mut iter = self.labels.iter();
152        if let Some(label) = iter.next() {
153            write!(f, "{label}")?;
154            for label in iter {
155                write!(f, ".{label}")?;
156            }
157        }
158        Ok(())
159    }
160}
161
162impl Deref for PgLTree {
163    type Target = [PgLTreeLabel];
164
165    fn deref(&self) -> &Self::Target {
166        &self.labels
167    }
168}
169
170impl Type<Postgres> for PgLTree {
171    fn type_info() -> PgTypeInfo {
172        // Since `ltree` is enabled by an extension, it does not have a stable OID.
173        PgTypeInfo::with_name("ltree")
174    }
175}
176
177impl PgHasArrayType for PgLTree {
178    fn array_type_info() -> PgTypeInfo {
179        PgTypeInfo::with_name("_ltree")
180    }
181}
182
183impl Encode<'_, Postgres> for PgLTree {
184    fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> IsNull {
185        buf.extend(1i8.to_le_bytes());
186        write!(buf, "{self}")
187            .expect("Display implementation panicked while writing to PgArgumentBuffer");
188
189        IsNull::No
190    }
191}
192
193impl<'r> Decode<'r, Postgres> for PgLTree {
194    fn decode(value: PgValueRef<'r>) -> Result<Self, BoxDynError> {
195        match value.format() {
196            PgValueFormat::Binary => {
197                let bytes = value.as_bytes()?;
198                let version = i8::from_le_bytes([bytes[0]; 1]);
199                if version != 1 {
200                    return Err(Box::new(PgLTreeParseError::InvalidLtreeVersion));
201                }
202                Ok(Self::from_str(std::str::from_utf8(&bytes[1..])?)?)
203            }
204            PgValueFormat::Text => Ok(Self::from_str(value.as_str()?)?),
205        }
206    }
207}