///|
/// A scalar or reference expression accepted by an EvoWitness field.
pub(all) enum TypeExpr {
StringType
IntType
BoolType
NumberType
EnumType(Array[String])
RefType(String)
ListType(String)
} derive(Eq, Debug)
///|
/// Validation constraints attached to one field.
pub(all) struct Constraints {
min_int : Int?
max_int : Int?
min_len : Int?
max_len : Int?
default_value : String?
} derive(Eq, Debug)
///|
/// Construct an unconstrained field rule.
pub fn Constraints::none() -> Constraints {
{
min_int: None,
max_int: None,
min_len: None,
max_len: None,
default_value: None,
}
}
///|
/// One named field in an object contract.
pub(all) struct Field {
name : String
type_expr : TypeExpr
required : Bool
constraints : Constraints
line : Int
} derive(Eq, Debug)
///|
/// One object type. Open objects accept unknown fields; closed objects reject them.
pub(all) struct ObjectType {
name : String
open : Bool
fields : Array[Field]
line : Int
} derive(Eq, Debug)
///|
/// A complete named and versioned contract.
pub(all) struct Contract {
name : String
version : String
objects : Array[ObjectType]
} derive(Eq, Debug)
///|
/// Stable parser diagnostic suitable for editors and CI logs.
pub(all) struct ParseError {
line : Int
column : Int
code : String
message : String
source_line : String
} derive(Eq, Debug)
///|
/// Return the object named `name` when it exists.
pub fn Contract::find_object(self : Contract, name : String) -> ObjectType? {
for object in self.objects {
if object.name == name {
return Some(object)
}
}
None
}
///|
/// Return the field named `name` when it exists.
pub fn ObjectType::find_field(self : ObjectType, name : String) -> Field? {
for field in self.fields {
if field.name == name {
return Some(field)
}
}
None
}
///|
/// Check whether this object contains a field with the given name.
pub fn ObjectType::has_field(self : ObjectType, name : String) -> Bool {
self.find_field(name) is Some(_)
}
///|
/// Render a compact, stable type spelling used in diagnostics.
pub fn TypeExpr::render(self : TypeExpr) -> String {
match self {
StringType => "string"
IntType => "int"
BoolType => "bool"
NumberType => "number"
EnumType(values) => "enum:" + values.join("|")
RefType(name) => "ref:" + name
ListType(item) => "list:" + item
}
}
///|
/// Whether the expression points to another object in the contract.
pub fn TypeExpr::referenced_name(self : TypeExpr) -> String? {
match self {
RefType(name) => Some(name)
ListType(item) =>
if item.has_prefix("ref:") {
Some(item[4:].to_owned())
} else {
None
}
_ => None
}
}
///|
/// Return a machine-stable path for a field.
pub fn field_path(object_name : String, field_name : String) -> String {
"$." + object_name + "." + field_name
}
///|
/// Create a parser error without exposing parser internals.
fn parse_error(
line : Int,
code : String,
message : String,
source_line : String,
) -> ParseError {
{ line, column: 1, code, message, source_line }
}