2021-08-14 01:25:31 +00:00
|
|
|
//! The implementation of [capability attenuation][crate::actor::Cap]:
|
|
|
|
//! filtering and rewriting of assertions and messages.
|
|
|
|
|
2021-07-15 07:13:31 +00:00
|
|
|
use preserves::value::Map;
|
|
|
|
use preserves::value::NestedValue;
|
|
|
|
use preserves::value::Value;
|
|
|
|
|
|
|
|
use std::convert::TryFrom;
|
|
|
|
|
|
|
|
use super::schemas::sturdy::*;
|
|
|
|
|
2021-08-14 01:25:31 +00:00
|
|
|
/// A triple of (1) the count of bindings captured by (2) a checked
|
|
|
|
/// `Pattern`, plus (3) a checked `Template`.
|
2021-07-15 07:13:31 +00:00
|
|
|
pub type CheckedRewrite = (usize, Pattern, Template);
|
|
|
|
|
2021-08-14 01:25:31 +00:00
|
|
|
/// A safety-checked [`Caveat`]: none of the errors enumerated in
|
|
|
|
/// `CaveatError` apply.
|
2021-07-15 07:13:31 +00:00
|
|
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
2023-02-06 13:48:18 +00:00
|
|
|
pub enum CheckedCaveat {
|
|
|
|
Alts(Vec<CheckedRewrite>),
|
|
|
|
Reject(Pattern),
|
|
|
|
}
|
2021-07-15 07:13:31 +00:00
|
|
|
|
2021-08-14 01:25:31 +00:00
|
|
|
/// Represents any detected error in a [`Caveat`]; that is, in a
|
|
|
|
/// [`Pattern`] or a [`Template`].
|
2021-07-15 07:13:31 +00:00
|
|
|
#[derive(Debug)]
|
|
|
|
pub enum CaveatError {
|
2021-08-14 01:25:31 +00:00
|
|
|
/// A template refers to a binding not present in the corresponding pattern.
|
2021-07-15 07:13:31 +00:00
|
|
|
UnboundRef,
|
2021-08-14 01:25:31 +00:00
|
|
|
/// A pattern includes a negation of a subpattern that includes a binding.
|
2021-07-15 07:13:31 +00:00
|
|
|
BindingUnderNegation,
|
|
|
|
}
|
|
|
|
|
2023-02-06 13:48:18 +00:00
|
|
|
impl Caveat {
|
|
|
|
/// Yields `Ok(())` iff `caveats` have no [`CaveatError`].
|
|
|
|
pub fn validate_many(caveats: &[Caveat]) -> Result<(), CaveatError> {
|
|
|
|
for c in caveats { c.validate()? }
|
2021-07-15 07:13:31 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2021-08-14 01:25:31 +00:00
|
|
|
/// Yields `Ok(())` iff `self` has no [`CaveatError`].
|
2021-07-15 07:13:31 +00:00
|
|
|
pub fn validate(&self) -> Result<(), CaveatError> {
|
|
|
|
match self {
|
|
|
|
Caveat::Rewrite(b) => (&**b).validate(),
|
|
|
|
Caveat::Alts(b) => (&**b).alternatives.iter().map(Rewrite::validate).collect::<Result<(), _>>(),
|
2023-02-06 13:48:18 +00:00
|
|
|
Caveat::Reject(_) => Ok(()),
|
|
|
|
Caveat::Unknown(_) => Ok(()), /* it's valid to have unknown caveats, they just won't pass anything */
|
2021-07-15 07:13:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-06 13:48:18 +00:00
|
|
|
/// Yields a vector of [`CheckedCaveat`s][CheckedCaveat] iff
|
|
|
|
/// `caveats` have no [`CaveatError`].
|
|
|
|
pub fn check_many(caveats: &[Caveat]) -> Result<Vec<CheckedCaveat>, CaveatError> {
|
|
|
|
caveats.iter().map(Caveat::check).collect()
|
|
|
|
}
|
|
|
|
|
2021-08-14 01:25:31 +00:00
|
|
|
/// Yields a [`CheckedCaveat`] iff `self` has no [`CaveatError`].
|
2021-07-15 07:13:31 +00:00
|
|
|
pub fn check(&self) -> Result<CheckedCaveat, CaveatError> {
|
|
|
|
match self {
|
|
|
|
Caveat::Rewrite(b) =>
|
2023-02-06 13:48:18 +00:00
|
|
|
Ok(CheckedCaveat::Alts(vec![ (*b).check()? ])),
|
2021-07-15 07:13:31 +00:00
|
|
|
Caveat::Alts(b) => {
|
|
|
|
let Alts { alternatives } = &**b;
|
2023-02-06 13:48:18 +00:00
|
|
|
Ok(CheckedCaveat::Alts(
|
|
|
|
alternatives.into_iter().map(Rewrite::check)
|
|
|
|
.collect::<Result<Vec<CheckedRewrite>, CaveatError>>()?))
|
2021-07-15 07:13:31 +00:00
|
|
|
}
|
2023-02-06 13:48:18 +00:00
|
|
|
Caveat::Reject(b) =>
|
|
|
|
Ok(CheckedCaveat::Reject(b.pattern.clone())),
|
|
|
|
Caveat::Unknown(_) =>
|
|
|
|
Ok(CheckedCaveat::Reject(Pattern::PDiscard(Box::new(PDiscard)))),
|
2021-07-15 07:13:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Pattern {
|
|
|
|
fn binding_count(&self) -> Result<usize, CaveatError> {
|
|
|
|
match self {
|
|
|
|
Pattern::PDiscard(_) |
|
|
|
|
Pattern::PAtom(_) |
|
|
|
|
Pattern::PEmbedded(_) |
|
|
|
|
Pattern::Lit(_) => Ok(0),
|
|
|
|
Pattern::PBind(b) => Ok(1 + (&**b).pattern.binding_count()?),
|
|
|
|
Pattern::PNot(b) => {
|
|
|
|
let PNot { pattern } = &**b;
|
|
|
|
if pattern.binding_count()? == 0 {
|
|
|
|
Ok(0)
|
|
|
|
} else {
|
|
|
|
Err(CaveatError::BindingUnderNegation)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Pattern::PAnd(b) => {
|
|
|
|
let mut count = 0;
|
|
|
|
for p in &(&**b).patterns {
|
|
|
|
count += p.binding_count()?
|
|
|
|
}
|
|
|
|
Ok(count)
|
|
|
|
}
|
2021-12-13 15:00:25 +00:00
|
|
|
Pattern::PCompound(b) => match &**b {
|
|
|
|
PCompound::Rec { fields: items, .. } |
|
|
|
|
PCompound::Arr { items } => {
|
|
|
|
let mut count = 0;
|
|
|
|
for p in items.iter() {
|
|
|
|
count += p.binding_count()?;
|
2021-07-15 07:13:31 +00:00
|
|
|
}
|
2021-12-13 15:00:25 +00:00
|
|
|
Ok(count)
|
|
|
|
}
|
|
|
|
PCompound::Dict { entries } => {
|
|
|
|
let mut count = 0;
|
|
|
|
for (_k, p) in entries.iter() {
|
|
|
|
count += p.binding_count()?;
|
|
|
|
}
|
|
|
|
Ok(count)
|
2021-07-15 07:13:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn matches(&self, a: &_Any, bindings: &mut Vec<_Any>) -> bool {
|
|
|
|
match self {
|
|
|
|
Pattern::PDiscard(_) => true,
|
|
|
|
Pattern::PAtom(b) => match &**b {
|
|
|
|
PAtom::Boolean => a.value().is_boolean(),
|
|
|
|
PAtom::Double => a.value().is_double(),
|
|
|
|
PAtom::SignedInteger => a.value().is_signedinteger(),
|
|
|
|
PAtom::String => a.value().is_string(),
|
|
|
|
PAtom::ByteString => a.value().is_bytestring(),
|
|
|
|
PAtom::Symbol => a.value().is_symbol(),
|
|
|
|
}
|
|
|
|
Pattern::PEmbedded(_) => a.value().is_embedded(),
|
|
|
|
Pattern::PBind(b) => {
|
|
|
|
bindings.push(a.clone());
|
|
|
|
(&**b).pattern.matches(a, bindings)
|
|
|
|
}
|
|
|
|
Pattern::PAnd(b) => {
|
|
|
|
for p in &(&**b).patterns {
|
|
|
|
if !p.matches(a, bindings) { return false; }
|
|
|
|
}
|
|
|
|
true
|
|
|
|
},
|
|
|
|
Pattern::PNot(b) => !(&**b).pattern.matches(a, bindings),
|
|
|
|
Pattern::Lit(b) => &(&**b).value == a,
|
|
|
|
Pattern::PCompound(b) => match &**b {
|
2021-12-13 15:00:25 +00:00
|
|
|
PCompound::Rec { label, fields } => {
|
|
|
|
match a.value().as_record(Some(fields.len())) {
|
2021-07-15 07:13:31 +00:00
|
|
|
Some(r) => {
|
|
|
|
if r.label() != label { return false; }
|
2021-12-13 15:05:43 +00:00
|
|
|
if r.fields().len() != fields.len() { return false; }
|
2021-12-13 15:00:25 +00:00
|
|
|
for (i, p) in fields.iter().enumerate() {
|
|
|
|
if !p.matches(&r.fields()[i], bindings) { return false; }
|
2021-07-15 07:13:31 +00:00
|
|
|
}
|
|
|
|
true
|
|
|
|
},
|
|
|
|
None => false,
|
|
|
|
}
|
|
|
|
}
|
2021-12-13 15:00:25 +00:00
|
|
|
PCompound::Arr { items } => {
|
2021-07-15 07:13:31 +00:00
|
|
|
match a.value().as_sequence() {
|
|
|
|
Some(vs) => {
|
2021-12-13 15:05:43 +00:00
|
|
|
if vs.len() != items.len() { return false; }
|
2021-12-13 15:00:25 +00:00
|
|
|
for (i, p) in items.iter().enumerate() {
|
|
|
|
if !p.matches(&vs[i], bindings) { return false; }
|
2021-07-15 07:13:31 +00:00
|
|
|
}
|
|
|
|
true
|
|
|
|
},
|
|
|
|
None => false,
|
|
|
|
}
|
|
|
|
}
|
2021-12-13 15:00:25 +00:00
|
|
|
PCompound::Dict { entries } => {
|
2021-07-15 07:13:31 +00:00
|
|
|
match a.value().as_dictionary() {
|
|
|
|
Some(es) => {
|
2021-12-13 15:00:25 +00:00
|
|
|
for (k, p) in entries.iter() {
|
2021-07-15 07:13:31 +00:00
|
|
|
match es.get(k) {
|
|
|
|
Some(v) => if !p.matches(v, bindings) { return false; },
|
|
|
|
None => return false,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
true
|
|
|
|
}
|
|
|
|
None => false,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Template {
|
|
|
|
fn implied_binding_count(&self) -> Result<usize, CaveatError> {
|
|
|
|
match self {
|
|
|
|
Template::TAttenuate(b) => {
|
|
|
|
let TAttenuate { template, attenuation } = &**b;
|
2023-02-06 13:48:18 +00:00
|
|
|
Caveat::validate_many(attenuation)?;
|
2021-07-15 07:13:31 +00:00
|
|
|
Ok(template.implied_binding_count()?)
|
|
|
|
}
|
|
|
|
Template::TRef(b) => match usize::try_from(&(&**b).binding) {
|
|
|
|
Ok(v) => Ok(1 + v),
|
|
|
|
Err(_) => Err(CaveatError::UnboundRef),
|
|
|
|
},
|
|
|
|
Template::Lit(_) => Ok(0),
|
2021-12-13 15:00:25 +00:00
|
|
|
Template::TCompound(b) => match &**b {
|
|
|
|
TCompound::Rec { fields: items, .. } |
|
|
|
|
TCompound::Arr { items } => {
|
|
|
|
let mut max = 0;
|
|
|
|
for t in items.iter() {
|
|
|
|
max = max.max(t.implied_binding_count()?);
|
2021-07-15 07:13:31 +00:00
|
|
|
}
|
2021-12-13 15:00:25 +00:00
|
|
|
Ok(max)
|
2021-07-15 07:13:31 +00:00
|
|
|
}
|
2021-12-13 15:00:25 +00:00
|
|
|
TCompound::Dict { entries } => {
|
|
|
|
let mut max = 0;
|
|
|
|
for (_k, t) in entries.iter() {
|
|
|
|
max = max.max(t.implied_binding_count()?);
|
2021-07-15 07:13:31 +00:00
|
|
|
}
|
2021-12-13 15:00:25 +00:00
|
|
|
Ok(max)
|
2021-07-15 07:13:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn instantiate(&self, bindings: &Vec<_Any>) -> Option<_Any> {
|
|
|
|
match self {
|
|
|
|
Template::TAttenuate(b) => {
|
|
|
|
let TAttenuate { template, attenuation } = &**b;
|
|
|
|
template.instantiate(bindings)
|
|
|
|
.and_then(|r| r.value().as_embedded().cloned())
|
|
|
|
.map(|r| Value::Embedded(r.attenuate(attenuation).expect("checked attenuation")).wrap())
|
|
|
|
}
|
|
|
|
Template::TRef(b) => Some(bindings[usize::try_from(&(&**b).binding).expect("in-range index")].clone()),
|
|
|
|
Template::Lit(b) => Some((&**b).value.clone()),
|
2021-12-13 15:00:25 +00:00
|
|
|
Template::TCompound(b) => match &**b {
|
|
|
|
TCompound::Rec { label, fields } => {
|
|
|
|
let mut r = Value::record(label.clone(), fields.len());
|
|
|
|
for t in fields.iter() {
|
|
|
|
r.fields_vec_mut().push(t.instantiate(bindings)?);
|
2021-07-15 07:13:31 +00:00
|
|
|
}
|
2021-12-13 15:00:25 +00:00
|
|
|
Some(r.finish().wrap())
|
|
|
|
}
|
|
|
|
TCompound::Arr { items } => {
|
|
|
|
let mut r = Vec::with_capacity(items.len());
|
|
|
|
for t in items.iter() {
|
|
|
|
r.push(t.instantiate(bindings)?);
|
2021-07-15 07:13:31 +00:00
|
|
|
}
|
2021-12-13 15:00:25 +00:00
|
|
|
Some(Value::from(r).wrap())
|
|
|
|
}
|
|
|
|
TCompound::Dict { entries } => {
|
|
|
|
let mut r = Map::new();
|
|
|
|
for (k, t) in entries.iter() {
|
|
|
|
r.insert(k.clone(), t.instantiate(bindings)?);
|
2021-07-15 07:13:31 +00:00
|
|
|
}
|
2021-12-13 15:00:25 +00:00
|
|
|
Some(Value::from(r).wrap())
|
2021-07-15 07:13:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Rewrite {
|
|
|
|
fn validated_binding_count(&self) -> Result<usize, CaveatError> {
|
|
|
|
let binding_count = self.pattern.binding_count()?;
|
|
|
|
let implied_binding_count = self.template.implied_binding_count()?;
|
|
|
|
if implied_binding_count > binding_count { return Err(CaveatError::UnboundRef); }
|
|
|
|
Ok(binding_count)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn validate(&self) -> Result<(), CaveatError> {
|
|
|
|
let _ = self.validated_binding_count()?;
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn check(&self) -> Result<CheckedRewrite, CaveatError> {
|
|
|
|
Ok((self.validated_binding_count()?, self.pattern.clone(), self.template.clone()))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl CheckedCaveat {
|
2021-08-14 01:25:31 +00:00
|
|
|
/// Rewrites `a` using the patterns/templates contained in `self`.
|
2021-07-15 07:13:31 +00:00
|
|
|
pub fn rewrite(&self, a: &_Any) -> Option<_Any> {
|
2023-02-06 13:48:18 +00:00
|
|
|
match self {
|
|
|
|
CheckedCaveat::Alts(alts) => {
|
|
|
|
for (n, p, t) in alts {
|
|
|
|
let mut bindings = Vec::with_capacity(*n);
|
|
|
|
if p.matches(a, &mut bindings) {
|
|
|
|
return t.instantiate(&bindings);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
None
|
|
|
|
},
|
|
|
|
CheckedCaveat::Reject(pat) => {
|
|
|
|
let mut bindings = Vec::with_capacity(0);
|
|
|
|
if pat.matches(a, &mut bindings) {
|
|
|
|
None
|
|
|
|
} else {
|
|
|
|
Some(a.clone())
|
|
|
|
}
|
2021-07-15 07:13:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|