use std::fmt::Display;
use std::str::FromStr;
use once_cell::sync::Lazy;
use regex::Regex;
use crate::operation::OperationValue;
use crate::schema::error::FieldTypeError;
use crate::schema::SchemaId;
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum FieldType {
Boolean,
Integer,
Float,
String,
Relation(SchemaId),
RelationList(SchemaId),
PinnedRelation(SchemaId),
PinnedRelationList(SchemaId),
}
impl Display for FieldType {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let field_type_str = match self {
FieldType::Boolean => "bool".to_string(),
FieldType::Integer => "int".to_string(),
FieldType::Float => "float".to_string(),
FieldType::String => "str".to_string(),
FieldType::Relation(schema_id) => format!("relation({})", schema_id),
FieldType::RelationList(schema_id) => {
format!("relation_list({})", schema_id)
}
FieldType::PinnedRelation(schema_id) => {
format!("pinned_relation({})", schema_id)
}
FieldType::PinnedRelationList(schema_id) => {
format!("pinned_relation_list({})", schema_id)
}
};
write!(f, "{}", field_type_str)
}
}
impl From<FieldType> for OperationValue {
fn from(field_type: FieldType) -> OperationValue {
OperationValue::String(field_type.to_string())
}
}
impl FromStr for FieldType {
type Err = FieldTypeError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let text_match = match s {
"bool" => Ok(FieldType::Boolean),
"int" => Ok(FieldType::Integer),
"float" => Ok(FieldType::Float),
"str" => Ok(FieldType::String),
_ => Err(FieldTypeError::InvalidFieldType(s.into())),
};
if text_match.is_ok() {
return text_match;
}
static RELATION_REGEX: Lazy<Regex> = Lazy::new(|| {
Regex::new(r"(\w+)(\((.+)\))?").unwrap()
});
let groups = RELATION_REGEX.captures(s).unwrap();
let relation_type = groups.get(1).map(|group_match| group_match.as_str());
let schema_id = groups.get(3).map(|group_match| group_match.as_str());
match (relation_type, schema_id) {
(Some("relation"), Some(schema_id)) => {
Ok(FieldType::Relation(SchemaId::from_str(schema_id)?))
}
(Some("relation_list"), Some(schema_id)) => {
Ok(FieldType::RelationList(SchemaId::from_str(schema_id)?))
}
(Some("pinned_relation"), Some(schema_id)) => {
Ok(FieldType::PinnedRelation(SchemaId::from_str(schema_id)?))
}
(Some("pinned_relation_list"), Some(schema_id)) => Ok(FieldType::PinnedRelationList(
SchemaId::from_str(schema_id)?,
)),
_ => Err(FieldTypeError::InvalidFieldType(s.into())),
}
}
}
#[cfg(test)]
mod tests {
use crate::schema::SchemaId;
use super::FieldType;
#[test]
fn to_string() {
assert_eq!(FieldType::Boolean.to_string(), "bool");
assert_eq!(FieldType::Integer.to_string(), "int");
assert_eq!(FieldType::Float.to_string(), "float");
assert_eq!(FieldType::String.to_string(), "str");
assert_eq!(
FieldType::Relation(SchemaId::SchemaFieldDefinition(1)).to_string(),
"relation(schema_field_definition_v1)"
);
assert_eq!(
FieldType::RelationList(SchemaId::SchemaFieldDefinition(1)).to_string(),
"relation_list(schema_field_definition_v1)"
);
assert_eq!(
FieldType::PinnedRelation(SchemaId::SchemaFieldDefinition(1)).to_string(),
"pinned_relation(schema_field_definition_v1)"
);
assert_eq!(
FieldType::PinnedRelationList(SchemaId::SchemaFieldDefinition(1)).to_string(),
"pinned_relation_list(schema_field_definition_v1)"
);
}
#[test]
fn from_str() {
assert_eq!(FieldType::Boolean, "bool".parse().unwrap());
assert_eq!(FieldType::Integer, "int".parse().unwrap());
assert_eq!(FieldType::Float, "float".parse().unwrap());
assert_eq!(FieldType::String, "str".parse().unwrap());
assert_eq!(
FieldType::Relation(SchemaId::SchemaFieldDefinition(1)),
"relation(schema_field_definition_v1)".parse().unwrap()
);
assert_eq!(
FieldType::RelationList(SchemaId::SchemaFieldDefinition(1)),
"relation_list(schema_field_definition_v1)".parse().unwrap()
);
assert_eq!(
FieldType::PinnedRelation(SchemaId::SchemaFieldDefinition(1)),
"pinned_relation(schema_field_definition_v1)"
.parse()
.unwrap()
);
assert_eq!(
FieldType::PinnedRelationList(SchemaId::SchemaFieldDefinition(1)),
"pinned_relation_list(schema_field_definition_v1)"
.parse()
.unwrap()
);
let invalid = "relation(no_no_no)".parse::<FieldType>();
assert_eq!(
invalid.unwrap_err().to_string(),
"encountered invalid hash while parsing application schema id: invalid hex encoding \
in hash string"
);
}
#[test]
fn invalid_type_string() {
assert!("poopy".parse::<FieldType>().is_err());
}
}