///|
fn[T] query_string_invalid_value(
field : String,
value : String,
start_offset : Int,
end_offset : Int,
) -> T raise QueryStringError {
query_string_fail(InvalidFieldValue(field, value), start_offset, end_offset)
}
///|
fn parse_query_u64(
field : String,
text : String,
start_offset : Int,
end_offset : Int,
) -> UInt64 raise QueryStringError {
let characters = text.to_array()
guard characters.length() > 0 else {
query_string_invalid_value(field, text, start_offset, end_offset)
}
let mut value = 0UL
for character in characters {
let code = character.to_int()
guard code >= 0x30 && code <= 0x39 else {
query_string_invalid_value(field, text, start_offset, end_offset)
}
let digit = UInt64::extend_uint((code - 0x30).reinterpret_as_uint())
guard value <= (18446744073709551615UL - digit) / 10UL else {
query_string_invalid_value(field, text, start_offset, end_offset)
}
value = value * 10UL + digit
}
value
}
///|
fn parse_query_i64(
field : String,
text : String,
start_offset : Int,
end_offset : Int,
) -> Int64 raise QueryStringError {
let characters = text.to_array()
guard characters.length() > 0 else {
query_string_invalid_value(field, text, start_offset, end_offset)
}
let negative = characters[0] == '-'
let positive = characters[0] == '+'
let start = if negative || positive { 1 } else { 0 }
guard start < characters.length() else {
query_string_invalid_value(field, text, start_offset, end_offset)
}
let mut magnitude = 0UL
let maximum = if negative {
9223372036854775808UL
} else {
9223372036854775807UL
}
for index in start..= 0x30 && code <= 0x39 else {
query_string_invalid_value(field, text, start_offset, end_offset)
}
let digit = UInt64::extend_uint((code - 0x30).reinterpret_as_uint())
guard magnitude <= (maximum - digit) / 10UL else {
query_string_invalid_value(field, text, start_offset, end_offset)
}
magnitude = magnitude * 10UL + digit
}
if negative {
if magnitude == 9223372036854775808UL {
-9223372036854775808L
} else {
-magnitude.reinterpret_as_int64()
}
} else {
magnitude.reinterpret_as_int64()
}
}
///|
fn parse_query_f64(
field : String,
text : String,
start_offset : Int,
end_offset : Int,
) -> Double raise QueryStringError {
let characters = text.to_array()
guard characters.length() > 0 else {
query_string_invalid_value(field, text, start_offset, end_offset)
}
let negative = characters[0] == '-'
let positive = characters[0] == '+'
let start = if negative || positive { 1 } else { 0 }
let mut value = 0.0
let mut divisor = 0.0
let mut saw_digit = false
let mut saw_dot = false
for index in start..= 0x30 && code <= 0x39 else {
query_string_invalid_value(field, text, start_offset, end_offset)
}
saw_digit = true
let digit = (code - 0x30).to_double()
if saw_dot {
value += digit / divisor
divisor *= 10.0
} else {
value = value * 10.0 + digit
}
}
guard saw_digit else {
query_string_invalid_value(field, text, start_offset, end_offset)
}
if negative {
-value
} else {
value
}
}
///|
fn iso_date_epoch_millis(text : String) -> Int64? {
let chars = text.to_array()
if chars.length() != 10 || chars[4] != '-' || chars[7] != '-' {
return None
}
let positions = [0, 1, 2, 3, 5, 6, 8, 9]
let numbers : Array[Int] = []
for index in positions {
let code = chars[index].to_int()
if code < 0x30 || code > 0x39 {
return None
}
numbers.push(code - 0x30)
}
let year = numbers[0] * 1000 + numbers[1] * 100 + numbers[2] * 10 + numbers[3]
let month = numbers[4] * 10 + numbers[5]
let day = numbers[6] * 10 + numbers[7]
if year < 1970 || month < 1 || month > 12 {
return None
}
let leap = year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)
let days_in_month = match month {
2 => if leap { 29 } else { 28 }
4 | 6 | 9 | 11 => 30
_ => 31
}
if day < 1 || day > days_in_month {
return None
}
let adjusted_year = if month <= 2 { year - 1 } else { year }
let era = adjusted_year / 400
let year_of_era = adjusted_year - era * 400
let adjusted_month = month + (if month > 2 { -3 } else { 9 })
let day_of_year = (153 * adjusted_month + 2) / 5 + day - 1
let day_of_era = year_of_era * 365 +
year_of_era / 4 -
year_of_era / 100 +
day_of_year
let days = era * 146097 + day_of_era - 719468
Some(days.to_int64() * 86400000L)
}
///|
fn QueryStringCompiler::typed_value(
self : QueryStringCompiler,
field_id : FieldId,
text : String,
start_offset : Int,
end_offset : Int,
) -> FieldValue raise QueryStringError {
let field_name = match self.schema.field_name(field_id) {
Some(name) => name
None =>
query_string_fail(
InvalidDefaultField(field_id.value),
start_offset,
end_offset,
)
}
match self.schema.field_type(field_id) {
Some(@schema.Keyword) => @core.Keyword(text)
Some(@schema.I64) =>
@core.I64(parse_query_i64(field_name, text, start_offset, end_offset))
Some(@schema.U64) =>
@core.U64(parse_query_u64(field_name, text, start_offset, end_offset))
Some(@schema.F64) =>
@core.F64(parse_query_f64(field_name, text, start_offset, end_offset))
Some(@schema.Bool) =>
match text {
"true" => @core.Bool(true)
"false" => @core.Bool(false)
_ =>
query_string_invalid_value(field_name, text, start_offset, end_offset)
}
Some(@schema.Date) =>
match iso_date_epoch_millis(text) {
Some(value) => @core.Date(value)
None =>
@core.Date(
parse_query_i64(field_name, text, start_offset, end_offset),
)
}
Some(@schema.Bytes) => @core.Bytes(@utf8.encode(text))
Some(@schema.Text) | None =>
query_string_invalid_value(field_name, text, start_offset, end_offset)
}
}