// One error type for every syntax in this package.
//
// The alternative -- an error type per identifier, as @atproto/syntax does with
// its ten Error subclasses -- buys a caller nothing here, because MoonBit
// already forces the raise type into the signature: a function returning
// `Did raise SyntaxError` cannot fail any other way, so the kind is redundant
// at the call site and useful only when errors are collected and reported
// together, which is exactly what `kind` is for.
//
// `reason` is the upstream message, unchanged. That is a deliberate constraint,
// not laziness: it means a test can assert the message and thereby pin that
// this implementation rejects for the same reason the reference one does, not
// merely that it also rejects.
///|
/// Which syntax a value failed to satisfy.
pub(all) enum SyntaxKind {
Did
Handle
AtIdentifier
Nsid
RecordKey
Tid
AtUri
Datetime
Language
Uri
Cid
} derive(Eq, Debug)
///|
/// The name used in error messages, matching the reference implementation's
/// spelling -- "ATURI" and "NSID" are shouted upstream, so they are here.
pub fn SyntaxKind::label(self : Self) -> String {
match self {
Did => "DID"
Handle => "handle"
AtIdentifier => "at-identifier"
Nsid => "NSID"
RecordKey => "record key"
Tid => "TID"
AtUri => "ATURI"
Datetime => "datetime"
Language => "language"
Uri => "URI"
Cid => "CID"
}
}
///|
pub(all) suberror SyntaxError {
SyntaxError(kind~ : SyntaxKind, input~ : String, reason~ : String)
} derive(Eq, Debug)
///|
pub fn SyntaxError::kind(self : Self) -> SyntaxKind {
match self {
SyntaxError(kind~, ..) => kind
}
}
///|
/// The upstream message, on its own. Callers rendering their own text want this
/// rather than `describe_error`, which adds the offending input.
pub fn SyntaxError::reason(self : Self) -> String {
match self {
SyntaxError(reason~, ..) => reason
}
}
///|
pub fn SyntaxError::input(self : Self) -> String {
match self {
SyntaxError(input~, ..) => input
}
}
///|
/// The input is truncated: a DID may be 2048 characters and an AT-URI 8192, and
/// a validation failure that floods a log is its own bug.
pub fn SyntaxError::describe_error(self : Self) -> String {
match self {
SyntaxError(input~, reason~, ..) => {
let shown = if input.length() > 80 {
input[:80].to_owned() + "..."
} else {
input
}
"\{reason} (got: \{shown})"
}
}
}
///|
pub impl Show for SyntaxError with fn output(self, logger) {
logger.write_string(self.describe_error())
}