Skip to content

Commit 3a91c60

Browse files
committed
runtime/stdlib: Support fallible field accessors
The Array field accessor now raises an error if it contains multiple values for the given field.
1 parent 464c205 commit 3a91c60

File tree

5 files changed

+30
-20
lines changed

5 files changed

+30
-20
lines changed

src/error.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,9 @@ pub enum RuntimeError {
122122
#[error("Field does not exists: {}", ValErrFmt(.0))]
123123
FieldNotFound(Box<Value>),
124124

125+
#[error("Attempted to access an array field with multiple values: {} (can use `t::multi($arr, $field)` to get them as an array)", ValErrFmt(.0))]
126+
FieldArrayTagDuplicated(Box<Value>),
127+
125128
#[error("Required value missing")]
126129
MissingValue,
127130

src/runtime/array.rs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -107,17 +107,20 @@ impl Array {
107107

108108
// Tagged array field access
109109
impl FieldAccess for Array {
110-
fn get_field(self, field: &Value) -> Option<Value> {
111-
// Returns the first element tagged with `field`. There may be more.
112-
// Getting multiple values is possible through `t::multi($array, $field)`
110+
fn get_field_fallible(self, field: &Value) -> Result<Option<Value>> {
111+
let mut field_value = None;
113112
for el in self.into_inner() {
114113
if let Value::Array(mut el_arr) = el {
115114
if el_arr.len() == 2 && el_arr[0] == *field {
116-
return Some(el_arr.remove(1));
115+
ensure!(
116+
field_value.is_none(),
117+
Error::FieldArrayTagDuplicated(field.clone().into())
118+
);
119+
field_value = Some(el_arr.remove(1));
117120
}
118121
}
119122
}
120-
None
123+
Ok(field_value)
121124
}
122125
}
123126

src/runtime/mod.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -235,16 +235,21 @@ impl Evaluate for ast::FieldAccess {
235235
fn eval(&self, scope: &ScopeRef) -> Result<Value> {
236236
let target = self.target.eval(scope)?;
237237
let field = self.field.eval(scope)?;
238-
let result = target.get_field(&field);
238+
let result = target.get_field_fallible(&field)?;
239239
if self.check_exists {
240240
Ok(result.is_some().into())
241241
} else {
242242
result.ok_or_else(|| Error::FieldNotFound(field.into()))
243243
}
244244
}
245245
}
246-
pub trait FieldAccess {
247-
fn get_field(self, field: &Value) -> Option<Value>;
246+
pub trait FieldAccess: Sized {
247+
fn get_field_fallible(self, field: &Value) -> Result<Option<Value>> {
248+
Ok(self.get_field(field))
249+
}
250+
fn get_field(self, _field: &Value) -> Option<Value> {
251+
unimplemented!("Must implement either get_field_fallible() or get_field()")
252+
}
248253
}
249254

250255
impl Evaluate for ast::FnExpr {

src/runtime/value.rs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -513,17 +513,17 @@ impl ExprRepr for Value {
513513
}
514514

515515
impl FieldAccess for Value {
516-
fn get_field(self, field: &Value) -> Option<Value> {
516+
fn get_field_fallible(self, field: &Value) -> Result<Option<Value>> {
517517
match self {
518-
Value::Array(x) => x.get_field(field),
519-
Value::Psbt(x) => x.get_field(field),
520-
Value::Transaction(x) => x.get_field(field),
521-
Value::Policy(x) => x.get_field(field),
522-
Value::Descriptor(x) => x.get_field(field),
523-
Value::TapInfo(x) => x.get_field(field),
524-
Value::Address(x) => x.get_field(field),
525-
Value::WshScript(x) => x.get_field(field),
526-
_ => None,
518+
Value::Array(x) => x.get_field_fallible(field),
519+
Value::Psbt(x) => x.get_field_fallible(field),
520+
Value::Transaction(x) => x.get_field_fallible(field),
521+
Value::Policy(x) => x.get_field_fallible(field),
522+
Value::Descriptor(x) => x.get_field_fallible(field),
523+
Value::TapInfo(x) => x.get_field_fallible(field),
524+
Value::Address(x) => x.get_field_fallible(field),
525+
Value::WshScript(x) => x.get_field_fallible(field),
526+
_ => Ok(None),
527527
}
528528
}
529529
}

src/stdlib/miniscript.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -273,8 +273,7 @@ impl FieldAccess for Descriptor {
273273
"scripts" => tap_scripts_to_val(&*self.tap_info().ok()??),
274274
_ => {
275275
return None;
276-
}
277-
// TODO address_type
276+
} // TODO address_type
278277
})
279278
}
280279
}

0 commit comments

Comments
 (0)