Convert maps to structures for advanced type checks
This commit is contained in:
parent
1f5dacad7d
commit
85419c47be
@ -57,7 +57,11 @@ impl AbstractTree for As {
|
||||
});
|
||||
}
|
||||
}
|
||||
Type::Any => todo!(),
|
||||
Type::Any => {
|
||||
// Do no validation when converting from "any" to a list.
|
||||
// This effectively defers to runtime behavior, potentially
|
||||
// causing a runtime error.
|
||||
}
|
||||
Type::Boolean => todo!(),
|
||||
Type::Collection => todo!(),
|
||||
Type::Custom(_) => todo!(),
|
||||
@ -91,14 +95,12 @@ impl AbstractTree for As {
|
||||
|
||||
Value::List(List::with_items(chars))
|
||||
}
|
||||
Value::Map(_) => todo!(),
|
||||
Value::Function(_) => todo!(),
|
||||
Value::Float(_) => todo!(),
|
||||
Value::Integer(_) => todo!(),
|
||||
Value::Boolean(_) => todo!(),
|
||||
Value::Range(_) => todo!(),
|
||||
Value::Option(_) => todo!(),
|
||||
Value::Structure(_) => todo!(),
|
||||
_ => {
|
||||
return Err(RuntimeError::ConversionImpossible {
|
||||
value,
|
||||
target_type: self.r#type.clone(),
|
||||
});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
todo!()
|
||||
|
@ -6,7 +6,8 @@ use serde::{Deserialize, Serialize};
|
||||
use crate::{
|
||||
built_in_functions::{fs::fs_functions, json::json_functions, str::string_functions, Callable},
|
||||
error::{RuntimeError, SyntaxError, ValidationError},
|
||||
AbstractTree, BuiltInFunction, Context, Format, Function, List, Map, SyntaxNode, Type, Value,
|
||||
AbstractTree, BuiltInFunction, Context, Format, Function, List, Map, Structure, SyntaxNode,
|
||||
Type, Value,
|
||||
};
|
||||
|
||||
static ARGS: OnceLock<Value> = OnceLock::new();
|
||||
@ -87,7 +88,9 @@ impl BuiltInValue {
|
||||
BuiltInValue::Args => Type::list(Type::String),
|
||||
BuiltInValue::AssertEqual => BuiltInFunction::AssertEqual.r#type(),
|
||||
BuiltInValue::Fs => Type::Map(None),
|
||||
BuiltInValue::Json => Type::Map(None),
|
||||
BuiltInValue::Json => Type::Map(Some(
|
||||
Structure::from_map(self.get().as_map().unwrap()).unwrap(),
|
||||
)),
|
||||
BuiltInValue::Length => BuiltInFunction::Length.r#type(),
|
||||
BuiltInValue::Output => BuiltInFunction::Output.r#type(),
|
||||
BuiltInValue::Random => Type::Map(None),
|
||||
|
@ -48,7 +48,17 @@ impl AbstractTree for Index {
|
||||
fn expected_type(&self, context: &Context) -> Result<Type, ValidationError> {
|
||||
match self.collection.expected_type(context)? {
|
||||
Type::List(item_type) => Ok(*item_type.clone()),
|
||||
Type::Map(_) => Ok(Type::Any),
|
||||
Type::Map(structure) => {
|
||||
if let Some(structure) = structure {
|
||||
if let IndexExpression::Identifier(identifier) = &self.index {
|
||||
if let Some((_, r#type)) = structure.inner().get(identifier.inner()) {
|
||||
return Ok(r#type.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Type::Any)
|
||||
}
|
||||
Type::None => Ok(Type::None),
|
||||
r#type => Ok(r#type),
|
||||
}
|
||||
|
@ -7,12 +7,18 @@ use std::{
|
||||
time,
|
||||
};
|
||||
|
||||
use crate::Value;
|
||||
use crate::{Type, Value};
|
||||
|
||||
use super::rw_lock_error::RwLockError;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum RuntimeError {
|
||||
/// The attempted conversion is impossible.
|
||||
ConversionImpossible {
|
||||
value: Value,
|
||||
target_type: Type,
|
||||
},
|
||||
|
||||
Csv(String),
|
||||
|
||||
Io(String),
|
||||
|
@ -11,7 +11,7 @@ use serde::{
|
||||
Deserialize, Deserializer, Serialize, Serializer,
|
||||
};
|
||||
|
||||
use crate::{Type, Value};
|
||||
use crate::{error::rw_lock_error::RwLockError, Map, Type, Value};
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
|
||||
pub struct Structure(Arc<BTreeMap<String, (Option<Value>, Type)>>);
|
||||
@ -21,6 +21,16 @@ impl Structure {
|
||||
Structure(Arc::new(map))
|
||||
}
|
||||
|
||||
pub fn from_map(map: &Map) -> Result<Self, RwLockError> {
|
||||
let mut structure = BTreeMap::new();
|
||||
|
||||
for (key, value) in map.inner()?.iter() {
|
||||
structure.insert(key.clone(), (Some(value.clone()), value.r#type()));
|
||||
}
|
||||
|
||||
Ok(Structure(Arc::new(structure)))
|
||||
}
|
||||
|
||||
pub fn inner(&self) -> &BTreeMap<String, (Option<Value>, Type)> {
|
||||
&self.0
|
||||
}
|
||||
@ -42,7 +52,7 @@ impl Display for Structure {
|
||||
}
|
||||
|
||||
impl Serialize for Structure {
|
||||
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
@ -94,7 +104,7 @@ impl<'de> Visitor<'de> for StructureVisitor {
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for Structure {
|
||||
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
|
20
tests/as.rs
20
tests/as.rs
@ -1,4 +1,7 @@
|
||||
use dust_lang::{error::ValidationError, *};
|
||||
use dust_lang::{
|
||||
error::{RuntimeError, ValidationError},
|
||||
*,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn string_as_list() {
|
||||
@ -25,3 +28,18 @@ fn string_as_list_conversion_error() {
|
||||
}))
|
||||
)
|
||||
}
|
||||
|
||||
const JSON: &str = "{ \"x\": 1 }";
|
||||
|
||||
#[test]
|
||||
fn conversion_runtime_error() {
|
||||
let json_value = interpret(&format!("json:parse('{JSON}')")).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
interpret(&format!("json:parse('{JSON}') as [map]")),
|
||||
Err(Error::Runtime(RuntimeError::ConversionImpossible {
|
||||
value: json_value,
|
||||
target_type: Type::List(Box::new(Type::Float))
|
||||
}))
|
||||
)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user