Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 102 additions & 1 deletion sdk/src/trp/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use base64::Engine as _;
use serde::{Deserialize, Serialize};
use serde_json::{json, Number, Value};
use thiserror::Error;
use tx3_lang::{ir::Type, UtxoRef};
use tx3_lang::{ir::Type, CustomValue, UtxoRef};

pub use tx3_lang::ArgValue;

Expand Down Expand Up @@ -157,6 +157,59 @@ fn value_to_utxo_ref(value: Value) -> Result<UtxoRef, Error> {
}
}

fn value_to_custom(value: Value) -> Result<CustomValue, Error> {
let obj = match value {
Value::Object(map) => map,
x => return Err(Error::ValueIsNotCustom(x)),
};

let constructor = obj
.get("constructor")
.and_then(|v| v.as_u64())
.ok_or_else(|| Error::InvalidCustomType("missing or invalid constructor".to_string()))?
as usize;

let fields_value = obj
.get("fields")
.ok_or_else(|| Error::InvalidCustomType("missing fields".to_string()))?;

let fields_array = match fields_value {
Value::Array(arr) => arr,
_ => {
return Err(Error::InvalidCustomType(
"fields is not an array".to_string(),
))
}
};

// recursively deserialize each field
let fields: Result<Vec<ArgValue>, Error> = fields_array
.iter()
.cloned()
.map(|field_value| {
if let Value::Object(ref obj) = field_value {
if obj.contains_key("constructor") && obj.contains_key("fields") {
return value_to_custom(field_value).map(ArgValue::Custom);
}
}
value_to_underfined(field_value)
})
.collect();

Ok(CustomValue {
constructor,
fields: fields?,
})
}

fn custom_to_value(custom: CustomValue) -> Value {
let fields: Vec<Value> = custom.fields.into_iter().map(to_json).collect();
json!({
"constructor": custom.constructor,
"fields": fields
})
}

#[derive(Debug, Error)]
pub enum Error {
#[error("value is null")]
Expand Down Expand Up @@ -186,6 +239,12 @@ pub enum Error {
#[error("value is not a utxo ref: {0}")]
ValueIsNotUtxoRef(Value),

#[error("value is not a custom type: {0}")]
ValueIsNotCustom(Value),

#[error("invalid custom type: {0}")]
InvalidCustomType(String),

#[error("invalid bytes envelope: {0}")]
InvalidBytesEnvelope(serde_json::Error),

Expand Down Expand Up @@ -220,6 +279,11 @@ pub fn to_json(value: ArgValue) -> Value {
Value::Array(v)
}
ArgValue::UtxoRef(x) => utxoref_to_value(x),
ArgValue::Custom(x) => custom_to_value(x),
ArgValue::List(values) => {
let v = values.into_iter().map(|x| to_json(x)).collect();
Value::Array(v)
}
}
}

Expand All @@ -245,6 +309,24 @@ pub fn from_json(value: Value, target: &Type) -> Result<ArgValue, Error> {
let x = value_to_utxo_ref(value)?;
Ok(ArgValue::UtxoRef(x))
}
Type::Custom(_) => {
let x = value_to_custom(value)?;
Ok(ArgValue::Custom(x))
}
Type::List => {
let arr = match value {
Value::Array(arr) => arr,
_ => return Err(Error::ValueIsNotAString),
};

let mut values = Vec::with_capacity(arr.len());
for item in arr {
let v = from_json(item, &Type::Undefined)?;
values.push(v);
}

Ok(ArgValue::List(values))
}
Type::Undefined => value_to_underfined(value),
x => Err(Error::TargetTypeNotSupported(x.clone())),
}
Expand Down Expand Up @@ -285,6 +367,25 @@ mod tests {
ArgValue::UtxoRef(b) => utxo_ref == b,
_ => false,
},
ArgValue::Custom(custom) => match b {
ArgValue::Custom(b) => {
custom.constructor == b.constructor
&& custom.fields.len() == b.fields.len()
&& custom
.fields
.into_iter()
.zip(b.fields)
.all(|(a, b)| partial_eq(a, b))
}
_ => false,
},
ArgValue::List(values) => match b {
ArgValue::List(b) => {
values.len() == b.len()
&& values.into_iter().zip(b).all(|(a, b)| partial_eq(a, b))
}
_ => false,
},
}
}

Expand Down
Loading