1use std::borrow::Cow;
25
26#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, serde::Deserialize, serde::Serialize)]
36#[serde(try_from = "String", into = "String")]
37pub struct Name(Cow<'static, str>);
38
39impl From<Name> for String {
40 fn from(name: Name) -> Self {
41 name.0.into()
42 }
43}
44
45impl Name {
46 #[must_use]
48 pub fn as_str(&self) -> &str {
49 &self.0
50 }
51
52 #[must_use]
60 pub const fn from_static_or_panic(name: &'static str) -> Self {
61 match validate_name(name) {
62 Ok(()) => {}
63 Err(NameError::Empty) => {
64 panic!("PostgreSQL parameter name cannot be empty");
65 }
66 Err(NameError::InvalidStartCharacter) => {
67 panic!("PostgreSQL parameter name must start with a letter or underscore");
68 }
69 Err(NameError::InvalidCharacter) => {
70 panic!("PostgreSQL parameter name contains an invalid character");
71 }
72 }
73 Self(Cow::Borrowed(name))
74 }
75}
76
77impl AsRef<str> for Name {
78 fn as_ref(&self) -> &str {
79 &self.0
80 }
81}
82
83impl std::fmt::Display for Name {
84 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
85 formatter.write_str(self.as_str())
86 }
87}
88
89#[derive(Debug, thiserror::Error)]
90pub enum NameError {
91 #[error("PostgreSQL parameter name cannot be empty")]
92 Empty,
93 #[error("PostgreSQL parameter name must start with a letter or underscore")]
94 InvalidStartCharacter,
95 #[error("PostgreSQL parameter name contains an invalid character")]
96 InvalidCharacter,
97}
98
99impl std::str::FromStr for Name {
100 type Err = NameError;
101
102 fn from_str(name: &str) -> Result<Self, Self::Err> {
103 validate_name(name).map(|()| Self(Cow::Owned(name.to_string())))
104 }
105}
106
107impl TryFrom<String> for Name {
108 type Error = NameError;
109
110 fn try_from(name: String) -> Result<Self, Self::Error> {
111 validate_name(&name).map(|()| Self(Cow::Owned(name)))
112 }
113}
114
115impl From<&'static str> for Name {
116 fn from(name: &'static str) -> Self {
117 Self::from_static_or_panic(name)
118 }
119}
120
121const fn validate_name(name: &str) -> Result<(), NameError> {
122 let bytes = name.as_bytes();
123 if bytes.is_empty() {
124 return Err(NameError::Empty);
125 }
126 let first = bytes[0];
127 if !(first.is_ascii_alphabetic() || first == b'_') {
128 return Err(NameError::InvalidStartCharacter);
129 }
130 let mut index = 1;
131 while index < bytes.len() {
132 let byte = bytes[index];
133 if !(byte.is_ascii_alphanumeric() || byte == b'_' || byte == b'.') {
134 return Err(NameError::InvalidCharacter);
135 }
136 index += 1;
137 }
138 Ok(())
139}
140
141pub const VALUE_MAX_LEN: usize = 4096;
151
152#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, serde::Deserialize, serde::Serialize)]
157#[serde(try_from = "String", into = "String")]
158pub struct Value(Cow<'static, str>);
159
160impl From<Value> for String {
161 fn from(value: Value) -> Self {
162 value.0.into()
163 }
164}
165
166impl Value {
167 #[must_use]
169 pub fn as_str(&self) -> &str {
170 &self.0
171 }
172
173 #[must_use]
181 pub const fn from_static_or_panic(value: &'static str) -> Self {
182 match validate_value(value) {
183 Ok(()) => {}
184 Err(ValueError::ContainsNul { .. }) => {
185 panic!("PostgreSQL parameter value cannot contain NUL byte");
186 }
187 Err(ValueError::TooLong { .. }) => {
188 panic!("PostgreSQL parameter value exceeds maximum of 4096 bytes");
189 }
190 }
191 Self(Cow::Borrowed(value))
192 }
193}
194
195impl AsRef<str> for Value {
196 fn as_ref(&self) -> &str {
197 &self.0
198 }
199}
200
201impl std::fmt::Display for Value {
202 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
203 formatter.write_str(self.as_str())
204 }
205}
206
207#[derive(Debug, thiserror::Error)]
208pub enum ValueError {
209 #[error("PostgreSQL parameter value contains NUL byte at index {index}")]
210 ContainsNul { index: usize },
211 #[error("PostgreSQL parameter value length {length} exceeds maximum {max}")]
212 TooLong { length: usize, max: usize },
213}
214
215impl std::str::FromStr for Value {
216 type Err = ValueError;
217
218 fn from_str(value: &str) -> Result<Self, Self::Err> {
219 validate_value(value).map(|()| Self(Cow::Owned(value.to_string())))
220 }
221}
222
223impl TryFrom<String> for Value {
224 type Error = ValueError;
225
226 fn try_from(value: String) -> Result<Self, Self::Error> {
227 validate_value(&value).map(|()| Self(Cow::Owned(value)))
228 }
229}
230
231impl From<&'static str> for Value {
232 fn from(value: &'static str) -> Self {
233 Self::from_static_or_panic(value)
234 }
235}
236
237const fn validate_value(value: &str) -> Result<(), ValueError> {
238 let bytes = value.as_bytes();
239 if bytes.len() > VALUE_MAX_LEN {
240 return Err(ValueError::TooLong {
241 length: bytes.len(),
242 max: VALUE_MAX_LEN,
243 });
244 }
245 let mut index = 0;
246 while index < bytes.len() {
247 if bytes[index] == 0 {
248 return Err(ValueError::ContainsNul { index });
249 }
250 index += 1;
251 }
252 Ok(())
253}
254
255pub type Map = std::collections::BTreeMap<Name, Value>;