diff --git a/minisql/src/type_system.rs b/minisql/src/type_system.rs index 92cabb4..08a2352 100644 --- a/minisql/src/type_system.rs +++ b/minisql/src/type_system.rs @@ -28,7 +28,6 @@ pub enum Value { None(DbType), } - #[derive(Debug, Eq, Clone, PartialEq, Serialize, Deserialize)] #[serde(try_from = "String", into = "String")] pub enum IndexableValue { @@ -91,6 +90,37 @@ impl DbType { } } + fn to_json_key_string(self) -> String { + match self { + DbType::String => format!("String()"), + DbType::Int => format!("Int()"), + DbType::Number => format!("Number()"), + DbType::Uuid => format!("Uuid()"), + DbType::Option(type_) => format!("Option({})", type_.to_json_key_string()), + } + } + + fn parse_json_key_string(str: &str) -> Result { + if !str.ends_with(')') { + return Err(format!("Invalid DbType: {}", str)); + } + + if str.starts_with("String(") { + Ok(DbType::String) + } else if str.starts_with("Int(") { + Ok(DbType::Int) + } else if str.starts_with("Number(") { + Ok(DbType::Number) + } else if str.starts_with("Uuid(") { + Ok(DbType::Uuid) + } else if str.starts_with("Option(") { + let s = &str[7..str.len() - 1]; + Ok(DbType::Option(Box::new(Self::parse_json_key_string(s)?))) + } else { + Err(format!("Invalid DbType: {}", str)) + } + } + pub fn type_oid(&self) -> i32 { // match self { // Self::String => 25, @@ -135,6 +165,58 @@ impl Value { } } + fn to_json_key_string(self) -> String { + match self { + Self::Number(x) => format!("Number({x})"), + Self::String(s) => format!("String({s})"), + Self::Int(i) => format!("Int({i})"), + Self::Uuid(u) => format!("Uuid({u})"), + Self::Some(val) => format!("Some({})", val.to_json_key_string()), + Self::None(_type_) => "None()".to_string(), + } + } + + // We don't really need this, since only indexable values are keys in maps. It is here for consistency. + fn parse_json_key_string(str: &str) -> Result { + if !str.ends_with(')') { + return Err(format!("Invalid IndexableValue: {}", str)); + } + + if str.starts_with("Number(") { + let s = str[7..str.len() - 1].to_string(); + let n = s + .parse::() + .map_err(|e| format!("Invalid Number: {}", e))?; + Ok(Self::Number(n)) + } else if str.starts_with("String(") { + let s = str[7..str.len() - 1].to_string(); + Ok(Self::String(s)) + } else if str.starts_with("Int(") { + let s = str[4..str.len() - 1].to_string(); + let i = s + .parse::() + .map_err(|e| format!("Invalid Int: {}", e))?; + Ok(Self::Int(i)) + } else if str.starts_with("Uuid(") { + let s = str[5..str.len() - 1].to_string(); + let u = s + .parse::() + .map_err(|e| format!("Invalid UUID: {}", e))?; + Ok(Self::Uuid(u)) + } else if str.starts_with("Some(") { + let s = str[5..str.len() - 1].to_string(); + let val: Value = Self::parse_json_key_string(&s)?; + Ok(Self::Some(Box::new(val))) + } else if str.starts_with("None(") { + let s = str[5..str.len() - 1].to_string(); + let type_: DbType = TryFrom::try_from(s)?; + Ok(Value::None(type_)) + } else { + Err(format!("Invalid IndexableValue: {}", str)) + } + } + + pub fn as_text_bytes(&self) -> Vec { // match self { // Self::Number(n) => format!("{n}").into_bytes(), @@ -179,171 +261,105 @@ impl Value { } } -// ===formatting=== -fn db_type_to_string(type_: DbType) -> String { - match type_ { - DbType::String => format!("String()"), - DbType::Int => format!("Int()"), - DbType::Number => format!("Number()"), - DbType::Uuid => format!("Uuid()"), - DbType::Option(type_) => format!("Option({})", db_type_to_string(*type_)), +impl IndexableValue { + fn to_value(self) -> Value { + match self { + IndexableValue::String(str) => Value::String(str), + IndexableValue::Int(n) => Value::Int(n), + IndexableValue::Uuid(id) => Value::Uuid(id), + IndexableValue::Some(indexable_value) => Value::Some(Box::new(indexable_value.to_value())), + IndexableValue::None(type_) => Value::None(type_), + } + } + + fn to_json_key_string(self) -> String { + match self { + IndexableValue::String(s) => format!("String({s})"), + IndexableValue::Int(i) => format!("Int({i})"), + IndexableValue::Uuid(u) => format!("Uuid({u})"), + IndexableValue::Some(val) => format!("Some({})", val.to_json_key_string()), + IndexableValue::None(_type_) => "None()".to_string(), + } + } + + fn parse_json_key_string(str: &str) -> Result { + if !str.ends_with(')') { + return Err(format!("Invalid IndexableValue: {}", str)); + } + + if str.starts_with("String(") { + let s = str[7..str.len() - 1].to_string(); + Ok(IndexableValue::String(s)) + } else if str.starts_with("Int(") { + let s = str[4..str.len() - 1].to_string(); + let i = s + .parse::() + .map_err(|e| format!("Invalid Int: {}", e))?; + Ok(IndexableValue::Int(i)) + } else if str.starts_with("Uuid(") { + let s = str[5..str.len() - 1].to_string(); + let u = s + .parse::() + .map_err(|e| format!("Invalid UUID: {}", e))?; + Ok(IndexableValue::Uuid(u)) + } else if str.starts_with("Some(") { + let s = str[5..str.len() - 1].to_string(); + let val: IndexableValue = Self::parse_json_key_string(&s)?; + Ok(IndexableValue::Some(Box::new(val))) + } else if str.starts_with("None(") { + let s = str[5..str.len() - 1].to_string(); + let type_: DbType = DbType::parse_json_key_string(&s)?; + Ok(IndexableValue::None(type_)) + } else { + Err(format!("Invalid IndexableValue: {}", str)) + } } } -fn indexable_value_to_string(value: IndexableValue) -> String { - match value { - IndexableValue::String(s) => format!("String({s})"), - IndexableValue::Int(i) => format!("Int({i})"), - IndexableValue::Uuid(u) => format!("Uuid({u})"), - IndexableValue::Some(val) => format!("Some({})", indexable_value_to_string(*val)), - IndexableValue::None(_type_) => "None()".to_string(), +impl From for Value { + fn from(indexable_value: IndexableValue) -> Self { + indexable_value.to_value() } } -fn value_to_string(value: Value) -> String { - match value { - Value::Number(x) => format!("Number({x})"), - Value::String(s) => format!("String({s})"), - Value::Int(i) => format!("Int({i})"), - Value::Uuid(u) => format!("Uuid({u})"), - Value::Some(val) => format!("Some({})", value_to_string(*val)), - Value::None(_type_) => "None()".to_string(), - } -} - -// ===parsing=== -fn parse_db_type(str: &str) -> Result { - if !str.ends_with(')') { - return Err(format!("Invalid DbType: {}", str)); - } - - if str.starts_with("String(") { - Ok(DbType::String) - } else if str.starts_with("Int(") { - Ok(DbType::Int) - } else if str.starts_with("Number(") { - Ok(DbType::Number) - } else if str.starts_with("Uuid(") { - Ok(DbType::Uuid) - } else if str.starts_with("Option(") { - let s = &str[7..str.len() - 1]; - Ok(DbType::Option(Box::new(parse_db_type(s)?))) - } else { - Err(format!("Invalid DbType: {}", str)) - } -} - -fn parse_value(str: &str) -> Result { - if !str.ends_with(')') { - return Err(format!("Invalid IndexableValue: {}", str)); - } - - if str.starts_with("Number(") { - let s = str[7..str.len() - 1].to_string(); - let n = s - .parse::() - .map_err(|e| format!("Invalid Number: {}", e))?; - Ok(Value::Number(n)) - } else if str.starts_with("String(") { - let s = str[7..str.len() - 1].to_string(); - Ok(Value::String(s)) - } else if str.starts_with("Int(") { - let s = str[4..str.len() - 1].to_string(); - let i = s - .parse::() - .map_err(|e| format!("Invalid Int: {}", e))?; - Ok(Value::Int(i)) - } else if str.starts_with("Uuid(") { - let s = str[5..str.len() - 1].to_string(); - let u = s - .parse::() - .map_err(|e| format!("Invalid UUID: {}", e))?; - Ok(Value::Uuid(u)) - } else if str.starts_with("Some(") { - let s = str[5..str.len() - 1].to_string(); - let val: Value = parse_value(&s)?; - Ok(Value::Some(Box::new(val))) - } else if str.starts_with("None(") { - let s = str[5..str.len() - 1].to_string(); - let type_: DbType = TryFrom::try_from(s)?; - Ok(Value::None(type_)) - } else { - Err(format!("Invalid IndexableValue: {}", str)) - } -} - -fn parse_indexable_value(str: &str) -> Result { - if !str.ends_with(')') { - return Err(format!("Invalid IndexableValue: {}", str)); - } - - if str.starts_with("String(") { - let s = str[7..str.len() - 1].to_string(); - Ok(IndexableValue::String(s)) - } else if str.starts_with("Int(") { - let s = str[4..str.len() - 1].to_string(); - let i = s - .parse::() - .map_err(|e| format!("Invalid Int: {}", e))?; - Ok(IndexableValue::Int(i)) - } else if str.starts_with("Uuid(") { - let s = str[5..str.len() - 1].to_string(); - let u = s - .parse::() - .map_err(|e| format!("Invalid UUID: {}", e))?; - Ok(IndexableValue::Uuid(u)) - } else if str.starts_with("Some(") { - let s = str[5..str.len() - 1].to_string(); - let val: IndexableValue = parse_indexable_value(&s)?; - Ok(IndexableValue::Some(Box::new(val))) - } else if str.starts_with("None(") { - let s = str[5..str.len() - 1].to_string(); - let type_: DbType = parse_db_type(&s)?; - Ok(IndexableValue::None(type_)) - } else { - Err(format!("Invalid IndexableValue: {}", str)) - } -} - - // Own string serialization so enums can be used as keys in maps impl From for String { fn from(value: IndexableValue) -> Self { - indexable_value_to_string(value) + value.to_json_key_string() } } impl From for String { fn from(type_: DbType) -> Self { - db_type_to_string(type_) + type_.to_json_key_string() } } impl TryFrom for DbType { type Error = String; fn try_from(str: String) -> Result { - parse_db_type(&str) + Self::parse_json_key_string(&str) } } impl TryFrom for IndexableValue { type Error = String; fn try_from(str: String) -> Result { - parse_indexable_value(&str) + Self::parse_json_key_string(&str) } } impl From for String { fn from(value: Value) -> Self { - value_to_string(value) + value.to_json_key_string() } } impl TryFrom for Value { type Error = String; fn try_from(value: String) -> Result { - parse_value(&value) + Value::parse_json_key_string(&value) } }