#[macro_export]
macro_rules! daml_path {
( @priv $record:ident / $path:ident $( { => $variant:ident } )? [ $index:expr ] ? $( :: $type:ident )? ) => {
{
let field_value = daml_path!(@get_record_field $record, $path);
let variant_value = daml_path!(@get_variant_value field_value, $($variant)? )?;
let list_item_value = daml_path!(@get_list_item variant_value, $index);
let optional_value = list_item_value.try_optional()?.ok_or(DamlError::MissingRequiredField)?;
daml_path!(@priv $($type)? optional_value)
}
};
( @priv $record:ident / $path:ident $( { => $variant:ident } )? [ $index:expr ] $( :: $type:ident )? ) => {
{
let field_value = daml_path!(@get_record_field $record, $path);
let variant_value = daml_path!(@get_variant_value field_value, $($variant)? )?;
let list_item_value = daml_path!(@get_list_item variant_value, $index);
daml_path!(@priv $($type)? list_item_value)
}
};
( @priv $record:ident / $path:ident $( { => $variant:ident } )? $( :: $type:ident )? ) => {
{
let field_value = daml_path!(@get_record_field $record, $path);
let variant_value = daml_path!(@get_variant_value field_value, $($variant)? )?;
daml_path!(@priv $($type)? variant_value)
}
};
( @priv $record:ident / $path:ident $( { => $variant:ident } )? ? $( :: $type:ident )? ) => {
{
let field_value = daml_path!(@get_record_field $record, $path);
let variant_value = daml_path!(@get_variant_value field_value, $($variant)? )?;
let optional_value = variant_value.try_optional()?.ok_or(DamlError::MissingRequiredField)?;
daml_path!(@priv $($type)? optional_value)
}
};
( @priv $record:ident / $path:ident $( { => $variant:ident } )? [ $index:expr ] ? $($rest:tt)* ) => {
{
let field_value = daml_path!(@get_record_field $record, $path);
let variant_value = daml_path!(@get_variant_value field_value, $($variant)? )?;
let list_item_value = daml_path!(@get_list_item variant_value, $index);
let optional_value = list_item_value.try_optional()?.ok_or(DamlError::MissingRequiredField)?;
let field_as_record = &(optional_value.try_record()?);
daml_path!( @priv field_as_record $($rest)* )
}
};
( @priv $record:ident / $path:ident $( { => $variant:ident } )? [ $index:expr ] $($rest:tt)* ) => {
{
let field_value = daml_path!(@get_record_field $record, $path);
let value_from_variant = daml_path!(@get_variant_value field_value, $($variant)? )?;
let list_item_value = daml_path!(@get_list_item value_from_variant, $index);
let field_as_record = &(list_item_value.try_record()?);
daml_path!( @priv field_as_record $($rest)* )
}
};
( @priv $record:ident / $path:ident $( { => $variant:ident } )? ? $($rest:tt)* ) => {
{
let field_value = daml_path!(@get_record_field $record, $path);
let variant_value = daml_path!(@get_variant_value field_value, $($variant)? )?;
let optional_value = variant_value.try_optional()?.ok_or(DamlError::MissingRequiredField)?;
let field_as_record = &(optional_value.try_record()?);
daml_path!( @priv field_as_record $($rest)* )
}
};
( @priv $record:ident / $path:ident { => $variant:ident } $($rest0:tt)* ) => {
{
let field_value = daml_path!(@get_record_field $record, $path);
let variant_value = daml_path!(@get_variant_value field_value, $variant )?;
let field_as_record = &(variant_value.try_record()?);
daml_path!( @priv field_as_record $($rest0)* )
}
};
( @priv $record:ident / $path:ident $( { => $variant:ident } )? $($rest1:tt)* ) => {
{
let field_value = daml_path!(@get_record_field $record, $path);
let variant_value = daml_path!(@get_variant_value field_value, $($variant)? )?;
let field_as_record = &(variant_value.try_record()?);
daml_path!( @priv field_as_record $($rest1)* )
}
};
( @priv $value:ident ) => {
{
let res: DamlResult<&DamlValue> = Ok($value);
res
}
};
( @priv c $value:ident ) => {
$value.try_contract_id()
};
( @priv u $value:ident ) => {
$value.try_unit_ref()
};
( @priv p $value:ident ) => {
$value.try_party()
};
( @priv i $value:ident ) => {
$value.try_int64_ref()
};
( @priv f $value:ident ) => {
$value.try_numeric()
};
( @priv t $value:ident ) => {
$value.try_text()
};
( @priv b $value:ident ) => {
$value.try_bool_ref()
};
( @priv s $value:ident ) => {
$value.try_timestamp_ref()
};
( @priv d $value:ident ) => {
$value.try_date_ref()
};
( @priv r $value:ident ) => {
$value.try_record()
};
( @priv l $value:ident ) => {
$value.try_list()
};
( @priv v $value:ident ) => {
$value.try_variant()
};
( @get_record_field $record:ident, $path:ident ) => {
$record.field(stringify!($path))?
};
( @get_list_item $value:ident, $index:expr ) => {
{
let field_as_list = $value.try_list()?;
let list_item_value: &DamlValue = field_as_list.get($index).ok_or(DamlError::ListIndexOutOfRange($index))?;
list_item_value
}
};
( @get_variant_value $value:ident , $variant:ident ) => {
{
let variant = $value.try_variant()?;
if stringify!($variant) == "__" || variant.constructor() == stringify!($variant) {
Ok(variant.value())
} else {
Err(DamlError::UnexpectedVariant(stringify!($variant).to_owned(), variant.constructor().to_owned()))
}
}
};
( @get_variant_value $value:ident , ) => {
{
let res: DamlResult<_> = Ok($value);
res
}
};
( $($rest:tt)* ) => {
{
use $crate::daml_grpc::data::DamlResult;
use $crate::daml_grpc::data::value::DamlRecord;
let func: fn(&DamlRecord) -> DamlResult<&_> = | rec_ref: &DamlRecord | {
daml_path!( @priv rec_ref / $($rest)* )
};
func
}
};
}
#[cfg(test)]
mod test {
use crate::daml_value;
use crate::test_util::TestResult;
use crate::test_util::{make_date, make_timestamp};
use bigdecimal::BigDecimal;
use daml_grpc::data::value::{DamlRecord, DamlValue, DamlVariant};
use daml_grpc::data::DamlError;
use std::convert::TryFrom;
#[test]
pub fn test_top_party() -> TestResult {
let value: DamlValue = get_test_value();
assert_eq!("Alice", value.extract(daml_path![sender::p])?);
Ok(())
}
#[test]
pub fn test_record_top_party() -> TestResult {
let value: DamlValue = get_test_value();
let record: &DamlRecord = value.try_record()?;
assert_eq!("Alice", record.extract(daml_path![sender::p])?);
Ok(())
}
#[test]
pub fn test_nested_party() -> TestResult {
let value: DamlValue = get_test_value();
assert_eq!("Sue", value.extract(daml_path![person / party::p])?);
Ok(())
}
#[test]
pub fn test_nested_contract() -> TestResult {
let value: DamlValue = get_test_value();
assert_eq!("#1:1", value.extract(daml_path![person / data / contractId::c])?);
Ok(())
}
#[test]
pub fn test_nested_value() -> TestResult {
let value: DamlValue = get_test_value();
assert_eq!(&DamlValue::new_contract_id("#1:1"), value.extract(daml_path![person / data / contractId])?);
Ok(())
}
#[test]
pub fn test_nested_int64() -> TestResult {
let value: DamlValue = get_test_value();
assert_eq!(0_i64, *value.extract(daml_path![person / data / count::i])?);
Ok(())
}
#[test]
pub fn test_nested_bool() -> TestResult {
let value: DamlValue = get_test_value();
assert!(!*value.extract(daml_path![person / data / is_true::b])?);
Ok(())
}
#[test]
pub fn test_nested_unit() -> TestResult {
let value: DamlValue = get_test_value();
assert_eq!((), *value.extract(daml_path![person / empty::u])?);
Ok(())
}
#[test]
pub fn test_top_numeric() -> TestResult {
let value: DamlValue = get_test_value();
assert_eq!(&BigDecimal::try_from(1.23).unwrap(), value.extract(daml_path![height::f])?);
Ok(())
}
#[test]
pub fn test_top_date() -> TestResult {
let value: DamlValue = get_test_value();
assert_eq!(&make_date("2019-01-02")?, value.extract(daml_path![today::d])?);
Ok(())
}
#[test]
pub fn test_top_datetime() -> TestResult {
let value: DamlValue = get_test_value();
assert_eq!(&make_timestamp("2019-01-02T03:45:56Z")?, value.extract(daml_path![right_now::s])?);
Ok(())
}
#[test]
pub fn test_top_optional_value() -> TestResult {
let value: DamlValue = get_test_value();
assert_eq!(&DamlValue::new_int64(123), value.extract(daml_path![opt_int?])?);
Ok(())
}
#[test]
pub fn test_top_variant_value() -> TestResult {
let value: DamlValue = get_test_value();
assert_eq!(&DamlValue::new_text("I'm a Foo"), value.extract(daml_path![variant_text{=>Foo}])?);
Ok(())
}
#[test]
pub fn test_top_variant() -> TestResult {
let value: DamlValue = get_test_value();
assert_eq!(
&DamlVariant::new("Foo", Box::new(DamlValue::new_text("I'm a Foo")), None),
value.extract(daml_path![variant_text::v])?
);
Ok(())
}
#[test]
pub fn test_nested_text() -> TestResult {
let value: DamlValue = get_test_value();
assert_eq!("apple", value.extract(daml_path![person / data / fruit::t])?);
Ok(())
}
#[test]
pub fn test_nested_record() -> TestResult {
let value: DamlValue = get_test_value();
assert_eq!(4, value.extract(daml_path![person / data::r])?.fields().len());
Ok(())
}
#[test]
pub fn test_nested_list() -> TestResult {
let value: DamlValue = get_test_value();
assert_eq!(3, value.extract(daml_path![person / stats::l])?.len());
Ok(())
}
#[test]
pub fn test_list_item() -> TestResult {
let value: DamlValue = get_test_value();
assert_eq!("a", value.extract(daml_path![items::l])?[0].extract(daml_path![a::t])?);
assert_eq!("b", value.extract(daml_path![items::l])?[1].extract(daml_path![b::t])?);
Ok(())
}
#[test]
pub fn test_top_list_record_item_text() -> TestResult {
let value: DamlValue = get_test_value();
assert_eq!("a", value.extract(daml_path![items[0] / a::t])?);
Ok(())
}
#[test]
pub fn test_top_list_leaf_item_text() -> TestResult {
let value: DamlValue = get_test_value();
assert_eq!("foo", value.extract(daml_path![simple_list[0]::t])?);
assert_eq!("bar", value.extract(daml_path![simple_list[1]::t])?);
Ok(())
}
#[test]
pub fn test_top_list_leaf_item_value() -> TestResult {
let value: DamlValue = get_test_value();
assert_eq!(&DamlValue::new_text("bar"), value.extract(daml_path![simple_list[1]])?);
Ok(())
}
#[test]
pub fn test_list_index_expression() -> TestResult {
let value: DamlValue = get_test_value();
assert_eq!("bar", value.extract(daml_path![simple_list[2 - 1]::t])?);
Ok(())
}
#[test]
pub fn test_nested_list_item_value() -> TestResult {
let value: DamlValue = get_test_value();
assert_eq!(&DamlValue::new_int64(1), value.extract(daml_path![person / stats[0]])?);
Ok(())
}
#[test]
pub fn test_nested_list_item_int64() -> TestResult {
let value: DamlValue = get_test_value();
assert_eq!(1, *value.extract(daml_path![person/stats[0]::i])?);
Ok(())
}
#[test]
pub fn test_list_in_list_of_record() -> TestResult {
let value: DamlValue = get_test_value();
assert_eq!(99, *value.extract(daml_path![items[1]/the_list[0]::i])?);
Ok(())
}
#[test]
pub fn test_optional_int() -> TestResult {
let value: DamlValue = get_test_value();
assert_eq!(123, *value.extract(daml_path![opt_int?::i])?);
Ok(())
}
#[test]
pub fn test_optional_rec() -> TestResult {
let value: DamlValue = get_test_value();
assert_eq!("cat", value.extract(daml_path![opt_rec? / pet::t])?);
Ok(())
}
#[test]
pub fn test_nested_optional_rec() -> TestResult {
let value: DamlValue = get_test_value();
assert!(*value.extract(daml_path![opt_rec?/is_cat?::b])?);
Ok(())
}
#[test]
pub fn test_list_of_optional_final() -> TestResult {
let value: DamlValue = get_test_value();
assert_eq!(1, *value.extract(daml_path![list_of_opt[0]?::i])?);
Ok(())
}
#[test]
pub fn test_list_of_optional_non_final() -> TestResult {
let value: DamlValue = get_test_value();
assert_eq!("a", value.extract(daml_path![list_of_opt_rec[0]? / a::t])?);
Ok(())
}
#[test]
pub fn test_top_named_variant_text() -> TestResult {
let value: DamlValue = get_test_value();
assert_eq!("I'm a Foo", value.extract(daml_path![variant_text{=>Foo}::t])?);
Ok(())
}
#[test]
pub fn test_top_named_variant_value() -> TestResult {
let value: DamlValue = get_test_value();
assert_eq!(&DamlValue::new_text("I'm a Foo"), value.extract(daml_path![variant_text{=>Foo}])?);
Ok(())
}
#[test]
pub fn test_top_any_variant_text() -> TestResult {
let value: DamlValue = get_test_value();
assert_eq!("I'm a Foo", value.extract(daml_path![variant_text{=>__}::t])?);
Ok(())
}
#[test]
pub fn test_top_named_variant_optional_int() -> TestResult {
let value: DamlValue = get_test_value();
assert_eq!(999, *value.extract(daml_path![variant_opt_int{=>Foo}?::i])?);
Ok(())
}
#[test]
pub fn test_top_named_variant_list() -> TestResult {
let value: DamlValue = get_test_value();
assert_eq!("One", value.extract(daml_path![variant_list{=>Foo}[0]::t])?);
Ok(())
}
#[test]
pub fn test_top_named_variant_list_optional() -> TestResult {
let value: DamlValue = get_test_value();
assert_eq!("Two", value.extract(daml_path![variant_list_opt{=>Foo}[1]?::t])?);
Ok(())
}
#[test]
pub fn test_nested_named_variant_int() -> TestResult {
let value: DamlValue = get_test_value();
assert_eq!(4, *value.extract(daml_path![other{=>Cat}/paw_count::i])?);
Ok(())
}
#[test]
pub fn test_nested_any_variant_int() -> TestResult {
let value: DamlValue = get_test_value();
assert_eq!(4, *value.extract(daml_path![other{=>__}/paw_count::i])?);
Ok(())
}
#[test]
pub fn test_nested_named_variant_optional_int() -> TestResult {
let value: DamlValue = get_test_value();
assert_eq!("Red", value.extract(daml_path![other_opt{=>Fruit}?/color::t])?);
Ok(())
}
#[test]
pub fn test_nested_named_variant_list() -> TestResult {
let value: DamlValue = get_test_value();
assert_eq!("Blue", value.extract(daml_path![other_var_list{=>Foo}[0]/color::t])?);
Ok(())
}
#[test]
pub fn test_nested_named_variant_list_optional() -> TestResult {
let value: DamlValue = get_test_value();
assert_eq!("Green", value.extract(daml_path![other_var_list_opt{=>Foo}[0]?/color::t])?);
Ok(())
}
#[test]
pub fn test_top_no_result_function() {
let value: DamlValue = get_test_value();
assert_eq!("Alice", value.extract(daml_path![sender::p]).expect("should not fail"));
}
#[test]
pub fn test_unknown_field() {
let value: DamlValue = get_test_value();
let result = value.extract(daml_path![unknown]);
match result {
Err(DamlError::UnknownField(s)) => assert_eq!("unknown", s),
_ => panic!("expected failure"),
}
}
#[test]
pub fn test_nested_unknown_field() {
let value: DamlValue = get_test_value();
let result = value.extract(daml_path![person / unknown]);
match result {
Err(DamlError::UnknownField(s)) => assert_eq!("unknown", s),
_ => panic!("expected failure"),
}
}
#[test]
pub fn test_wrong_type_field() {
let value: DamlValue = get_test_value();
let result = value.extract(daml_path![sender::i]);
match result {
Err(DamlError::UnexpectedType(expected, actual)) => {
assert_eq!("Int64", expected);
assert_eq!("Party", actual);
},
_ => panic!("expected failure"),
}
}
#[test]
pub fn test_list_index_out_of_range() {
let value: DamlValue = get_test_value();
let result = value.extract(daml_path![items[99]::i]);
match result {
Err(DamlError::ListIndexOutOfRange(idx)) => assert_eq!(99, idx),
_ => panic!("expected failure"),
}
}
#[test]
pub fn test_bad_variant() {
let value: DamlValue = get_test_value();
let result = value.extract(daml_path![variant_text{=>Bar}::t]);
match result {
Err(DamlError::UnexpectedVariant(expected, actual)) => {
assert_eq!("Bar", expected);
assert_eq!("Foo", actual);
},
_ => panic!("expected failure"),
}
}
fn get_test_value() -> DamlValue {
daml_value![{
sender: "Alice"::p,
receiver: "Bob"::p,
person: {
party: "Sue"::p,
data: {
count: 0,
fruit: "apple",
contractId: "#1:1"::c,
is_true: false
},
stats: [1, 2, 3],
empty: ()
},
height: 1.23,
items: [{a: "a"}, {b: "b", the_list: [99, 98, 101]}],
simple_list: ["foo", "bar", ["text in nested list"]],
today: "2019-01-02"::d,
right_now: "2019-01-02T03:45:56Z"::t,
opt_int: {?= 123},
opt_rec: {?= {
pet: "cat",
is_cat: {?= true},
food: ["ham", "eggs"]
}},
list_of_opt: [{?=1}, {?=2}, {?=3}],
opt_list: {?=[1,2,3]},
list_of_opt_rec: [
{?= {a: "a"}},
{?= {b: "b"}},
{?= {c: "c"}}
],
variant_text: {=>Foo "I'm a Foo"},
variant_opt_int: {=>Foo {?= 999}},
variant_list: {=>Foo ["One", "Two"]},
variant_list_opt: {=>Foo [{?="One"}, {?="Two"}]},
other: {=>Cat {
paw_count: 4
}},
other_opt: {=>Fruit {?= {
color: "Red"
}}},
other_var_list: {=>Foo [{color: "Blue"}]},
other_var_list_opt: {=>Foo [{?= {color: "Green"}}]}
}]
}
}