Skip to content

Commit

Permalink
Error handling, extra fields, and error handling for IDL deserializat…
Browse files Browse the repository at this point in the history
…ion (#53)

* refactor and check_error

* checkpoint for struct subtype

* checkpoint

* remove unwrap

* checkpoint

* struct subtype works

* enum inside struct works

* cargo fmt

* cargo fmt

* update doc

* fix

* fix

* tests

* remove eprintln to pass e2e

* add states to error

* fix

* out-of-bounds check

* make error module more generic to take strings

* move idltype test to dfx_info; add docs for public functions

* fix

* fix doc
  • Loading branch information
chenyan-dfinity authored and paulyoung committed Oct 14, 2019
1 parent 815942a commit 2be3988
Show file tree
Hide file tree
Showing 8 changed files with 762 additions and 433 deletions.
10 changes: 10 additions & 0 deletions lib/dfx_info/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,13 @@ pub trait Compound {
where
T: IDLType;
}

// IDL hash function comes from
// https://caml.inria.fr/pub/papers/garrigue-polymorphic_variants-ml98.pdf
pub fn idl_hash(id: &str) -> u32 {
let mut s: u32 = 0;
for c in id.chars() {
s = s.wrapping_mul(223).wrapping_add(c as u32);
}
s
}
99 changes: 99 additions & 0 deletions lib/dfx_info/tests/idltype.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
extern crate dfx_info;

use dfx_info::types::{get_type, Type};
use dfx_info::{idl_hash, IDLType};

#[test]
fn test_primitive() {
assert_eq!(get_type(&true), Type::Bool);
assert_eq!(get_type(&Box::new(42)), Type::Int);
let opt: Option<&str> = None;
assert_eq!(get_type(&opt), Type::Opt(Box::new(Type::Text)));
assert_eq!(get_type(&[0, 1, 2, 3]), Type::Vec(Box::new(Type::Int)));
}

#[test]
fn test_struct() {
#[derive(Debug, IDLType)]
struct A {
foo: i32,
bar: bool,
}
assert_eq!(
A::ty(),
Type::Record(vec![field("bar", Type::Bool), field("foo", Type::Int),])
);

#[derive(Debug, IDLType)]
struct G<T, E> {
g1: T,
g2: E,
}
let res = G { g1: 42, g2: true };
assert_eq!(
get_type(&res),
Type::Record(vec![field("g1", Type::Int), field("g2", Type::Bool)])
);

#[derive(Debug, IDLType)]
struct List {
head: i32,
tail: Option<Box<List>>,
}
assert_eq!(
List::ty(),
Type::Record(vec![
field("head", Type::Int),
field(
"tail",
Type::Opt(Box::new(Type::Knot(dfx_info::types::TypeId::of::<List>())))
)
])
);
}

#[test]
fn test_variant() {
#[allow(dead_code)]
#[derive(Debug, IDLType)]
enum E {
Foo,
Bar(bool, i32),
Baz { a: i32, b: u32 },
}

let v = E::Bar(true, 42);
assert_eq!(
get_type(&v),
Type::Variant(vec![
field(
"Bar",
Type::Record(vec![
unnamed_field(0, Type::Bool),
unnamed_field(1, Type::Int)
])
),
field(
"Baz",
Type::Record(vec![field("a", Type::Int), field("b", Type::Nat)])
),
field("Foo", Type::Null),
])
);
}

fn field(id: &str, ty: Type) -> dfx_info::types::Field {
dfx_info::types::Field {
id: id.to_string(),
hash: idl_hash(id),
ty,
}
}

fn unnamed_field(id: u32, ty: Type) -> dfx_info::types::Field {
dfx_info::types::Field {
id: id.to_string(),
hash: id,
ty,
}
}
42 changes: 22 additions & 20 deletions lib/serde_idl/README.adoc
Original file line number Diff line number Diff line change
@@ -1,41 +1,43 @@
= IDL Serialization library in Rust

== Using the library
* Serialization
* Example
[source,rust]
#[macro_use]
extern crate serde_idl;
Encode!(&42, &Some(42), &[1,2,3])
// Encode! macro expands to:
IDLBuilder::new().arg(&42).arg(&Some(42)).arg(&[1,2,3]).to_vec().unwrap(),
use serde_idl::{IDLType, Deserialize, Encode, Decode};
// Serialization
let bytes = Encode!(&[(42, "text")], &(42, "text"));
// Deserialization
Decode!(&bytes, a: Vec<(i64, &str)>, b: (i32, String));
assert_eq!(a, [(42, "text")]);
assert_eq!(b, (42i32, "text".to_string()));

* Serialize/Deserialize struct/enum
[source,rust]
#[derive(IDLType, Deserialize)]
struct List {
head: i32,
tail: Option<Box<List>>,
}
let list = List { head: 42, tail: None };
let bytes = Encode!(&list);
Decode!(&bytes, l: List);

* Derive serialization trait and inspect IDL type
* Inspect IDL type
[source,rust]
#[derive(IDLType)]
struct List { head: i32, tail: Option<Box<List>> }
let list = List { head: 42, tail: None };
assert_eq!(serde_idl::get_type(&list),
assert_eq!(List::ty(),
Type::Record(vec![
field("head", Type::Int),
field("tail", Type::Opt(Box::new(
Type::Knot(TypeId::of::<List>()))))])
);
assert_eq!(Encode!(&list),
hex::decode("4449444c026c02a0d2aca8047c90eddae704016e00002a00").unwrap());

* Deserialization
[source,rust]
let bytes = Encode!(&[(42, "text")], &(42, "text"));
assert_eq!(bytes, hex::decode("4449444c026d016c02007c0171020001012a04746578742a0474657874").unwrap());
Decode!(&bytes, a: Vec<(i64, &str)>, b: (i64, &str));
assert_eq!(a, [(42, "text")]);
assert_eq!(b, (42, "text"));

== Remaining items
* Error handling
# Limit recursion depth for deserialization
* Ignore extra fields, future values (subtype)
* Limit recursion depth and heap size for deserialization
* Support future values
* Optimize for `new struct` type
* Refactor serialization to use serde
* Import `.did` file in Rust
Expand Down
Loading

0 comments on commit 2be3988

Please sign in to comment.