///|
pub(all) enum Param {
Null
Bool(Bool)
Byte(Byte)
Int(Int)
Int16(Int16)
UInt16(UInt16)
UInt(UInt)
UInt64(UInt64)
Int64(Int64)
BigInt(BigInt)
Float(Float)
Double(Double)
Decimal(String)
Date(@time.PlainDate, String?)
Time(@time.PlainTime, String?)
DateTime(@time.PlainDateTime, String?)
Timestamp(@time.ZonedDateTime, String?)
Uuid(String)
Object(Map[String, Param])
Json(Json)
String(String)
Bytes(Bytes)
}
///|
pub impl Show for Param with fn output(self : Param, logger : &Logger) {
logger.write_string(self.to_json().stringify())
}
///|
pub(all) suberror ParamDecodeError {
ParamDecodeError(String)
} derive(Eq, ToJson)
///|
pub impl Show for ParamDecodeError with fn output(
self : ParamDecodeError,
logger : &Logger,
) {
logger.write_string(self.to_json().stringify())
}
///|
pub impl @json.FromJson for Param with fn from_json(json, _path) {
param_from_json_value(json)
}
///|
pub impl Eq for Param with fn equal(self, other) {
match (self, other) {
(Null, Null) => true
(Bool(a), Bool(b)) => a == b
(Byte(a), Byte(b)) => a == b
(Int(a), Int(b)) => a == b
(Int16(a), Int16(b)) => a == b
(UInt16(a), UInt16(b)) => a == b
(UInt(a), UInt(b)) => a == b
(UInt64(a), UInt64(b)) => a == b
(Int64(a), Int64(b)) => a == b
(BigInt(a), BigInt(b)) => a == b
(Float(a), Float(b)) => a == b
(Double(a), Double(b)) => a == b
(Decimal(a), Decimal(b)) => a == b
(Date(a, af), Date(b, bf)) => a == b && af == bf
(Time(a, af), Time(b, bf)) => a == b && af == bf
(DateTime(a, af), DateTime(b, bf)) => a == b && af == bf
(Timestamp(a, af), Timestamp(b, bf)) =>
a.to_string() == b.to_string() && af == bf
(Uuid(a), Uuid(b)) => a == b
(Object(a), Object(b)) => a == b
(Json(a), Json(b)) => a == b
(String(a), String(b)) => a == b
(Bytes(a), Bytes(b)) => a == b
_ => false
}
}
///|
fn pad_with_zeros(value : Int, width : Int) -> String {
let mut out = value.abs().to_string()
while out.length() < width {
out = "0" + out
}
out
}
///|
fn format_year_4(year : Int) -> String {
if year < 0 {
"-" + pad_with_zeros(year, 4)
} else {
pad_with_zeros(year, 4)
}
}
///|
fn pad_fraction_millis(nanosecond : Int) -> String {
pad_with_zeros(nanosecond / 1_000_000, 3)
}
///|
fn pad_fraction_nanos(nanosecond : Int) -> String {
pad_with_zeros(nanosecond, 9)
}
///|
fn apply_temporal_format(
pattern : String,
year : Int,
month : Int,
day : Int,
hour : Int,
minute : Int,
second : Int,
nanosecond : Int,
offset : String,
) -> String {
pattern
.replace_all(old="yyyy", new=format_year_4(year))
.replace_all(old="YYYY", new=format_year_4(year))
.replace_all(old="MM", new=pad_with_zeros(month, 2))
.replace_all(old="dd", new=pad_with_zeros(day, 2))
.replace_all(old="DD", new=pad_with_zeros(day, 2))
.replace_all(old="HH", new=pad_with_zeros(hour, 2))
.replace_all(old="mm", new=pad_with_zeros(minute, 2))
.replace_all(old="ss", new=pad_with_zeros(second, 2))
.replace_all(old="SSS", new=pad_fraction_millis(nanosecond))
.replace_all(old="nnnnnnnnn", new=pad_fraction_nanos(nanosecond))
.replace_all(old="XXX", new=offset)
.replace_all(old="Z", new=offset)
}
///|
pub fn format_plain_date(value : @time.PlainDate, format : String?) -> String {
match format {
Some(pattern) =>
apply_temporal_format(
pattern,
value.year(),
value.month(),
value.day(),
0,
0,
0,
0,
"",
)
None => value.to_string()
}
}
///|
pub fn format_plain_time(value : @time.PlainTime, format : String?) -> String {
match format {
Some(pattern) =>
apply_temporal_format(
pattern,
0,
0,
0,
value.hour(),
value.minute(),
value.second(),
value.nanosecond(),
"",
)
None => value.to_string()
}
}
///|
pub fn format_plain_date_time(
value : @time.PlainDateTime,
format : String?,
) -> String {
match format {
Some(pattern) =>
apply_temporal_format(
pattern,
value.year(),
value.month(),
value.day(),
value.hour(),
value.minute(),
value.second(),
value.nanosecond(),
"",
)
None => value.to_string()
}
}
///|
pub fn format_zoned_date_time(
value : @time.ZonedDateTime,
format : String?,
) -> String {
match format {
Some(pattern) =>
apply_temporal_format(
pattern,
value.year(),
value.month(),
value.day(),
value.hour(),
value.minute(),
value.second(),
value.nanosecond(),
value.offset().to_string(),
)
None => value.to_string()
}
}
///|
pub impl ToJson for Param with fn to_json(self) -> Json {
match self {
Null => Json::null()
Bool(v) => Json::boolean(v)
Byte(v) => Json::number(v.to_int().to_double())
Int(v) => v.to_json()
Int16(v) => v.to_json()
UInt16(v) => v.to_json()
UInt(v) => v.to_json()
UInt64(v) => v.to_json()
Int64(v) => v.to_json()
BigInt(v) => v.to_json()
Float(v) => v.to_json()
Double(v) => v.to_json()
Decimal(v) => Json::string(v)
Date(v, format) => Json::string(format_plain_date(v, format))
Time(v, format) => Json::string(format_plain_time(v, format))
DateTime(v, format) => Json::string(format_plain_date_time(v, format))
Timestamp(v, format) => Json::string(format_zoned_date_time(v, format))
Uuid(v) => Json::string(v)
Object(v) => {
let obj : Map[String, Json] = Map([])
for key in v.keys() {
if v.get(key) is Some(value) {
obj.set(key, value.to_json())
}
}
Json::object(obj)
}
Json(v) => v
String(v) => v.to_json()
Bytes(v) => v.to_json()
}
}
///|
pub fn param_from_json_value(json : Json) -> Param {
match json {
Null => Null
True => Bool(true)
False => Bool(false)
Number(n, ..) =>
if n.to_int().to_double() == n {
Int(n.to_int())
} else {
Double(n)
}
String(s) => String(s)
Array(_) => Json(json)
Object(obj) => {
let out : Map[String, Param] = Map([])
for key in obj.keys() {
if obj.get(key) is Some(value) {
out.set(key, param_from_json_value(value))
}
}
Object(out)
}
}
}
///|
fn[T] param_decode_error(msg : String) -> T raise ParamDecodeError {
raise ParamDecodeError(msg)
}
///|
pub(open) trait FromParam {
fn from_param(Param) -> Self raise ParamDecodeError
}
///|
pub fn[T : FromParam] from_param(value : Param) -> T raise ParamDecodeError {
FromParam::from_param(value)
}
///|
pub fn[T : @json.FromJson] enum_from_param(
value : Param,
) -> T raise ParamDecodeError {
let json = match value {
String(text) => Json::string(text)
_ => value.to_json()
}
@json.from_json(json) catch {
_ => param_decode_error("enum_from_param: expected enum-compatible value")
}
}
///|
pub impl FromParam for Param with fn from_param(self) {
self
}
///|
pub impl FromParam for Unit with fn from_param(value) {
match value {
Null => ()
_ => param_decode_error("Unit::from_param: expected Null")
}
}
///|
pub impl FromParam for Bool with fn from_param(value) {
match value {
Bool(v) => v
_ => param_decode_error("Bool::from_param: expected Bool")
}
}
///|
pub impl FromParam for Byte with fn from_param(value) {
match value {
Byte(v) => v
Int(v) if v >= 0 && v <= 255 => v.to_byte()
_ => param_decode_error("Byte::from_param: expected Byte-compatible value")
}
}
///|
pub impl FromParam for Int with fn from_param(value) {
match value {
Int(v) => v
Int16(v) => v.to_int()
UInt16(v) => v.to_int()
Byte(v) => v.to_int()
_ => param_decode_error("Int::from_param: expected Int-compatible value")
}
}
///|
pub impl FromParam for Int16 with fn from_param(value) {
match value {
Int16(v) => v
Int(v) if v >= -32768 && v <= 32767 => Int16::from_int(v)
_ =>
param_decode_error("Int16::from_param: expected Int16-compatible value")
}
}
///|
pub impl FromParam for UInt16 with fn from_param(value) {
match value {
UInt16(v) => v
Int(v) if v >= 0 && v <= 65535 => v.to_uint16()
_ =>
param_decode_error("UInt16::from_param: expected UInt16-compatible value")
}
}
///|
pub impl FromParam for UInt with fn from_param(value) {
match value {
UInt(v) => v
UInt16(v) => v.to_uint()
Byte(v) => v.to_uint()
Int(v) if v >= 0 => v.reinterpret_as_uint()
String(s) =>
@string.parse_uint(s) catch {
_ => param_decode_error("UInt::from_param: invalid UInt string")
}
_ => param_decode_error("UInt::from_param: expected UInt-compatible value")
}
}
///|
pub impl FromParam for UInt64 with fn from_param(value) {
match value {
UInt64(v) => v
UInt(v) => v.to_uint64()
UInt16(v) => v.to_uint64()
Byte(v) => v.to_uint64()
Int(v) if v >= 0 => v.to_uint64()
String(s) =>
@string.parse_uint64(s) catch {
_ => param_decode_error("UInt64::from_param: invalid UInt64 string")
}
_ =>
param_decode_error("UInt64::from_param: expected UInt64-compatible value")
}
}
///|
pub impl FromParam for Int64 with fn from_param(value) {
match value {
Int64(v) => v
Int(v) => v.to_int64()
Int16(v) => v.to_int64()
UInt16(v) => v.to_int64()
Byte(v) => v.to_int64()
String(s) =>
@string.parse_int64(s) catch {
_ => param_decode_error("Int64::from_param: invalid Int64 string")
}
_ =>
param_decode_error("Int64::from_param: expected Int64-compatible value")
}
}
///|
pub impl FromParam for BigInt with fn from_param(value) {
match value {
BigInt(v) => v
Int(v) => BigInt::from_int(v)
Int64(v) => BigInt::from_int64(v)
String(s) => BigInt::from_string(s)
_ =>
param_decode_error("BigInt::from_param: expected BigInt-compatible value")
}
}
///|
pub impl FromParam for Float with fn from_param(value) {
match value {
Float(v) => v
Double(v) => Float::from_double(v)
Int(v) => Float::from_int(v)
String(s) =>
Float::from_double(@string.parse_double(s)) catch {
_ => param_decode_error("Float::from_param: invalid Float string")
}
_ =>
param_decode_error("Float::from_param: expected Float-compatible value")
}
}
///|
pub impl FromParam for Double with fn from_param(value) {
match value {
Double(v) => v
Float(v) => v.to_double()
Int(v) => v.to_double()
String(s) =>
@string.parse_double(s) catch {
_ => param_decode_error("Double::from_param: invalid Double string")
}
_ =>
param_decode_error("Double::from_param: expected Double-compatible value")
}
}
///|
pub impl FromParam for String with fn from_param(value) {
match value {
String(v) => v
Decimal(v) => v
Uuid(v) => v
Date(v, format) => format_plain_date(v, format)
Time(v, format) => format_plain_time(v, format)
DateTime(v, format) => format_plain_date_time(v, format)
Timestamp(v, format) => format_zoned_date_time(v, format)
_ =>
param_decode_error("String::from_param: expected String-compatible value")
}
}
///|
pub impl FromParam for Bytes with fn from_param(value) {
match value {
Bytes(v) => v
_ => param_decode_error("Bytes::from_param: expected Bytes")
}
}
///|
pub impl FromParam for Json with fn from_param(value) {
match value {
Json(v) => v
_ => value.to_json()
}
}
///|
pub impl FromParam for @time.PlainDate with fn from_param(value) {
match value {
Date(v, _) => v
String(v) =>
@time.PlainDate::from_string(v) catch {
_ => param_decode_error("PlainDate::from_param: invalid date string")
}
_ =>
param_decode_error(
"PlainDate::from_param: expected date-compatible value",
)
}
}
///|
pub impl FromParam for @time.PlainTime with fn from_param(value) {
match value {
Time(v, _) => v
String(v) =>
@time.PlainTime::from_string(v) catch {
_ => param_decode_error("PlainTime::from_param: invalid time string")
}
_ =>
param_decode_error(
"PlainTime::from_param: expected time-compatible value",
)
}
}
///|
pub impl FromParam for @time.PlainDateTime with fn from_param(value) {
match value {
DateTime(v, _) => v
String(v) =>
@time.PlainDateTime::from_string(v) catch {
_ =>
param_decode_error(
"PlainDateTime::from_param: invalid datetime string",
)
}
_ =>
param_decode_error(
"PlainDateTime::from_param: expected datetime-compatible value",
)
}
}
///|
pub impl FromParam for @time.ZonedDateTime with fn from_param(value) {
match value {
Timestamp(v, _) => v
String(v) =>
@time.ZonedDateTime::from_string(v) catch {
_ =>
param_decode_error(
"ZonedDateTime::from_param: invalid timestamp string",
)
}
_ =>
param_decode_error(
"ZonedDateTime::from_param: expected timestamp-compatible value",
)
}
}
///|
pub impl[T : FromParam] FromParam for T? with fn from_param(value) {
match value {
Null => None
_ => Some(try! FromParam::from_param(value))
}
}
///|
fn page_json_int_field(obj : Map[String, Json], key : String) -> Int {
match obj.get(key) {
Some(Number(n, ..)) => n.to_int()
_ => 0
}
}
///|
fn page_json_bool_field(obj : Map[String, Json], key : String) -> Bool {
match obj.get(key) {
Some(True) => true
Some(False) => false
_ => false
}
}
///|
pub impl[T : FromParam] FromParam for Page[T] with fn from_param(value) {
match value {
Object(obj) => {
let content : FixedArray[T] = match obj.get("content") {
Some(Json(Array(items))) => {
let out : Array[T] = []
for item in items {
out.push(try! FromParam::from_param(param_from_json_value(item)))
}
FixedArray::from_array(out)
}
_ => []
}
{
content,
total_elements: match obj.get("total_elements") {
Some(v) => try! FromParam::from_param(v)
None => 0
},
total_pages: match obj.get("total_pages") {
Some(v) => try! FromParam::from_param(v)
None => 0
},
number: match obj.get("number") {
Some(v) => try! FromParam::from_param(v)
None => 0
},
size: match obj.get("size") {
Some(v) => try! FromParam::from_param(v)
None => 0
},
first: match obj.get("first") {
Some(v) => try! FromParam::from_param(v)
None => false
},
last: match obj.get("last") {
Some(v) => try! FromParam::from_param(v)
None => false
},
empty: match obj.get("empty") {
Some(v) => try! FromParam::from_param(v)
None => false
},
}
}
Json(Object(obj)) => {
let content : FixedArray[T] = match obj.get("content") {
Some(Array(items)) => {
let out : Array[T] = []
for item in items {
out.push(try! FromParam::from_param(param_from_json_value(item)))
}
FixedArray::from_array(out)
}
_ => []
}
{
content,
total_elements: page_json_int_field(obj, "total_elements"),
total_pages: page_json_int_field(obj, "total_pages"),
number: page_json_int_field(obj, "number"),
size: page_json_int_field(obj, "size"),
first: page_json_bool_field(obj, "first"),
last: page_json_bool_field(obj, "last"),
empty: page_json_bool_field(obj, "empty"),
}
}
_ => param_decode_error("Page::from_param: expected Json object")
}
}
///|
pub(open) trait ToParam {
fn to_param(Self) -> Param
}
///|
pub fn[T : ToParam] to_param(value : T) -> Param {
value.to_param()
}
///|
pub fn[T : ToJson] enum_to_param(value : T) -> Param raise ParamDecodeError {
match value.to_json() {
String(text) => String(text)
_ =>
param_decode_error(
"enum_to_param: enum value must serialize to JSON string",
)
}
}
///|
pub impl ToParam for Param with fn to_param(self) -> Param {
self
}
///|
pub impl ToParam for Unit with fn to_param(_self) -> Param {
Null
}
///|
pub impl ToParam for Bool with fn to_param(self) -> Param {
Bool(self)
}
///|
pub impl ToParam for Byte with fn to_param(self) -> Param {
Byte(self)
}
///|
pub impl ToParam for Int with fn to_param(self) -> Param {
Int(self)
}
///|
pub impl ToParam for Int16 with fn to_param(self) -> Param {
Int16(self)
}
///|
pub impl ToParam for UInt16 with fn to_param(self) -> Param {
UInt16(self)
}
///|
pub impl ToParam for UInt with fn to_param(self) -> Param {
UInt(self)
}
///|
pub impl ToParam for UInt64 with fn to_param(self) -> Param {
UInt64(self)
}
///|
pub impl ToParam for Int64 with fn to_param(self) -> Param {
Int64(self)
}
///|
pub impl ToParam for BigInt with fn to_param(self) -> Param {
BigInt(self)
}
///|
pub impl ToParam for Double with fn to_param(self) -> Param {
Double(self)
}
///|
pub impl ToParam for Float with fn to_param(self) -> Param {
Float(self)
}
///|
pub impl ToParam for @time.PlainDate with fn to_param(self) -> Param {
Date(self, None)
}
///|
pub impl ToParam for @time.PlainTime with fn to_param(self) -> Param {
Time(self, None)
}
///|
pub impl ToParam for @time.PlainDateTime with fn to_param(self) -> Param {
DateTime(self, None)
}
///|
pub impl ToParam for @time.ZonedDateTime with fn to_param(self) -> Param {
Timestamp(self, None)
}
///|
pub impl ToParam for String with fn to_param(self) -> Param {
String(self)
}
///|
pub impl ToParam for Bytes with fn to_param(self) -> Param {
Bytes(self)
}
///|
pub impl ToParam for Json with fn to_param(self) -> Param {
Json(self)
}
///|
pub impl ToParam for Map[String, Param] with fn to_param(self) -> Param {
Object(self)
}