///|
pub(all) enum Outcome {
Accepted
Rejected(String, String?)
RejectedWithoutError
Released
Modified(Bool, Bool)
} derive(Debug, Eq)
///|
pub extend Outcome with @debug.Debug::{to_repr}
///|
pub extend Outcome with Eq::{equal, not_equal}
///|
pub fn Outcome::value(self : Outcome) -> Value {
match self {
Accepted => Described(ULong(0x24UL), List([]))
RejectedWithoutError => Described(ULong(0x25UL), List([]))
Rejected(condition, description) =>
Described(
ULong(0x25UL),
List([
Described(
ULong(0x1dUL),
List([
Symbol(condition),
description.map(s => Text(s)).unwrap_or(Null),
]),
),
]),
)
Released => Described(ULong(0x26UL), List([]))
Modified(failed, undeliverable) =>
Described(ULong(0x27UL), List([Boolean(failed), Boolean(undeliverable)]))
}
}
///|
fn described_list(
value : Value,
expected : UInt64,
name : String,
) -> Array[Value] raise AmqpError {
match value {
Described(ULong(code), List(items)) if code == expected => items
Described(Symbol(symbol), List(items)) if symbol == name => items
_ => raise Invalid("unexpected composite type", 0)
}
}
///|
fn optional_boolean(value : Value) -> Bool raise AmqpError {
match value {
Null => false
Boolean(b) => b
_ => raise Invalid("expected boolean", 0)
}
}
///|
pub fn Outcome::from_value(value : Value) -> Outcome raise AmqpError {
let (code, items) = match value {
Described(ULong(code), List(items)) => (code, items)
Described(Symbol(symbol), List(items)) => {
let code = match symbol {
"amqp:accepted:list" => 0x24UL
"amqp:rejected:list" => 0x25UL
"amqp:released:list" => 0x26UL
"amqp:modified:list" => 0x27UL
_ => raise Unsupported("delivery state")
}
(code, items)
}
_ => raise Invalid("delivery outcome", 0)
}
match code {
0x24UL | 0x26UL => {
if !items.is_empty() {
raise Invalid("empty outcome fields", 0)
}
if code == 0x24UL {
Accepted
} else {
Released
}
}
0x25UL => {
if items.length() > 1 {
raise Invalid("rejected fields", 0)
}
match field_at(items, 0) {
Null => RejectedWithoutError
error => {
let fields = described_list(error, 0x1dUL, "amqp:error:list")
if fields.length() < 1 || fields.length() > 3 {
raise Invalid("error fields", 0)
}
let condition = match fields[0] {
Symbol(s) => s
_ => raise Invalid("error condition", 0)
}
let description = match field_at(fields, 1) {
Text(s) => Some(s)
Null => None
_ => raise Invalid("error description", 0)
}
if !wire_kind(field_at(fields, 2), 10) {
raise Invalid("error info map", 0)
}
Rejected(condition, description)
}
}
}
0x27UL => {
if items.length() > 3 {
raise Invalid("modified fields", 0)
}
if !(field_at(items, 2) is Null) {
raise Unsupported("modified outcome message annotations")
}
Modified(
optional_boolean(field_at(items, 0)),
optional_boolean(field_at(items, 1)),
)
}
_ => raise Unsupported("non-terminal or transactional delivery state")
}
}
///|
test "outcomes preserve semantic acceptance rejection release and modification" {
let outcomes = [
Accepted,
Released,
Rejected("amqp:not-found", Some("target")),
Modified(true, false),
]
for outcome in outcomes {
assert_eq(
Outcome::from_value(decode_value(encode_value(outcome.value()))),
outcome,
)
}
}