use std::{collections::HashMap, ops::Range};
use boreal_parser::expression::{Identifier, IdentifierOperation, IdentifierOperationType};
use super::expression::{compile_expression, Expr, Expression, Type};
use super::rule::RuleCompiler;
use super::{CompilationError, ImportedModule};
use crate::module::{Module as ModuleTrait, StaticFunction, StaticValue, Type as ValueType};
#[derive(Debug)]
pub struct Module {
pub name: &'static str,
static_values: HashMap<&'static str, StaticValue>,
dynamic_types: ValueType,
}
#[derive(Debug, PartialEq)]
pub enum BoundedValueIndex {
Module(usize),
BoundedStack(usize),
}
#[derive(Debug)]
#[cfg_attr(all(test, feature = "serialize"), derive(PartialEq))]
pub struct ModuleExpression {
pub kind: ModuleExpressionKind,
pub operations: ModuleOperations,
}
impl ModuleExpression {
#[cfg(feature = "serialize")]
pub(super) fn deserialize<R: std::io::Read>(
ctx: &crate::wire::DeserializeContext,
reader: &mut R,
) -> std::io::Result<Self> {
wire::deserialize_module_expression(ctx, reader)
}
}
#[allow(unpredictable_function_pointer_comparisons)]
#[cfg_attr(all(test, feature = "serialize"), derive(PartialEq))]
pub enum ModuleExpressionKind {
BoundedModuleValueUse {
index: BoundedValueIndex,
},
StaticFunction {
fun: StaticFunction,
#[cfg(feature = "serialize")]
module_index: usize,
#[cfg(feature = "serialize")]
subfields: Vec<String>,
},
}
#[derive(Debug)]
#[cfg_attr(all(test, feature = "serialize"), derive(PartialEq))]
pub struct ModuleOperations {
pub expressions: Vec<Expression>,
pub operations: Vec<ValueOperation>,
}
#[derive(Debug, PartialEq)]
pub enum ValueOperation {
Subfield(String),
Subscript,
FunctionCall(usize),
}
impl std::fmt::Debug for ModuleExpressionKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::BoundedModuleValueUse { index } => f
.debug_struct("BoundedModuleValueUse")
.field("index", index)
.finish(),
Self::StaticFunction {
fun,
#[cfg(feature = "serialize")]
module_index,
#[cfg(feature = "serialize")]
subfields,
} => {
let mut d = f.debug_struct("Function");
let _r = d.field("fun", &(*fun as usize));
#[cfg(feature = "serialize")]
{
let _r = d.field("module_index", module_index);
let _r = d.field("subfields", subfields);
}
d.finish()
}
}
}
}
#[derive(Debug)]
pub enum IteratorType {
Array(ValueType),
Dictionary(ValueType),
}
pub(crate) fn compile_module(module: &dyn ModuleTrait) -> Module {
Module {
name: module.get_name(),
static_values: module.get_static_values(),
dynamic_types: ValueType::Object(module.get_dynamic_types()),
}
}
pub(super) fn compile_bounded_identifier_use<'a, 'b>(
compiler: &'b mut RuleCompiler<'a>,
starting_type: &'b ValueType,
identifier: Identifier,
identifier_stack_index: usize,
) -> Result<ModuleUse<'a, 'b>, CompilationError> {
let mut module_use = ModuleUse {
compiler,
operations_expressions: Vec::new(),
operations: Vec::with_capacity(identifier.operations.len()),
current_span: identifier.name_span,
kind: ModuleUseKind::Dynamic {
current_type: starting_type,
bounded_value_index: BoundedValueIndex::BoundedStack(identifier_stack_index),
},
};
for op in identifier.operations {
module_use.add_operation(op)?;
}
Ok(module_use)
}
pub(super) fn compile_identifier<'a, 'b>(
compiler: &'b mut RuleCompiler<'a>,
module: &'b ImportedModule,
identifier: Identifier,
identifier_span: &Range<usize>,
) -> Result<ModuleUse<'a, 'b>, CompilationError> {
let nb_ops = identifier.operations.len();
let mut ops = identifier.operations.into_iter();
let Some(IdentifierOperation {
op: first_op,
span: first_op_span,
}) = ops.next()
else {
return Err(CompilationError::InvalidIdentifierUse {
span: identifier_span.clone(),
});
};
let subfield = match first_op {
IdentifierOperationType::Subfield(subfield) => subfield,
IdentifierOperationType::Subscript(_) => {
return Err(CompilationError::InvalidIdentifierType {
actual_type: "object".to_string(),
expected_type: "array or dictionary".to_string(),
span: identifier.name_span,
});
}
IdentifierOperationType::FunctionCall(_) => {
return Err(CompilationError::InvalidIdentifierType {
actual_type: "object".to_string(),
expected_type: "function".to_string(),
span: identifier.name_span,
});
}
};
let mut module_use = match module.module.static_values.get(&*subfield) {
Some(value) => ModuleUse {
compiler,
operations_expressions: Vec::new(),
operations: Vec::with_capacity(nb_ops),
current_span: identifier.name_span,
kind: ModuleUseKind::Static {
current_value: value,
module_index: module.module_index,
applied_subfields: vec![subfield],
},
},
None => {
let mut module_use = ModuleUse {
compiler,
operations_expressions: Vec::new(),
operations: Vec::with_capacity(nb_ops),
current_span: identifier.name_span,
kind: ModuleUseKind::Dynamic {
current_type: &module.module.dynamic_types,
bounded_value_index: BoundedValueIndex::Module(module.module_index),
},
};
module_use.add_operation(IdentifierOperation {
op: IdentifierOperationType::Subfield(subfield),
span: first_op_span,
})?;
module_use
}
};
for op in ops {
module_use.add_operation(op)?;
}
Ok(module_use)
}
pub(super) struct ModuleUse<'a, 'b> {
compiler: &'b mut RuleCompiler<'a>,
operations_expressions: Vec<Expression>,
operations: Vec<ValueOperation>,
current_span: Range<usize>,
kind: ModuleUseKind<'b>,
}
enum ModuleUseKind<'a> {
Static {
current_value: &'a StaticValue,
module_index: usize,
applied_subfields: Vec<String>,
},
StaticFunction {
fun: StaticFunction,
current_type: &'a ValueType,
module_index: usize,
applied_subfields: Vec<String>,
},
Dynamic {
current_type: &'a ValueType,
bounded_value_index: BoundedValueIndex,
},
}
impl ModuleUse<'_, '_> {
fn add_operation(&mut self, op: IdentifierOperation) -> Result<(), CompilationError> {
let res = match op.op {
IdentifierOperationType::Subfield(subfield) => {
let res = self.kind.subfield(&subfield);
match &mut self.kind {
ModuleUseKind::Static {
applied_subfields, ..
} => applied_subfields.push(subfield),
ModuleUseKind::StaticFunction { .. } | ModuleUseKind::Dynamic { .. } => {
self.operations.push(ValueOperation::Subfield(subfield));
}
}
res
}
IdentifierOperationType::Subscript(subscript) => {
let Expr { expr, ty, span } = compile_expression(self.compiler, *subscript)?;
self.operations_expressions.push(expr);
self.operations.push(ValueOperation::Subscript);
self.kind.subscript(ty, span)
}
IdentifierOperationType::FunctionCall(arguments) => {
self.operations
.push(ValueOperation::FunctionCall(arguments.len()));
let mut arguments_types = Vec::with_capacity(arguments.len());
for arg in arguments {
let res = compile_expression(self.compiler, arg)?;
self.operations_expressions.push(res.expr);
arguments_types.push(res.ty);
}
self.kind.function_call(&arguments_types)
}
};
match res {
Err(TypeError::UnknownSubfield(subfield)) => {
Err(CompilationError::UnknownIdentifierField {
field_name: subfield,
span: op.span,
})
}
Err(TypeError::WrongType {
actual_type,
expected_type,
}) => Err(CompilationError::InvalidIdentifierType {
actual_type,
expected_type,
span: self.current_span.clone(),
}),
Err(TypeError::WrongIndexType {
actual_type,
expected_type,
span,
}) => Err(CompilationError::InvalidIdentifierIndexType {
ty: actual_type.to_string(),
span,
expected_type: expected_type.to_string(),
}),
Err(TypeError::WrongFunctionArguments { arguments_types }) => {
Err(CompilationError::InvalidIdentifierCall {
arguments_types,
span: op.span,
})
}
Ok(()) => {
self.current_span.end = op.span.end;
Ok(())
}
}
}
pub(super) fn into_expression(self) -> Option<(Expression, Type)> {
let (expr, ty) = match self.kind {
ModuleUseKind::Static { current_value, .. } => {
match current_value {
StaticValue::Integer(v) => (Expression::Integer(*v), ValueType::Integer),
StaticValue::Float(v) => (Expression::Double(*v), ValueType::Float),
StaticValue::Bytes(v) => (
Expression::Bytes(self.compiler.bytes_pool.insert(v)),
ValueType::Bytes,
),
StaticValue::Boolean(v) => (Expression::Boolean(*v), ValueType::Boolean),
_ => return None,
}
}
ModuleUseKind::StaticFunction {
fun,
current_type,
module_index,
applied_subfields,
} => {
#[cfg(not(feature = "serialize"))]
{
let _ = module_index;
drop(applied_subfields);
}
let expr = Expression::Module(ModuleExpression {
kind: ModuleExpressionKind::StaticFunction {
fun,
#[cfg(feature = "serialize")]
module_index,
#[cfg(feature = "serialize")]
subfields: applied_subfields,
},
operations: ModuleOperations {
expressions: self.operations_expressions,
operations: self.operations,
},
});
(expr, current_type.clone())
}
ModuleUseKind::Dynamic {
current_type,
bounded_value_index,
} => {
let expr = Expression::Module(ModuleExpression {
kind: ModuleExpressionKind::BoundedModuleValueUse {
index: bounded_value_index,
},
operations: ModuleOperations {
expressions: self.operations_expressions,
operations: self.operations,
},
});
(expr, current_type.clone())
}
};
let ty = match ty {
ValueType::Integer => Type::Integer,
ValueType::Float => Type::Float,
ValueType::Bytes => Type::Bytes,
ValueType::Boolean => Type::Boolean,
_ => return None,
};
Some((expr, ty))
}
pub(super) fn into_iterator_expression(self) -> Option<(ModuleExpression, IteratorType)> {
match self.kind {
ModuleUseKind::Static { .. } | ModuleUseKind::StaticFunction { .. } => None,
ModuleUseKind::Dynamic {
current_type,
bounded_value_index,
} => {
let expr = ModuleExpression {
kind: ModuleExpressionKind::BoundedModuleValueUse {
index: bounded_value_index,
},
operations: ModuleOperations {
expressions: self.operations_expressions,
operations: self.operations,
},
};
let ty = match current_type {
ValueType::Array { value_type, .. } => {
IteratorType::Array((**value_type).clone())
}
ValueType::Dictionary { value_type, .. } => {
IteratorType::Dictionary((**value_type).clone())
}
_ => return None,
};
Some((expr, ty))
}
}
}
}
#[derive(Debug)]
enum TypeError {
UnknownSubfield(String),
WrongType {
actual_type: String,
expected_type: String,
},
WrongIndexType {
actual_type: Type,
expected_type: Type,
span: Range<usize>,
},
WrongFunctionArguments {
arguments_types: Vec<String>,
},
}
impl ModuleUseKind<'_> {
fn subfield(&mut self, subfield: &str) -> Result<(), TypeError> {
match self {
Self::Static { current_value, .. } => {
if let StaticValue::Object(map) = *current_value {
match map.get(subfield) {
Some(v) => {
*current_value = v;
return Ok(());
}
None => return Err(TypeError::UnknownSubfield(subfield.to_string())),
}
}
}
Self::StaticFunction { current_type, .. } | Self::Dynamic { current_type, .. } => {
if let ValueType::Object(map) = current_type {
match map.get(subfield) {
Some(v) => {
*current_type = v;
return Ok(());
}
None => return Err(TypeError::UnknownSubfield(subfield.to_string())),
}
}
}
}
Err(TypeError::WrongType {
actual_type: self.type_to_string(),
expected_type: "object".to_owned(),
})
}
fn subscript(
&mut self,
subscript_type: Type,
subscript_span: Range<usize>,
) -> Result<(), TypeError> {
let check_subscript_type = |expected_type: Type| {
if subscript_type == expected_type {
Ok(())
} else {
Err(TypeError::WrongIndexType {
actual_type: subscript_type,
expected_type,
span: subscript_span,
})
}
};
match self {
Self::Static { .. } => (),
Self::StaticFunction { current_type, .. } | Self::Dynamic { current_type, .. } => {
match current_type {
ValueType::Array { value_type, .. } => {
check_subscript_type(Type::Integer)?;
*current_type = value_type;
return Ok(());
}
ValueType::Dictionary { value_type, .. } => {
check_subscript_type(Type::Bytes)?;
*current_type = value_type;
return Ok(());
}
_ => (),
}
}
}
Err(TypeError::WrongType {
actual_type: self.type_to_string(),
expected_type: "array or dictionary".to_string(),
})
}
fn function_call(&mut self, actual_args_types: &[Type]) -> Result<(), TypeError> {
match self {
Self::Static {
current_value,
module_index,
applied_subfields,
} => {
if let StaticValue::Function {
arguments_types,
return_type,
fun,
} = current_value
{
check_all_arguments_types(arguments_types, actual_args_types)?;
*self = Self::StaticFunction {
fun: *fun,
current_type: return_type,
module_index: *module_index,
applied_subfields: std::mem::take(applied_subfields),
};
return Ok(());
}
}
Self::StaticFunction { current_type, .. } | Self::Dynamic { current_type, .. } => {
if let ValueType::Function {
arguments_types,
return_type,
} = current_type
{
check_all_arguments_types(arguments_types, actual_args_types)?;
*current_type = return_type;
return Ok(());
}
}
}
Err(TypeError::WrongType {
actual_type: self.type_to_string(),
expected_type: "function".to_owned(),
})
}
fn type_to_string(&self) -> String {
match self {
Self::Static { current_value, .. } => match current_value {
StaticValue::Integer(_) => "integer",
StaticValue::Float(_) => "float",
StaticValue::Bytes(_) => "bytes",
StaticValue::Boolean(_) => "boolean",
StaticValue::Object(_) => "object",
StaticValue::Function { .. } => "function",
},
Self::StaticFunction { current_type, .. } | Self::Dynamic { current_type, .. } => {
match current_type {
ValueType::Integer => "integer",
ValueType::Float => "float",
ValueType::Bytes => "bytes",
ValueType::Regex => "regex",
ValueType::Boolean => "boolean",
ValueType::Array { .. } => "array",
ValueType::Dictionary { .. } => "dict",
ValueType::Object(_) => "object",
ValueType::Function { .. } => "function",
}
}
}
.to_owned()
}
}
fn check_all_arguments_types(
valid_types_vec: &[Vec<ValueType>],
actual_types: &[Type],
) -> Result<(), TypeError> {
if valid_types_vec.is_empty() && actual_types.is_empty() {
return Ok(());
}
for valid_types in valid_types_vec {
if arguments_types_are_equal(valid_types, actual_types) {
return Ok(());
}
}
Err(TypeError::WrongFunctionArguments {
arguments_types: actual_types.iter().map(ToString::to_string).collect(),
})
}
fn arguments_types_are_equal(valid_types: &[ValueType], actual_types: &[Type]) -> bool {
if valid_types.len() != actual_types.len() {
return false;
}
for (expected, actual) in valid_types.iter().zip(actual_types.iter()) {
let expected = module_type_to_expr_type(expected);
match expected {
Some(expected) => {
if expected != *actual {
return false;
}
}
None => return false,
}
}
true
}
fn module_type_to_expr_type(v: &ValueType) -> Option<Type> {
match v {
ValueType::Integer => Some(Type::Integer),
ValueType::Float => Some(Type::Float),
ValueType::Bytes => Some(Type::Bytes),
ValueType::Regex => Some(Type::Regex),
ValueType::Boolean => Some(Type::Boolean),
_ => None,
}
}
#[cfg(feature = "serialize")]
mod wire {
use std::io;
use crate::wire::{Deserialize, Serialize};
use crate::compiler::expression::Expression;
use crate::module::StaticValue;
use crate::wire::DeserializeContext;
use super::{
BoundedValueIndex, ModuleExpression, ModuleExpressionKind, ModuleOperations,
StaticFunction, ValueOperation,
};
impl Serialize for ModuleExpression {
fn serialize<W: io::Write>(&self, writer: &mut W) -> io::Result<()> {
self.kind.serialize(writer)?;
self.operations.serialize(writer)?;
Ok(())
}
}
pub(super) fn deserialize_module_expression<R: io::Read>(
ctx: &DeserializeContext,
reader: &mut R,
) -> io::Result<ModuleExpression> {
let kind = deserialize_module_expression_kind(ctx, reader)?;
let operations = deserialize_module_operations(ctx, reader)?;
Ok(ModuleExpression { kind, operations })
}
impl Serialize for ModuleOperations {
fn serialize<W: io::Write>(&self, writer: &mut W) -> io::Result<()> {
self.expressions.serialize(writer)?;
self.operations.serialize(writer)?;
Ok(())
}
}
fn deserialize_module_operations<R: io::Read>(
ctx: &DeserializeContext,
reader: &mut R,
) -> io::Result<ModuleOperations> {
let len = u32::deserialize_reader(reader)?;
let len = usize::try_from(len).map_err(|_| {
io::Error::new(io::ErrorKind::InvalidData, format!("length too big: {len}"))
})?;
let mut expressions = Vec::with_capacity(len);
for _ in 0..len {
expressions.push(Expression::deserialize(ctx, reader)?);
}
let operations = <Vec<ValueOperation>>::deserialize_reader(reader)?;
Ok(ModuleOperations {
expressions,
operations,
})
}
impl Serialize for ModuleExpressionKind {
fn serialize<W: io::Write>(&self, writer: &mut W) -> io::Result<()> {
match self {
Self::BoundedModuleValueUse { index } => {
0_u8.serialize(writer)?;
index.serialize(writer)?;
}
Self::StaticFunction {
fun: _,
module_index,
subfields,
} => {
1_u8.serialize(writer)?;
module_index.serialize(writer)?;
subfields.serialize(writer)?;
}
}
Ok(())
}
}
fn deserialize_module_expression_kind<R: io::Read>(
ctx: &DeserializeContext,
reader: &mut R,
) -> io::Result<ModuleExpressionKind> {
let discriminant = u8::deserialize_reader(reader)?;
match discriminant {
0 => {
let index = BoundedValueIndex::deserialize_reader(reader)?;
Ok(ModuleExpressionKind::BoundedModuleValueUse { index })
}
1 => {
let module_index = usize::deserialize_reader(reader)?;
let subfields = <Vec<String>>::deserialize_reader(reader)?;
let Some(value) = ctx.modules_static_values.get(module_index) else {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
format!("Unknown module with index {module_index} used in expression"),
));
};
match get_function_from_subfield_ops(value, &subfields) {
Some(fun) => Ok(ModuleExpressionKind::StaticFunction {
fun,
module_index,
subfields,
}),
None => {
Err(io::Error::new(
io::ErrorKind::InvalidData,
format!("Invalid subfields {subfields:?} in expression using module with index {module_index}")
))
}
}
}
v => Err(io::Error::new(
io::ErrorKind::InvalidData,
format!("invalid discriminant when deserializing a module expression kind: {v}"),
)),
}
}
fn get_function_from_subfield_ops(
mut value: &StaticValue,
subfields: &[String],
) -> Option<StaticFunction> {
for subfield in subfields {
match value {
StaticValue::Object(map) => value = map.get(&**subfield)?,
_ => return None,
}
}
match value {
StaticValue::Function { fun, .. } => Some(*fun),
_ => None,
}
}
impl Serialize for BoundedValueIndex {
fn serialize<W: io::Write>(&self, writer: &mut W) -> io::Result<()> {
match self {
Self::Module(v) => {
0_u8.serialize(writer)?;
v.serialize(writer)?;
}
Self::BoundedStack(v) => {
1_u8.serialize(writer)?;
v.serialize(writer)?;
}
}
Ok(())
}
}
impl Deserialize for BoundedValueIndex {
fn deserialize_reader<R: io::Read>(reader: &mut R) -> io::Result<Self> {
let discriminant = u8::deserialize_reader(reader)?;
match discriminant {
0 => Ok(Self::Module(usize::deserialize_reader(reader)?)),
1 => Ok(Self::BoundedStack(usize::deserialize_reader(reader)?)),
v => Err(io::Error::new(
io::ErrorKind::InvalidData,
format!("invalid discriminant when deserializing a bounded value index: {v}"),
)),
}
}
}
impl Serialize for ValueOperation {
fn serialize<W: io::Write>(&self, writer: &mut W) -> io::Result<()> {
match self {
ValueOperation::Subfield(v) => {
0_u8.serialize(writer)?;
v.serialize(writer)?;
}
ValueOperation::Subscript => 1_u8.serialize(writer)?,
ValueOperation::FunctionCall(v) => {
2_u8.serialize(writer)?;
(*v as u64).serialize(writer)?;
}
}
Ok(())
}
}
impl Deserialize for ValueOperation {
fn deserialize_reader<R: io::Read>(reader: &mut R) -> io::Result<Self> {
let discriminant = u8::deserialize_reader(reader)?;
match discriminant {
0 => Ok(Self::Subfield(String::deserialize_reader(reader)?)),
1 => Ok(Self::Subscript),
2 => {
let v = usize::deserialize_reader(reader)?;
Ok(Self::FunctionCall(v))
}
v => Err(io::Error::new(
io::ErrorKind::InvalidData,
format!("invalid discriminant when deserializing a value operation: {v}"),
)),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::module::{Math, Module, Time};
use crate::wire::tests::{
test_invalid_deserialization, test_round_trip, test_round_trip_custom_deser,
};
#[test]
fn test_wire_module_expression() {
let ctx = DeserializeContext::default();
test_round_trip_custom_deser(
&ModuleExpression {
kind: ModuleExpressionKind::BoundedModuleValueUse {
index: BoundedValueIndex::Module(5),
},
operations: ModuleOperations {
expressions: vec![],
operations: vec![],
},
},
|reader| deserialize_module_expression(&ctx, reader),
&[0, 10],
);
test_round_trip_custom_deser(
&ModuleOperations {
expressions: vec![Expression::Integer(23)],
operations: vec![
ValueOperation::Subscript,
ValueOperation::Subfield("abc".to_owned()),
],
},
|reader| deserialize_module_operations(&ctx, reader),
&[0, 4, 12],
);
}
#[test]
fn test_wire_module_expression_kind() {
let time_module = Time;
let time_static_values = time_module.get_static_values();
let now_fun = match time_static_values.get("now") {
Some(StaticValue::Function { fun, .. }) => *fun,
_ => panic!("missing now function in time module"),
};
let math_module = Math;
let math_static_values = math_module.get_static_values();
let mean_fun = match math_static_values.get("mean") {
Some(StaticValue::Function { fun, .. }) => *fun,
_ => panic!("missing mean function in math module"),
};
let ctx = DeserializeContext {
modules_static_values: vec![
StaticValue::Object(time_static_values),
StaticValue::Object(math_static_values),
],
};
test_round_trip_custom_deser(
&ModuleExpressionKind::BoundedModuleValueUse {
index: BoundedValueIndex::Module(3),
},
|reader| deserialize_module_expression_kind(&ctx, reader),
&[0, 2],
);
test_round_trip_custom_deser(
&ModuleExpressionKind::StaticFunction {
fun: now_fun,
module_index: 0,
subfields: vec!["now".to_owned()],
},
|reader| deserialize_module_expression_kind(&ctx, reader),
&[0, 1, 9],
);
test_round_trip_custom_deser(
&ModuleExpressionKind::StaticFunction {
fun: mean_fun,
module_index: 1,
subfields: vec!["mean".to_owned()],
},
|reader| deserialize_module_expression_kind(&ctx, reader),
&[0, 1, 9],
);
let test_fail_deser = |kind: ModuleExpressionKind| {
let mut buf = Vec::new();
kind.serialize(&mut buf).unwrap();
let mut reader = io::Cursor::new(&*buf);
assert!(deserialize_module_expression_kind(&ctx, &mut reader).is_err());
};
test_fail_deser(ModuleExpressionKind::StaticFunction {
fun: mean_fun,
module_index: 5,
subfields: vec!["mean".to_owned()],
});
test_fail_deser(ModuleExpressionKind::StaticFunction {
fun: mean_fun,
module_index: 1,
subfields: vec![],
});
test_fail_deser(ModuleExpressionKind::StaticFunction {
fun: mean_fun,
module_index: 1,
subfields: vec!["toto".to_owned()],
});
test_fail_deser(ModuleExpressionKind::StaticFunction {
fun: mean_fun,
module_index: 1,
subfields: vec!["MEAN_BYTES".to_owned(), "toto".to_owned()],
});
let mut reader = io::Cursor::new(b"\x05");
assert!(deserialize_module_expression_kind(&ctx, &mut reader).is_err());
}
#[test]
fn test_wire_bonded_value_index() {
test_round_trip(&BoundedValueIndex::Module(15), &[0, 2]);
test_round_trip(&BoundedValueIndex::BoundedStack(3), &[0, 2]);
test_invalid_deserialization::<BoundedValueIndex>(b"\x05");
}
#[test]
fn test_wire_value_operation() {
test_round_trip(&ValueOperation::Subfield("aze".to_string()), &[0, 2]);
test_round_trip(&ValueOperation::Subscript, &[0]);
test_round_trip(&ValueOperation::FunctionCall(23), &[0, 2]);
test_invalid_deserialization::<ValueOperation>(b"\x05");
}
}
}
#[cfg(test)]
mod tests {
use crate::module::{EvalContext, Value};
use crate::test_helpers::test_type_traits_non_clonable;
use super::*;
#[cfg_attr(coverage_nightly, coverage(off))]
fn test_fun(_ctx: &mut EvalContext, args: Vec<Value>) -> Option<Value> {
drop(args);
None
}
#[test]
fn test_types_traits() {
test_type_traits_non_clonable(compile_module(&crate::module::Time));
test_type_traits_non_clonable(ValueOperation::Subfield("a".to_owned()));
test_type_traits_non_clonable(BoundedValueIndex::Module(0));
test_type_traits_non_clonable(ModuleOperations {
expressions: Vec::new(),
operations: Vec::new(),
});
test_type_traits_non_clonable(ModuleExpression {
kind: ModuleExpressionKind::BoundedModuleValueUse {
index: BoundedValueIndex::Module(0),
},
operations: ModuleOperations {
expressions: Vec::new(),
operations: Vec::new(),
},
});
test_type_traits_non_clonable(ModuleExpressionKind::BoundedModuleValueUse {
index: BoundedValueIndex::Module(0),
});
test_type_traits_non_clonable(ModuleExpressionKind::StaticFunction {
fun: test_fun,
#[cfg(feature = "serialize")]
module_index: 0,
#[cfg(feature = "serialize")]
subfields: Vec::new(),
});
test_type_traits_non_clonable(IteratorType::Array(ValueType::Integer));
test_type_traits_non_clonable(TypeError::UnknownSubfield("a".to_owned()));
}
}