Introduce optional type/value
This commit is contained in:
parent
597c52caa0
commit
339686f5c5
4 changed files with 255 additions and 204 deletions
|
|
@ -85,18 +85,18 @@ impl Table {
|
|||
value: Value,
|
||||
) -> DbResult<impl Iterator<Item = RestrictedRow> + '_> {
|
||||
let restrict_columns_of_row = move |row: Row| row.restrict_columns(&selected_columns);
|
||||
match value {
|
||||
Value::Indexable(value) => match self.fetch_ids_from_index(column, &value)? {
|
||||
match value.to_indexable() {
|
||||
Some(indexable_value) => match self.fetch_ids_from_index(column, &indexable_value)? {
|
||||
Some(ids) => Ok(self
|
||||
.get_rows_by_ids(ids)
|
||||
.into_iter()
|
||||
.map(restrict_columns_of_row)),
|
||||
None => Ok(self
|
||||
.get_rows_by_value(column, &Value::Indexable(value))
|
||||
.get_rows_by_value(column, &value)
|
||||
.into_iter()
|
||||
.map(restrict_columns_of_row)),
|
||||
},
|
||||
_ => Ok(self
|
||||
None => Ok(self
|
||||
.get_rows_by_value(column, &value)
|
||||
.into_iter()
|
||||
.map(restrict_columns_of_row)),
|
||||
|
|
@ -113,8 +113,8 @@ impl Table {
|
|||
}
|
||||
|
||||
for (column, column_index) in &mut self.indexes {
|
||||
if let Value::Indexable(val) = &row[*column] {
|
||||
column_index.add(val.clone(), id)
|
||||
if let Some(indexable_value) = &row[*column].to_indexable() {
|
||||
column_index.add(indexable_value.clone(), id)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -127,8 +127,8 @@ impl Table {
|
|||
match self.rows.remove(&id) {
|
||||
Some(row) => {
|
||||
for (column, column_index) in &mut self.indexes {
|
||||
if let Value::Indexable(value) = &row[*column] {
|
||||
let _ = column_index.remove(value, id);
|
||||
if let Some(indexable_value) = &row[*column].to_indexable() {
|
||||
let _ = column_index.remove(indexable_value, id);
|
||||
};
|
||||
}
|
||||
1
|
||||
|
|
@ -168,12 +168,12 @@ impl Table {
|
|||
}
|
||||
|
||||
pub fn delete_rows_where_eq(&mut self, column: Column, value: Value) -> DbResult<usize> {
|
||||
match value {
|
||||
Value::Indexable(value) => match self.fetch_ids_from_index(column, &value)? {
|
||||
match value.to_indexable() {
|
||||
Some(indexable_value) => match self.fetch_ids_from_index(column, &indexable_value)? {
|
||||
Some(ids) => Ok(self.delete_rows_by_ids(ids)),
|
||||
None => Ok(self.delete_rows_by_value(column, &Value::Indexable(value))),
|
||||
None => Ok(self.delete_rows_by_value(column, &value)),
|
||||
},
|
||||
_ => Ok(self.delete_rows_by_value(column, &value)),
|
||||
None => Ok(self.delete_rows_by_value(column, &value)),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -226,9 +226,9 @@ fn update_index_from_table(
|
|||
column: Column,
|
||||
) -> DbResult<()> {
|
||||
for (id, row) in &table.rows {
|
||||
let value = match &row[column] {
|
||||
Value::Indexable(value) => value.clone(),
|
||||
_ => {
|
||||
let value = match &row[column].to_indexable() {
|
||||
Some(indexable_value) => indexable_value.clone(),
|
||||
None => {
|
||||
let column_name: ColumnName = table.schema.column_name_from_column(column);
|
||||
return Err(RuntimeError::AttemptToIndexNonIndexableColumn(
|
||||
table.table_name().to_string(),
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use crate::internals::table::Table;
|
|||
use crate::operation::{ColumnSelection, Condition, Operation};
|
||||
use crate::restricted_row::RestrictedRow;
|
||||
use crate::result::DbResult;
|
||||
use crate::schema::{Column, TableName, TablePosition, TableSchema};
|
||||
use crate::schema::{TableName, TablePosition, TableSchema};
|
||||
use bimap::BiMap;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
|
|
@ -491,102 +491,103 @@ mod tests {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn example() {
|
||||
use crate::type_system::{DbType, IndexableValue, Value};
|
||||
use Condition::*;
|
||||
use IndexableValue::*;
|
||||
use Operation::*;
|
||||
use Value::*;
|
||||
// TODO
|
||||
// pub fn example() {
|
||||
// use crate::type_system::{DbType, IndexableValue, Value};
|
||||
// use Condition::*;
|
||||
// use IndexableValue::*;
|
||||
// use Operation::*;
|
||||
// use Value::*;
|
||||
|
||||
let id_column: Column = 0;
|
||||
let name_column: Column = 1;
|
||||
// let age_column: ColumnPosition = 2;
|
||||
// let id_column: Column = 0;
|
||||
// let name_column: Column = 1;
|
||||
// // let age_column: ColumnPosition = 2;
|
||||
|
||||
let users_schema: TableSchema = {
|
||||
TableSchema::new(
|
||||
"users".to_string(),
|
||||
"id".to_string(),
|
||||
vec![
|
||||
"id".to_string(), // 0
|
||||
"name".to_string(), // 1
|
||||
"age".to_string(), // 2
|
||||
],
|
||||
vec![DbType::Uuid, DbType::String, DbType::Int],
|
||||
)
|
||||
};
|
||||
let users_position: TablePosition = 0;
|
||||
// let users_schema: TableSchema = {
|
||||
// TableSchema::new(
|
||||
// "users".to_string(),
|
||||
// "id".to_string(),
|
||||
// vec![
|
||||
// "id".to_string(), // 0
|
||||
// "name".to_string(), // 1
|
||||
// "age".to_string(), // 2
|
||||
// ],
|
||||
// vec![DbType::Uuid, DbType::String, DbType::Int],
|
||||
// )
|
||||
// };
|
||||
// let users_position: TablePosition = 0;
|
||||
|
||||
let mut state = State::new();
|
||||
state
|
||||
.interpret(Operation::CreateTable(users_schema.clone()))
|
||||
.unwrap();
|
||||
// let mut state = State::new();
|
||||
// state
|
||||
// .interpret(Operation::CreateTable(users_schema.clone()))
|
||||
// .unwrap();
|
||||
|
||||
let (id0, name0, age0) = (
|
||||
Indexable(Uuid(0)),
|
||||
Indexable(String("Plato".to_string())),
|
||||
Indexable(Int(64)),
|
||||
);
|
||||
println!("==INSERT Plato==");
|
||||
state
|
||||
.interpret(Insert(
|
||||
users_position,
|
||||
vec![id0.clone(), name0.clone(), age0.clone()],
|
||||
))
|
||||
.unwrap();
|
||||
// let (id0, name0, age0) = (
|
||||
// Indexable(Uuid(0)),
|
||||
// Indexable(String("Plato".to_string())),
|
||||
// Indexable(Int(64)),
|
||||
// );
|
||||
// println!("==INSERT Plato==");
|
||||
// state
|
||||
// .interpret(Insert(
|
||||
// users_position,
|
||||
// vec![id0.clone(), name0.clone(), age0.clone()],
|
||||
// ))
|
||||
// .unwrap();
|
||||
|
||||
let (id1, name1, age1) = (
|
||||
Indexable(Uuid(1)),
|
||||
Indexable(String("Aristotle".to_string())),
|
||||
Indexable(Int(20)),
|
||||
);
|
||||
println!("==INSERT Aristotle==");
|
||||
state
|
||||
.interpret(Insert(
|
||||
users_position,
|
||||
vec![id1.clone(), name1.clone(), age1.clone()],
|
||||
))
|
||||
.unwrap();
|
||||
println!();
|
||||
// let (id1, name1, age1) = (
|
||||
// Indexable(Uuid(1)),
|
||||
// Indexable(String("Aristotle".to_string())),
|
||||
// Indexable(Int(20)),
|
||||
// );
|
||||
// println!("==INSERT Aristotle==");
|
||||
// state
|
||||
// .interpret(Insert(
|
||||
// users_position,
|
||||
// vec![id1.clone(), name1.clone(), age1.clone()],
|
||||
// ))
|
||||
// .unwrap();
|
||||
// println!();
|
||||
|
||||
{
|
||||
let response: Response = state
|
||||
.interpret(Operation::Select(
|
||||
users_position,
|
||||
users_schema.all_selection(),
|
||||
None,
|
||||
))
|
||||
.unwrap();
|
||||
println!("==SELECT ALL==");
|
||||
println!("{:?}", response);
|
||||
println!();
|
||||
}
|
||||
{
|
||||
let response: Response = state
|
||||
.interpret(Select(
|
||||
users_position,
|
||||
users_schema.all_selection(),
|
||||
Some(Eq(id_column, id0.clone())),
|
||||
))
|
||||
.unwrap();
|
||||
println!("==SELECT Plato==");
|
||||
println!("{:?}", response);
|
||||
println!();
|
||||
}
|
||||
// {
|
||||
// let response: Response = state
|
||||
// .interpret(Operation::Select(
|
||||
// users_position,
|
||||
// users_schema.all_selection(),
|
||||
// None,
|
||||
// ))
|
||||
// .unwrap();
|
||||
// println!("==SELECT ALL==");
|
||||
// println!("{:?}", response);
|
||||
// println!();
|
||||
// }
|
||||
// {
|
||||
// let response: Response = state
|
||||
// .interpret(Select(
|
||||
// users_position,
|
||||
// users_schema.all_selection(),
|
||||
// Some(Eq(id_column, id0.clone())),
|
||||
// ))
|
||||
// .unwrap();
|
||||
// println!("==SELECT Plato==");
|
||||
// println!("{:?}", response);
|
||||
// println!();
|
||||
// }
|
||||
|
||||
{
|
||||
{
|
||||
// TODO: Why do I have to write these braces explicitely? Why doesn't Rust compiler
|
||||
// "infer" them?
|
||||
let _delete_response: Response = state
|
||||
.interpret(Delete(users_position, Some(Eq(id_column, id0.clone()))))
|
||||
.unwrap();
|
||||
println!("==DELETE Plato==");
|
||||
}
|
||||
let response: Response = state
|
||||
.interpret(Select(users_position, vec![name_column, id_column], None))
|
||||
.unwrap();
|
||||
println!("==SELECT All==");
|
||||
println!("{:?}", response);
|
||||
println!();
|
||||
}
|
||||
}
|
||||
// {
|
||||
// {
|
||||
// // TODO: Why do I have to write these braces explicitely? Why doesn't Rust compiler
|
||||
// // "infer" them?
|
||||
// let _delete_response: Response = state
|
||||
// .interpret(Delete(users_position, Some(Eq(id_column, id0.clone()))))
|
||||
// .unwrap();
|
||||
// println!("==DELETE Plato==");
|
||||
// }
|
||||
// let response: Response = state
|
||||
// .interpret(Select(users_position, vec![name_column, id_column], None))
|
||||
// .unwrap();
|
||||
// println!("==SELECT All==");
|
||||
// println!("{:?}", response);
|
||||
// println!();
|
||||
// }
|
||||
// }
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use crate::internals::row::Row;
|
||||
use crate::operation::{ColumnSelection, InsertionValues};
|
||||
use crate::result::DbResult;
|
||||
use crate::type_system::{DbType, IndexableValue, Uuid, Value};
|
||||
use crate::type_system::{DbType, IndexableValue, Uuid};
|
||||
use bimap::BiMap;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
|
|
@ -51,7 +51,7 @@ impl TableSchema {
|
|||
}
|
||||
|
||||
pub fn column_type(&self, column: Column) -> DbType {
|
||||
self.types[column]
|
||||
self.types[column].clone()
|
||||
}
|
||||
|
||||
pub fn get_columns(&self) -> Vec<&ColumnName> {
|
||||
|
|
@ -88,7 +88,7 @@ impl TableSchema {
|
|||
|
||||
pub fn get_type_at(&self, column_name: &ColumnName) -> Option<DbType> {
|
||||
let position = self.get_column(column_name)?;
|
||||
self.types.get(position).copied()
|
||||
self.types.get(position).cloned()
|
||||
}
|
||||
|
||||
pub fn is_primary(&self, column: Column) -> bool {
|
||||
|
|
@ -115,8 +115,13 @@ impl TableSchema {
|
|||
let row: Row = Row::new_from_insertion_values(insertion_values);
|
||||
|
||||
let id: Uuid = match row.get(self.primary_key) {
|
||||
Some(Value::Indexable(IndexableValue::Uuid(id))) => *id,
|
||||
Some(value) => {
|
||||
match value.to_indexable() {
|
||||
Some(IndexableValue::Uuid(id)) => id,
|
||||
Some(_) => unreachable!(), // SAFETY: Should be guaranteed by validation (type-safety)
|
||||
None => unreachable!(), // SAFETY: Should be guaranteed by validation (type-safety)
|
||||
}
|
||||
}
|
||||
None => unreachable!(), // SAFETY: Should be guaranteed by validation (missing columns)
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,36 +1,55 @@
|
|||
use crate::error::TypeConversionError;
|
||||
use std::cmp::Ordering;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
// ==============Types================
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, Ord, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum DbType {
|
||||
String,
|
||||
Int,
|
||||
Number,
|
||||
Uuid,
|
||||
Option(Box<DbType>)
|
||||
}
|
||||
|
||||
impl PartialOrd for DbType {
|
||||
// TODO: Explain why we need this (because of IndexableValue::None contains a type)
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
// ==============Values================
|
||||
pub type Uuid = u64;
|
||||
|
||||
// TODO: What about nulls? I would rather not have that in SQL, it sucks.
|
||||
// I would rather have non-nullable values by default,
|
||||
// and something like an explicit Option type for nulls.
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(try_from = "String", into = "String")]
|
||||
pub enum Value {
|
||||
Number(f64), // TODO: Can't put floats as keys in maps, since they don't implement Eq. What to
|
||||
// do?
|
||||
Indexable(IndexableValue),
|
||||
Number(f64),
|
||||
String(String),
|
||||
Int(u64),
|
||||
Uuid(Uuid),
|
||||
Some(Box<Value>),
|
||||
// Note that without polymorphic types,
|
||||
// we can't type None on its own. Hence we require additional type information in the value
|
||||
// itself.
|
||||
None(DbType),
|
||||
}
|
||||
|
||||
#[derive(Debug, Ord, Eq, Clone, PartialOrd, PartialEq, Serialize, Deserialize)]
|
||||
|
||||
#[derive(Debug, Ord, Eq, Clone, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(try_from = "String", into = "String")]
|
||||
pub enum IndexableValue {
|
||||
String(String),
|
||||
Int(u64),
|
||||
Uuid(Uuid),
|
||||
// TODO: what about null?
|
||||
Some(Box<IndexableValue>),
|
||||
None(DbType), // See Value::None
|
||||
}
|
||||
|
||||
impl PartialOrd for IndexableValue {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl DbType {
|
||||
|
|
@ -40,25 +59,28 @@ impl DbType {
|
|||
Self::Int => true,
|
||||
Self::Number => false,
|
||||
Self::Uuid => true,
|
||||
Self::Option(type_) => type_.is_indexable()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn type_oid(&self) -> i32 {
|
||||
match self {
|
||||
Self::String => 25,
|
||||
Self::Int => 23,
|
||||
Self::Number => 701,
|
||||
Self::Uuid => 2950,
|
||||
}
|
||||
// match self {
|
||||
// Self::String => 25,
|
||||
// Self::Int => 23,
|
||||
// Self::Number => 701,
|
||||
// Self::Uuid => 2950,
|
||||
// }
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub fn type_size(&self) -> i16 {
|
||||
match self {
|
||||
Self::String => -2, // null terminated string
|
||||
Self::Int => 8,
|
||||
Self::Number => 8,
|
||||
Self::Uuid => 16,
|
||||
}
|
||||
// match self {
|
||||
// Self::String => -2, // null terminated string
|
||||
// Self::Int => 8,
|
||||
// Self::Number => 8,
|
||||
// Self::Uuid => 16,
|
||||
// }
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -66,63 +88,84 @@ impl Value {
|
|||
pub fn to_type(&self) -> DbType {
|
||||
match self {
|
||||
Self::Number(_) => DbType::Number,
|
||||
Self::Indexable(val) => match val {
|
||||
IndexableValue::String(_) => DbType::String,
|
||||
IndexableValue::Int(_) => DbType::Int,
|
||||
IndexableValue::Uuid(_) => DbType::Uuid,
|
||||
},
|
||||
Self::String(_) => DbType::String,
|
||||
Self::Int(_) => DbType::Int,
|
||||
Self::Uuid(_) => DbType::Uuid,
|
||||
Self::Some(val) => DbType::Option(Box::new(val.to_type())),
|
||||
Self::None(type_) => type_.clone(),
|
||||
}
|
||||
}
|
||||
pub fn as_text_bytes(&self) -> Vec<u8> {
|
||||
|
||||
pub fn to_indexable(&self) -> Option<IndexableValue> {
|
||||
match self {
|
||||
Self::Number(n) => format!("{n}").into_bytes(),
|
||||
Self::Indexable(i) => match i {
|
||||
IndexableValue::String(s) => format!("{s}\0").into_bytes(),
|
||||
IndexableValue::Int(i) => format!("{i}").into_bytes(),
|
||||
IndexableValue::Uuid(u) => format!("{u}").into_bytes(),
|
||||
},
|
||||
Self::Number(_) => None,
|
||||
Self::String(s) => Some(IndexableValue::String(s.to_string())),
|
||||
Self::Int(n) => Some(IndexableValue::Int(*n)),
|
||||
Self::Uuid(id) => Some(IndexableValue::Uuid(*id)),
|
||||
Self::Some(val) => val.to_indexable(),
|
||||
Self::None(type_) => Some(IndexableValue::None(type_.clone())),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_text_bytes(&self) -> Vec<u8> {
|
||||
// match self {
|
||||
// Self::Number(n) => format!("{n}").into_bytes(),
|
||||
// Self::Indexable(i) => match i {
|
||||
// IndexableValue::String(s) => format!("{s}\0").into_bytes(),
|
||||
// IndexableValue::Int(i) => format!("{i}").into_bytes(),
|
||||
// IndexableValue::Uuid(u) => format!("{u}").into_bytes(),
|
||||
// },
|
||||
// }
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub fn from_text_bytes(
|
||||
bytes: &[u8],
|
||||
type_oid: i32,
|
||||
type_size: i16,
|
||||
) -> Result<Value, TypeConversionError> {
|
||||
match (type_oid, type_size) {
|
||||
(701, 8) => {
|
||||
let s = std::str::from_utf8(bytes)?;
|
||||
let n = s.parse::<f64>()?;
|
||||
Ok(Value::Number(n))
|
||||
// match (type_oid, type_size) {
|
||||
// (701, 8) => {
|
||||
// let s = std::str::from_utf8(bytes)?;
|
||||
// let n = s.parse::<f64>()?;
|
||||
// Ok(Value::Number(n))
|
||||
// }
|
||||
// (25, -2) => {
|
||||
// let s = std::str::from_utf8(bytes)?;
|
||||
// let s = &s[..s.len() - 1]; // remove null terminator
|
||||
// Ok(Value::Indexable(IndexableValue::String(s.to_string())))
|
||||
// }
|
||||
// (23, 8) => {
|
||||
// let s = std::str::from_utf8(bytes)?;
|
||||
// let n = s.parse::<u64>()?;
|
||||
// Ok(Value::Indexable(IndexableValue::Int(n)))
|
||||
// }
|
||||
// (2950, 16) => {
|
||||
// let s = std::str::from_utf8(bytes)?;
|
||||
// let n = s.parse::<Uuid>()?;
|
||||
// Ok(Value::Indexable(IndexableValue::Uuid(n)))
|
||||
// }
|
||||
// (oid, size) => Err(TypeConversionError::UnknownType { oid, size }),
|
||||
// }
|
||||
todo!()
|
||||
}
|
||||
(25, -2) => {
|
||||
let s = std::str::from_utf8(bytes)?;
|
||||
let s = &s[..s.len() - 1]; // remove null terminator
|
||||
Ok(Value::Indexable(IndexableValue::String(s.to_string())))
|
||||
}
|
||||
(23, 8) => {
|
||||
let s = std::str::from_utf8(bytes)?;
|
||||
let n = s.parse::<u64>()?;
|
||||
Ok(Value::Indexable(IndexableValue::Int(n)))
|
||||
}
|
||||
(2950, 16) => {
|
||||
let s = std::str::from_utf8(bytes)?;
|
||||
let n = s.parse::<Uuid>()?;
|
||||
Ok(Value::Indexable(IndexableValue::Uuid(n)))
|
||||
}
|
||||
(oid, size) => Err(TypeConversionError::UnknownType { oid, size }),
|
||||
}
|
||||
|
||||
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::Some(val) => todo!(),
|
||||
IndexableValue::None(type_) => "None()".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
// Own string serialization so enums can be used as keys in maps
|
||||
impl From<IndexableValue> for String {
|
||||
fn from(value: IndexableValue) -> Self {
|
||||
match value {
|
||||
IndexableValue::String(s) => format!("String({s})"),
|
||||
IndexableValue::Int(i) => format!("Int({i})"),
|
||||
IndexableValue::Uuid(u) => format!("Uuid({u})"),
|
||||
}
|
||||
indexable_value_to_string(value)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -135,35 +178,37 @@ impl TryFrom<String> for IndexableValue {
|
|||
|
||||
if value.starts_with("String(") {
|
||||
let s = value[7..value.len() - 1].to_string();
|
||||
return Ok(Self::String(s));
|
||||
}
|
||||
|
||||
if value.starts_with("Int(") {
|
||||
Ok(Self::String(s))
|
||||
} else if value.starts_with("Int(") {
|
||||
let s = value[4..value.len() - 1].to_string();
|
||||
let i = s
|
||||
.parse::<u64>()
|
||||
.map_err(|e| format!("Invalid Int: {}", e))?;
|
||||
return Ok(Self::Int(i));
|
||||
}
|
||||
|
||||
if value.starts_with("Uuid(") {
|
||||
Ok(Self::Int(i))
|
||||
} else if value.starts_with("Uuid(") {
|
||||
let s = value[5..value.len() - 1].to_string();
|
||||
let u = s
|
||||
.parse::<u64>()
|
||||
.map_err(|e| format!("Invalid UUID: {}", e))?;
|
||||
return Ok(Self::Uuid(u));
|
||||
}
|
||||
|
||||
Ok(Self::Uuid(u))
|
||||
} else if value.starts_with("Some(") {
|
||||
// TODO: This needs some recursion
|
||||
todo!()
|
||||
} else if value.starts_with("None(") {
|
||||
todo!()
|
||||
} else {
|
||||
Err(format!("Invalid IndexableValue: {}", value))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Value> for String {
|
||||
fn from(value: Value) -> Self {
|
||||
match value {
|
||||
Value::Number(n) => format!("Number({n})"),
|
||||
Value::Indexable(i) => format!("Indexable({})", String::from(i)),
|
||||
}
|
||||
// match value {
|
||||
// Value::Number(n) => format!("Number({n})"),
|
||||
// Value::Indexable(i) => format!("Indexable({})", String::from(i)),
|
||||
// }
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -174,21 +219,21 @@ impl TryFrom<String> for Value {
|
|||
return Err(format!("Invalid Value: {}", value));
|
||||
}
|
||||
|
||||
if value.starts_with("Number(") {
|
||||
let s = value[7..value.len() - 1].to_string();
|
||||
let n = s
|
||||
.parse::<f64>()
|
||||
.map_err(|e| format!("Invalid Number: {}", e))?;
|
||||
return Ok(Self::Number(n));
|
||||
}
|
||||
// if value.starts_with("Number(") {
|
||||
// let s = value[7..value.len() - 1].to_string();
|
||||
// let n = s
|
||||
// .parse::<f64>()
|
||||
// .map_err(|e| format!("Invalid Number: {}", e))?;
|
||||
// Ok(Self::Number(n))
|
||||
// } else if value.starts_with("Indexable(") {
|
||||
// let s = value[10..value.len() - 1].to_string();
|
||||
// let i = IndexableValue::try_from(s)?;
|
||||
// Ok(Self::Indexable(i))
|
||||
// } else {
|
||||
// Err(format!("Invalid Value: {}", value))
|
||||
// }
|
||||
|
||||
if value.starts_with("Indexable(") {
|
||||
let s = value[10..value.len() - 1].to_string();
|
||||
let i = IndexableValue::try_from(s)?;
|
||||
return Ok(Self::Indexable(i));
|
||||
}
|
||||
|
||||
Err(format!("Invalid Value: {}", value))
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue