diff --git a/src/ast.rs b/src/ast.rs index b4eabd8..542ef09 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -1,7 +1,7 @@ //! Represents the WIT language as a tree. This is the central //! representation of the language. -use crate::interpreter::Instruction; +use crate::{interpreter::Instruction, vec1::Vec1}; use std::str; /// Represents the types supported by WIT. @@ -57,7 +57,7 @@ pub enum InterfaceType { #[derive(PartialEq, Debug, Clone)] pub struct RecordType { /// Types representing the fields. - pub fields: Vec, + pub fields: Vec1, } /// Represents the kind of type. diff --git a/src/decoders/binary.rs b/src/decoders/binary.rs index 68396cf..5aae148 100644 --- a/src/decoders/binary.rs +++ b/src/decoders/binary.rs @@ -1,6 +1,6 @@ //! Parse the WIT binary representation into an [AST](crate::ast). -use crate::{ast::*, interpreter::Instruction}; +use crate::{ast::*, interpreter::Instruction, vec1::Vec1}; use nom::{ error::{make_error, ErrorKind, ParseError}, Err, IResult, @@ -112,7 +112,12 @@ fn record_type<'input, E: ParseError<&'input [u8]>>( ) -> IResult<&'input [u8], RecordType, E> { let (output, fields) = list(input, ty)?; - Ok((output, RecordType { fields })) + Ok(( + output, + RecordType { + fields: Vec1::new(fields).expect("Record must have at least one field, zero given."), + }, + )) } /// Parse a UTF-8 string. @@ -640,7 +645,7 @@ mod tests { InterfaceType::I32, InterfaceType::I64, InterfaceType::Record(RecordType { - fields: vec![InterfaceType::S32], + fields: vec1![InterfaceType::S32], }), ], )); @@ -670,16 +675,16 @@ mod tests { &[0x01][..], vec![ RecordType { - fields: vec![InterfaceType::String], + fields: vec1![InterfaceType::String], }, RecordType { - fields: vec![InterfaceType::String, InterfaceType::I32], + fields: vec1![InterfaceType::String, InterfaceType::I32], }, RecordType { - fields: vec![ + fields: vec1![ InterfaceType::String, InterfaceType::Record(RecordType { - fields: vec![InterfaceType::I32, InterfaceType::I32], + fields: vec1![InterfaceType::I32, InterfaceType::I32], }), InterfaceType::F64, ], @@ -864,7 +869,7 @@ mod tests { outputs: vec![InterfaceType::S32], }, Type::Record(RecordType { - fields: vec![InterfaceType::S32, InterfaceType::S32], + fields: vec1![InterfaceType::S32, InterfaceType::S32], }), ], )); diff --git a/src/decoders/wat.rs b/src/decoders/wat.rs index e7fc976..9b07be0 100644 --- a/src/decoders/wat.rs +++ b/src/decoders/wat.rs @@ -1,6 +1,6 @@ //! Parse the WIT textual representation into an [AST](crate::ast). -use crate::{ast::*, interpreter::Instruction}; +use crate::{ast::*, interpreter::Instruction, vec1::Vec1}; pub use wast::parser::ParseBuffer as Buffer; use wast::parser::{self, Cursor, Parse, Parser, Peek, Result}; @@ -151,7 +151,9 @@ impl Parse<'_> for RecordType { })?); } - Ok(RecordType { fields }) + Ok(RecordType { + fields: Vec1::new(fields).expect("Record must have at least one field, zero given."), + }) } } @@ -681,7 +683,7 @@ mod tests { InterfaceType::I32, InterfaceType::I64, InterfaceType::Record(RecordType { - fields: vec![InterfaceType::String], + fields: vec1![InterfaceType::String], }), ]; @@ -704,16 +706,16 @@ mod tests { ]; let outputs = vec![ RecordType { - fields: vec![InterfaceType::String], + fields: vec1![InterfaceType::String], }, RecordType { - fields: vec![InterfaceType::String, InterfaceType::I32], + fields: vec1![InterfaceType::String, InterfaceType::I32], }, RecordType { - fields: vec![ + fields: vec1![ InterfaceType::String, InterfaceType::Record(RecordType { - fields: vec![InterfaceType::I32, InterfaceType::I32], + fields: vec1![InterfaceType::I32, InterfaceType::I32], }), InterfaceType::F64, ], @@ -874,7 +876,7 @@ mod tests { fn test_type_record() { let input = buffer(r#"(@interface type (record (field string) (field i32)))"#); let output = Interface::Type(Type::Record(RecordType { - fields: vec![InterfaceType::String, InterfaceType::I32], + fields: vec1![InterfaceType::String, InterfaceType::I32], })); assert_eq!(parser::parse::(&input).unwrap(), output); diff --git a/src/encoders/binary.rs b/src/encoders/binary.rs index 2370017..2849dce 100644 --- a/src/encoders/binary.rs +++ b/src/encoders/binary.rs @@ -445,7 +445,7 @@ mod tests { assert_to_bytes!(InterfaceType::I64, &[0x0d]); assert_to_bytes!( InterfaceType::Record(RecordType { - fields: vec![InterfaceType::String] + fields: vec1![InterfaceType::String] }), &[0x0e, 0x01, 0x0a] ); @@ -455,7 +455,7 @@ mod tests { fn test_record_type() { assert_to_bytes!( RecordType { - fields: vec![InterfaceType::String] + fields: vec1![InterfaceType::String] }, &[ 0x01, // 1 field @@ -464,7 +464,7 @@ mod tests { ); assert_to_bytes!( RecordType { - fields: vec![InterfaceType::String, InterfaceType::I32] + fields: vec1![InterfaceType::String, InterfaceType::I32] }, &[ 0x02, // 2 fields @@ -474,10 +474,10 @@ mod tests { ); assert_to_bytes!( RecordType { - fields: vec![ + fields: vec1![ InterfaceType::String, InterfaceType::Record(RecordType { - fields: vec![InterfaceType::I32, InterfaceType::I32], + fields: vec1![InterfaceType::I32, InterfaceType::I32], }), InterfaceType::F64, ], @@ -542,7 +542,7 @@ mod tests { fn test_type_record() { assert_to_bytes!( Type::Record(RecordType { - fields: vec![InterfaceType::I32, InterfaceType::I64], + fields: vec1![InterfaceType::I32, InterfaceType::I64], }), &[ 0x01, // record type diff --git a/src/encoders/wat.rs b/src/encoders/wat.rs index 6ec680c..a141b23 100644 --- a/src/encoders/wat.rs +++ b/src/encoders/wat.rs @@ -368,7 +368,7 @@ mod tests { (&InterfaceType::I32).to_string(), (&InterfaceType::I64).to_string(), (&InterfaceType::Record(RecordType { - fields: vec![InterfaceType::String], + fields: vec1![InterfaceType::String], })) .to_string(), ]; @@ -397,18 +397,18 @@ mod tests { fn test_record_type() { let inputs = vec![ (&RecordType { - fields: vec![InterfaceType::String], + fields: vec1![InterfaceType::String], }) .to_string(), (&RecordType { - fields: vec![InterfaceType::String, InterfaceType::I32], + fields: vec1![InterfaceType::String, InterfaceType::I32], }) .to_string(), (&RecordType { - fields: vec![ + fields: vec1![ InterfaceType::String, InterfaceType::Record(RecordType { - fields: vec![InterfaceType::I32, InterfaceType::I32], + fields: vec1![InterfaceType::I32, InterfaceType::I32], }), InterfaceType::F64, ], @@ -539,7 +539,7 @@ mod tests { }) .to_string(), (&Type::Record(RecordType { - fields: vec![InterfaceType::String, InterfaceType::I32], + fields: vec1![InterfaceType::String, InterfaceType::I32], })) .to_string(), ]; diff --git a/src/interpreter/instructions/mod.rs b/src/interpreter/instructions/mod.rs index cf71c6e..39f25ab 100644 --- a/src/interpreter/instructions/mod.rs +++ b/src/interpreter/instructions/mod.rs @@ -326,10 +326,10 @@ pub(crate) mod tests { }, memory: Memory::new(vec![Cell::new(0); 128]), wit_types: vec![ast::Type::Record(ast::RecordType { - fields: vec![ + fields: vec1![ InterfaceType::I32, InterfaceType::Record(ast::RecordType { - fields: vec![InterfaceType::String, InterfaceType::F32], + fields: vec1![InterfaceType::String, InterfaceType::F32], }), InterfaceType::I64, ], diff --git a/src/interpreter/instructions/records.rs b/src/interpreter/instructions/records.rs index 5ed388f..15b9551 100644 --- a/src/interpreter/instructions/records.rs +++ b/src/interpreter/instructions/records.rs @@ -293,7 +293,7 @@ mod tests { let mut instance = Instance::new(); instance.wit_types.push( Type::Record(RecordType { - fields: vec![InterfaceType::I32, InterfaceType::I32], + fields: vec1![InterfaceType::I32, InterfaceType::I32], }) ); diff --git a/src/interpreter/wasm/values.rs b/src/interpreter/wasm/values.rs index 44df1d5..66a3d8d 100644 --- a/src/interpreter/wasm/values.rs +++ b/src/interpreter/wasm/values.rs @@ -1,7 +1,7 @@ //! Defines WIT values and associated operations. pub use crate::ast::{InterfaceType, RecordType}; -use crate::errors::WasmValueNativeCastError; +use crate::{errors::WasmValueNativeCastError, vec1::Vec1}; use std::{convert::TryFrom, slice::Iter}; #[cfg(feature = "serde")] @@ -85,7 +85,8 @@ impl Default for InterfaceValue { impl From<&Vec> for RecordType { fn from(values: &Vec) -> Self { RecordType { - fields: values.iter().map(Into::into).collect(), + fields: Vec1::new(values.iter().map(Into::into).collect()) + .expect("Record must have at least one field, zero given."), } } } @@ -225,7 +226,7 @@ mod tests { InterfaceValue::S8(2) ])), InterfaceType::Record(RecordType { - fields: vec![InterfaceType::I32, InterfaceType::S8] + fields: vec1![InterfaceType::I32, InterfaceType::S8] }) ); @@ -239,10 +240,10 @@ mod tests { InterfaceValue::S8(2) ])), InterfaceType::Record(RecordType { - fields: vec![ + fields: vec1![ InterfaceType::I32, InterfaceType::Record(RecordType { - fields: vec![InterfaceType::String, InterfaceType::F64] + fields: vec1![InterfaceType::String, InterfaceType::F64] }), InterfaceType::S8 ] diff --git a/src/lib.rs b/src/lib.rs index ebf8603..fd319ac 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -57,3 +57,4 @@ pub mod decoders; pub mod encoders; pub mod errors; pub mod interpreter; +pub mod vec1; diff --git a/src/macros.rs b/src/macros.rs index b2e2cfa..8ff4109 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -1,3 +1,28 @@ +/// This macro creates a `Vec1` by checking at compile-time that its +/// invariant holds. +#[allow(unused)] +macro_rules! vec1 { + ($item:expr; 0) => { + compile_error!("Cannot create an empty `Vec1`, it violates its invariant.") + }; + + () => { + compile_error!("Cannot create an empty `Vec1`, it violates its invariant.") + }; + + ($item:expr; $length:expr) => { + { + crate::vec1::Vec1::new(vec![$item; $length]).unwrap() + } + }; + + ($($item:expr),+ $(,)?) => { + { + crate::vec1::Vec1::new(vec![$($item),*]).unwrap() + } + }; +} + /// This macro runs a parser, extracts the next input and the parser /// output, and positions the next input on `$input`. macro_rules! consume { diff --git a/src/vec1.rs b/src/vec1.rs new file mode 100644 index 0000000..3e89293 --- /dev/null +++ b/src/vec1.rs @@ -0,0 +1,62 @@ +//! `Vec1` represents a non-empty `Vec`. + +use std::{ + error, + fmt::{self, Debug}, + ops, +}; + +/// `Vec1` represents a non-empty `Vec`. It derefs to `Vec` +/// directly. +#[derive(Clone, PartialEq)] +pub struct Vec1(Vec) +where + T: Debug; + +/// Represents the only error that can be emitted by `Vec1`, i.e. when +/// the number of items is zero. +#[derive(Debug)] +pub struct EmptyVec; + +impl error::Error for EmptyVec {} + +impl fmt::Display for EmptyVec { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(formatter, "Vec1 must as least contain one item, zero given") + } +} + +impl Vec1 +where + T: Debug, +{ + /// Creates a new non-empty vector, based on an inner `Vec`. If + /// the inner vector is empty, a `EmptyVec` error is returned. + pub fn new(items: Vec) -> Result { + if items.len() == 0 { + Err(EmptyVec) + } else { + Ok(Self(items)) + } + } +} + +impl fmt::Debug for Vec1 +where + T: Debug, +{ + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(formatter, "{:?}", self.0) + } +} + +impl ops::Deref for Vec1 +where + T: Debug, +{ + type Target = Vec; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} diff --git a/tests/binary.rs b/tests/binary.rs index 2238c97..c9d4709 100644 --- a/tests/binary.rs +++ b/tests/binary.rs @@ -1,5 +1,6 @@ use wasmer_interface_types::{ ast::*, decoders::binary::parse, encoders::binary::ToBytes, interpreter::Instruction, + vec1::Vec1, }; /// Tests an AST to binary, then binary to AST roundtrip. @@ -16,7 +17,7 @@ fn test_binary_encoding_decoding_roundtrip() { outputs: vec![InterfaceType::S32], }, Type::Record(RecordType { - fields: vec![InterfaceType::String, InterfaceType::I32], + fields: Vec1::new(vec![InterfaceType::String, InterfaceType::I32]).unwrap(), }), ], imports: vec![Import {