mod exception;
mod limits;
mod os_call;
mod resume;
mod type_checking;
use std::{error, fmt};
use monty_types::{DictPairs, MontyObject};
pub use resume::future_results_from_proto;
#[derive(Debug)]
pub enum ProtoConvertError {
MissingField(&'static str),
UnknownExcType(String),
UnknownType(String),
UnknownBuiltinFunction(String),
InvalidFileMode(String),
InvalidValue {
field: &'static str,
reason: String,
},
}
impl fmt::Display for ProtoConvertError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::MissingField(field) => write!(f, "missing required field {field}"),
Self::UnknownExcType(name) => write!(f, "unknown exception type {name:?}"),
Self::UnknownType(name) => write!(f, "unknown type name {name:?}"),
Self::UnknownBuiltinFunction(name) => write!(f, "unknown builtin function {name:?}"),
Self::InvalidFileMode(mode) => write!(f, "invalid file mode {mode:?}"),
Self::InvalidValue { field, reason } => write!(f, "invalid value for {field}: {reason}"),
}
}
}
impl error::Error for ProtoConvertError {}
const PROST_RECURSION_LIMIT: usize = 100;
const FRAME_WRAPPER_DEPTH: usize = 3;
const MAX_PROTO_VALUE_DEPTH: usize = PROST_RECURSION_LIMIT - FRAME_WRAPPER_DEPTH;
const LIST_COST: usize = 2;
const DICT_COST: usize = 3;
const DATACLASS_COST: usize = 4;
pub const MAX_VALUE_DEPTH: usize = (MAX_PROTO_VALUE_DEPTH - 1) / LIST_COST;
#[must_use]
pub fn exceeds_max_value_depth(value: &MontyObject) -> bool {
depth_exceeds(value, MAX_PROTO_VALUE_DEPTH)
}
fn depth_exceeds(value: &MontyObject, budget: usize) -> bool {
match value {
MontyObject::List(items)
| MontyObject::Tuple(items)
| MontyObject::Set(items)
| MontyObject::FrozenSet(items) => seq_exceeds(items, budget, LIST_COST),
MontyObject::NamedTuple { values, .. } => seq_exceeds(values, budget, LIST_COST),
MontyObject::Dict(pairs) => pairs_exceed(pairs, budget, DICT_COST),
MontyObject::Dataclass { attrs, .. } => pairs_exceed(attrs, budget, DATACLASS_COST),
_ => budget == 0,
}
}
fn seq_exceeds(items: &[MontyObject], budget: usize, cost: usize) -> bool {
match budget.checked_sub(cost) {
None => true,
Some(remaining) => items.iter().any(|child| depth_exceeds(child, remaining)),
}
}
fn pairs_exceed(pairs: &DictPairs, budget: usize, cost: usize) -> bool {
match budget.checked_sub(cost) {
None => true,
Some(remaining) => pairs
.into_iter()
.any(|(key, value)| depth_exceeds(key, remaining) || depth_exceeds(value, remaining)),
}
}