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: 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 FromIterator<PgLTreeLabel> for PgLTree {
142 fn from_iter<T: IntoIterator<Item = PgLTreeLabel>>(iter: T) -> Self {
143 Self {
144 labels: iter.into_iter().collect(),
145 }
146 }
147}
148
149impl IntoIterator for PgLTree {
150 type Item = PgLTreeLabel;
151 type IntoIter = std::vec::IntoIter<Self::Item>;
152
153 fn into_iter(self) -> Self::IntoIter {
154 self.labels.into_iter()
155 }
156}
157
158impl FromStr for PgLTree {
159 type Err = PgLTreeParseError;
160
161 fn from_str(s: &str) -> Result<Self, Self::Err> {
162 Ok(Self {
163 labels: s
164 .split('.')
165 .map(PgLTreeLabel::new)
166 .collect::<Result<Vec<_>, Self::Err>>()?,
167 })
168 }
169}
170
171impl Display for PgLTree {
172 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
173 let mut iter = self.labels.iter();
174 if let Some(label) = iter.next() {
175 write!(f, "{label}")?;
176 for label in iter {
177 write!(f, ".{label}")?;
178 }
179 }
180 Ok(())
181 }
182}
183
184impl Deref for PgLTree {
185 type Target = [PgLTreeLabel];
186
187 fn deref(&self) -> &Self::Target {
188 &self.labels
189 }
190}
191
192impl Type<Postgres> for PgLTree {
193 fn type_info() -> PgTypeInfo {
194 PgTypeInfo::with_name("ltree")
196 }
197}
198
199impl PgHasArrayType for PgLTree {
200 fn array_type_info() -> PgTypeInfo {
201 PgTypeInfo::with_name("_ltree")
202 }
203}
204
205impl Encode<'_, Postgres> for PgLTree {
206 fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result<IsNull, BoxDynError> {
207 buf.extend(1i8.to_le_bytes());
208 write!(buf, "{self}")?;
209
210 Ok(IsNull::No)
211 }
212}
213
214impl<'r> Decode<'r, Postgres> for PgLTree {
215 fn decode(value: PgValueRef<'r>) -> Result<Self, BoxDynError> {
216 match value.format() {
217 PgValueFormat::Binary => {
218 let bytes = value.as_bytes()?;
219 let version = i8::from_le_bytes([bytes[0]; 1]);
220 if version != 1 {
221 return Err(Box::new(PgLTreeParseError::InvalidLtreeVersion));
222 }
223 Ok(Self::from_str(std::str::from_utf8(&bytes[1..])?)?)
224 }
225 PgValueFormat::Text => Ok(Self::from_str(value.as_str()?)?),
226 }
227 }
228}