mz_repr/adt/
varchar.rs
1use std::error::Error;
11use std::fmt;
12
13use anyhow::bail;
14use mz_lowertest::MzReflect;
15use mz_ore::cast::CastFrom;
16use mz_proto::{RustType, TryFromProtoError};
17use proptest::arbitrary::Arbitrary;
18use proptest::strategy::{BoxedStrategy, Strategy};
19use serde::{Deserialize, Serialize};
20
21include!(concat!(env!("OUT_DIR"), "/mz_repr.adt.varchar.rs"));
22
23pub const MAX_MAX_LENGTH: u32 = 10_485_760;
25
26#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
31pub struct VarChar<S: AsRef<str>>(pub S);
32
33#[derive(
39 Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize, MzReflect,
40)]
41pub struct VarCharMaxLength(pub(crate) u32);
42
43impl VarCharMaxLength {
44 pub fn into_u32(self) -> u32 {
46 self.0
47 }
48}
49
50impl TryFrom<i64> for VarCharMaxLength {
51 type Error = InvalidVarCharMaxLengthError;
52
53 fn try_from(max_length: i64) -> Result<Self, Self::Error> {
54 match u32::try_from(max_length) {
55 Ok(max_length) if max_length > 0 && max_length < MAX_MAX_LENGTH => {
56 Ok(VarCharMaxLength(max_length))
57 }
58 _ => Err(InvalidVarCharMaxLengthError),
59 }
60 }
61}
62
63impl RustType<ProtoVarCharMaxLength> for VarCharMaxLength {
64 fn into_proto(&self) -> ProtoVarCharMaxLength {
65 ProtoVarCharMaxLength { value: self.0 }
66 }
67
68 fn from_proto(proto: ProtoVarCharMaxLength) -> Result<Self, TryFromProtoError> {
69 Ok(VarCharMaxLength(proto.value))
70 }
71}
72
73impl Arbitrary for VarCharMaxLength {
74 type Parameters = ();
75 type Strategy = BoxedStrategy<VarCharMaxLength>;
76
77 fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
78 proptest::arbitrary::any::<u32>()
79 .prop_map(|len| VarCharMaxLength(len % 300))
83 .boxed()
84 }
85}
86
87#[derive(Debug, Clone)]
90pub struct InvalidVarCharMaxLengthError;
91
92impl fmt::Display for InvalidVarCharMaxLengthError {
93 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
94 write!(
95 f,
96 "length for type character varying must be between 1 and {}",
97 MAX_MAX_LENGTH
98 )
99 }
100}
101
102impl Error for InvalidVarCharMaxLengthError {}
103
104pub fn format_str(
105 s: &str,
106 length: Option<VarCharMaxLength>,
107 fail_on_len: bool,
108) -> Result<&str, anyhow::Error> {
109 Ok(match length {
110 Some(l) => {
114 let l = usize::cast_from(l.into_u32());
115 match s.char_indices().nth(l) {
116 None => s,
117 Some((idx, _)) => {
118 if !fail_on_len || s[idx..].chars().all(|c| c.is_ascii_whitespace()) {
119 &s[..idx]
120 } else {
121 bail!("{} exceeds maximum length of {}", s, l)
122 }
123 }
124 }
125 }
126 None => s,
127 })
128}
129
130#[cfg(test)]
131mod tests {
132 use mz_ore::assert_ok;
133 use mz_proto::protobuf_roundtrip;
134 use proptest::prelude::*;
135
136 use super::*;
137
138 proptest! {
139 #[mz_ore::test]
140 fn var_char_max_length_protobuf_roundtrip(expect in any::<VarCharMaxLength>()) {
141 let actual = protobuf_roundtrip::<_, ProtoVarCharMaxLength>(&expect);
142 assert_ok!(actual);
143 assert_eq!(actual.unwrap(), expect);
144 }
145 }
146}