p2panda_rs/schema/
field_types.rs1use std::fmt::Display;
4use std::str::FromStr;
5
6use once_cell::sync::Lazy;
7use regex::Regex;
8
9use crate::operation::OperationValue;
10use crate::schema::error::FieldTypeError;
11use crate::schema::SchemaId;
12
13#[derive(Clone, Debug, Eq, PartialEq)]
25pub enum FieldType {
26 Boolean,
28
29 Bytes,
31
32 Integer,
34
35 Float,
37
38 String,
40
41 Relation(SchemaId),
44
45 RelationList(SchemaId),
48
49 PinnedRelation(SchemaId),
52
53 PinnedRelationList(SchemaId),
56}
57
58impl Display for FieldType {
60 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
62 let field_type_str = match self {
63 FieldType::Boolean => "bool".to_string(),
64 FieldType::Bytes => "bytes".to_string(),
65 FieldType::Integer => "int".to_string(),
66 FieldType::Float => "float".to_string(),
67 FieldType::String => "str".to_string(),
68 FieldType::Relation(schema_id) => format!("relation({})", schema_id),
69 FieldType::RelationList(schema_id) => {
70 format!("relation_list({})", schema_id)
71 }
72 FieldType::PinnedRelation(schema_id) => {
73 format!("pinned_relation({})", schema_id)
74 }
75 FieldType::PinnedRelationList(schema_id) => {
76 format!("pinned_relation_list({})", schema_id)
77 }
78 };
79
80 write!(f, "{}", field_type_str)
81 }
82}
83
84impl From<FieldType> for OperationValue {
85 fn from(field_type: FieldType) -> OperationValue {
86 OperationValue::String(field_type.to_string())
87 }
88}
89
90impl FromStr for FieldType {
91 type Err = FieldTypeError;
92
93 fn from_str(s: &str) -> Result<Self, Self::Err> {
94 let text_match = match s {
96 "bool" => Ok(FieldType::Boolean),
97 "int" => Ok(FieldType::Integer),
98 "float" => Ok(FieldType::Float),
99 "str" => Ok(FieldType::String),
100 "bytes" => Ok(FieldType::Bytes),
101 _ => Err(FieldTypeError::InvalidFieldType(s.into())),
102 };
103
104 if text_match.is_ok() {
105 return text_match;
106 }
107
108 static RELATION_REGEX: Lazy<Regex> = Lazy::new(|| {
111 Regex::new(r"(\w+)(\((.+)\))?").unwrap()
113 });
114
115 let groups = RELATION_REGEX.captures(s).unwrap();
117 let relation_type = groups.get(1).map(|group_match| group_match.as_str());
118 let schema_id = groups.get(3).map(|group_match| group_match.as_str());
119
120 match (relation_type, schema_id) {
121 (Some("relation"), Some(schema_id)) => {
122 Ok(FieldType::Relation(SchemaId::from_str(schema_id)?))
123 }
124 (Some("relation_list"), Some(schema_id)) => {
125 Ok(FieldType::RelationList(SchemaId::from_str(schema_id)?))
126 }
127 (Some("pinned_relation"), Some(schema_id)) => {
128 Ok(FieldType::PinnedRelation(SchemaId::from_str(schema_id)?))
129 }
130 (Some("pinned_relation_list"), Some(schema_id)) => Ok(FieldType::PinnedRelationList(
131 SchemaId::from_str(schema_id)?,
132 )),
133 _ => Err(FieldTypeError::InvalidFieldType(s.into())),
134 }
135 }
136}
137
138#[cfg(test)]
139mod tests {
140 use crate::schema::SchemaId;
141
142 use super::FieldType;
143
144 #[test]
145 fn to_string() {
146 assert_eq!(FieldType::Boolean.to_string(), "bool");
147 assert_eq!(FieldType::Integer.to_string(), "int");
148 assert_eq!(FieldType::Float.to_string(), "float");
149 assert_eq!(FieldType::String.to_string(), "str");
150 assert_eq!(FieldType::Bytes.to_string(), "bytes");
151 assert_eq!(
152 FieldType::Relation(SchemaId::SchemaFieldDefinition(1)).to_string(),
153 "relation(schema_field_definition_v1)"
154 );
155 assert_eq!(
156 FieldType::RelationList(SchemaId::SchemaFieldDefinition(1)).to_string(),
157 "relation_list(schema_field_definition_v1)"
158 );
159 assert_eq!(
160 FieldType::PinnedRelation(SchemaId::SchemaFieldDefinition(1)).to_string(),
161 "pinned_relation(schema_field_definition_v1)"
162 );
163 assert_eq!(
164 FieldType::PinnedRelationList(SchemaId::SchemaFieldDefinition(1)).to_string(),
165 "pinned_relation_list(schema_field_definition_v1)"
166 );
167 }
168
169 #[test]
170 fn from_str() {
171 assert_eq!(FieldType::Boolean, "bool".parse().unwrap());
172 assert_eq!(FieldType::Integer, "int".parse().unwrap());
173 assert_eq!(FieldType::Float, "float".parse().unwrap());
174 assert_eq!(FieldType::String, "str".parse().unwrap());
175 assert_eq!(FieldType::Bytes, "bytes".parse().unwrap());
176 assert_eq!(
177 FieldType::Relation(SchemaId::SchemaFieldDefinition(1)),
178 "relation(schema_field_definition_v1)".parse().unwrap()
179 );
180 assert_eq!(
181 FieldType::RelationList(SchemaId::SchemaFieldDefinition(1)),
182 "relation_list(schema_field_definition_v1)".parse().unwrap()
183 );
184 assert_eq!(
185 FieldType::PinnedRelation(SchemaId::SchemaFieldDefinition(1)),
186 "pinned_relation(schema_field_definition_v1)"
187 .parse()
188 .unwrap()
189 );
190 assert_eq!(
191 FieldType::PinnedRelationList(SchemaId::SchemaFieldDefinition(1)),
192 "pinned_relation_list(schema_field_definition_v1)"
193 .parse()
194 .unwrap()
195 );
196
197 let invalid = "relation(no_no_no)".parse::<FieldType>();
198 assert_eq!(
199 invalid.unwrap_err().to_string(),
200 "encountered invalid hash while parsing application schema id: invalid hex encoding \
201 in hash string"
202 );
203 }
204
205 #[test]
206 fn invalid_type_string() {
207 assert!("poopy".parse::<FieldType>().is_err());
208 }
209}