///|
/// Structured error type for all parsing, serialization, and
/// canonicalization failures in this crate.
///
/// The error carries a structured `kind`, a UTF-8 byte `offset` into the
/// original input, and a short human-readable `context`. Offsets are byte
/// offsets into the UTF-8 encoding of the input, not Unicode scalar counts.
pub(all) enum SfErrorKind {
UnexpectedEnd
UnexpectedByte(Byte)
InvalidTopLevelType
InvalidKey
InvalidInteger
IntegerOutOfRange
InvalidDecimal
DecimalOutOfRange
InvalidString
InvalidEscape
InvalidToken
InvalidByteSequence
InvalidBase64
InvalidBoolean
InvalidDate
InvalidDisplayString
InvalidPercentEncoding
InvalidUtf8
InvalidParameter
InvalidInnerList
InvalidDictionary
TrailingInput
TooManyMembers
TooManyParameters
InputTooLarge
SerializationError
} derive(Debug, Eq)
///|
pub(all) struct SfError {
kind : SfErrorKind
offset : Int
context : String
} derive(Debug, Eq)
///|
/// Builds a structured error.
pub fn SfError::make(
kind : SfErrorKind,
offset : Int,
context : String,
) -> SfError {
{ kind, offset, context }
}
///|
/// The category of the failure.
pub fn SfError::kind(self : SfError) -> SfErrorKind {
self.kind
}
///|
/// The UTF-8 byte offset into the input at which the failure occurred.
pub fn SfError::offset(self : SfError) -> Int {
self.offset
}
///|
/// A short description of the surrounding input.
pub fn SfError::context(self : SfError) -> String {
self.context
}
///|
/// Renders a human-readable, single-line description of the error.
///
/// The context is truncated so that very large inputs are never echoed
/// back in full.
pub fn SfError::to_string(self : SfError) -> String {
let kind_label = match self.kind {
UnexpectedEnd => "unexpected end of input"
UnexpectedByte(_) => "unexpected byte"
InvalidTopLevelType => "invalid top-level structured type"
InvalidKey => "invalid key"
InvalidInteger => "invalid integer"
IntegerOutOfRange => "integer out of range"
InvalidDecimal => "invalid decimal"
DecimalOutOfRange => "decimal out of range"
InvalidString => "invalid string"
InvalidEscape => "invalid escape sequence"
InvalidToken => "invalid token"
InvalidByteSequence => "invalid byte sequence"
InvalidBase64 => "invalid base64"
InvalidBoolean => "invalid boolean"
InvalidDate => "invalid date"
InvalidDisplayString => "invalid display string"
InvalidPercentEncoding => "invalid percent encoding"
InvalidUtf8 => "invalid UTF-8"
InvalidParameter => "invalid parameter"
InvalidInnerList => "invalid inner list"
InvalidDictionary => "invalid dictionary"
TrailingInput => "trailing input after value"
TooManyMembers => "too many members"
TooManyParameters => "too many parameters"
InputTooLarge => "input exceeds configured size limit"
SerializationError => "serialization error"
}
let ctx = truncate_for_display(self.context, 40)
let suffix = if ctx.is_empty() { "" } else { ": near \"" + ctx + "\"" }
kind_label + " at byte " + self.offset.to_string() + suffix
}
///|
/// Returns the label of an error kind without offset/context details.
pub fn SfErrorKind::label(self : SfErrorKind) -> String {
let e = SfError::make(self, 0, "")
e.to_string()
}
///|
fn truncate_for_display(s : String, limit : Int) -> String {
if s.length() <= limit {
return s
}
let cut = s[:limit].to_owned()
cut + "..."
}
///|
/// Resource limits applied while parsing. RFC 9651 requires parsers to
/// support at least 1024 List/Dictionary members, 256 parameters, 1024
/// String characters, 512 Token characters, and 16384 decoded Byte
/// Sequence octets; the defaults below satisfy all of those while still
/// bounding hostile inputs.
pub(all) struct ParseLimits {
max_input_bytes : Int
max_members : Int
max_parameters : Int
max_string_bytes : Int
max_inner_list_items : Int
max_nesting_depth : Int
} derive(Debug, Eq)
///|
/// The default [`ParseLimits`], comfortably above every RFC requirement.
pub fn ParseLimits::default() -> ParseLimits {
{
max_input_bytes: 4 * 1024 * 1024,
max_members: 100_000,
max_parameters: 100_000,
max_string_bytes: 1 * 1024 * 1024,
max_inner_list_items: 100_000,
max_nesting_depth: 8,
}
}
///|
/// The three top-level Structured Fields types.
pub(all) enum FieldType {
Item
List
Dictionary
} derive(Debug, Eq)
///|
/// The wire name of a field type, used by the CLI and the conformance
/// harness.
pub fn FieldType::wire_name(self : FieldType) -> String {
match self {
Item => "item"
List => "list"
Dictionary => "dictionary"
}
}
///|
/// Parses a wire field-type name ("item", "list", "dictionary").
pub fn FieldType::from_wire_name(s : String) -> FieldType? {
if s == "item" {
return Some(Item)
}
if s == "list" {
return Some(List)
}
if s == "dictionary" {
return Some(Dictionary)
}
None
}