///|
pub(all) suberror SdmxError {
Invalid(path~ : String, reason~ : String)
Limit(resource~ : String, maximum~ : Int32)
Unsupported(feature~ : String)
Conflict(key~ : String, reason~ : String)
} derive(@debug.Debug)
///|
pub extend SdmxError with @debug.Debug::{to_repr}
///|
pub fn SdmxError::message(self : SdmxError) -> String {
match self {
Invalid(path~, reason~) => "\{path}: \{reason}"
Limit(resource~, maximum~) => "\{resource}: limit \{maximum} exceeded"
Unsupported(feature~) => "unsupported profile: \{feature}"
Conflict(key~, reason~) => "\{key}: \{reason}"
}
}
///|
pub struct Limits {
priv max_text : Int32
priv max_rows : Int32
priv max_columns : Int32
priv max_field : Int32
priv max_codes : Int32
priv max_output : Int32
} derive(Eq, @debug.Debug)
///|
pub extend Limits with Eq::{equal, not_equal}
///|
pub extend Limits with @debug.Debug::{to_repr}
///|
pub fn Limits::new(
max_text~ : Int32 = 16777216,
max_rows~ : Int32 = 100000,
max_columns~ : Int32 = 128,
max_field~ : Int32 = 65536,
max_codes~ : Int32 = 100000,
max_output~ : Int32 = 33554432,
) -> Limits raise SdmxError {
if max_text < 1 ||
max_text > 67108864 ||
max_rows < 1 ||
max_rows > 1000000 ||
max_columns < 1 ||
max_columns > 1024 ||
max_field < 1 ||
max_field > 1048576 ||
max_codes < 1 ||
max_codes > 1000000 ||
max_output < 1 ||
max_output > 67108864 {
raise Invalid(path="limits", reason="outside supported positive bounds")
}
{ max_text, max_rows, max_columns, max_field, max_codes, max_output, }
}
///|
pub fn Limits::standard() -> Limits {
{
max_text: 16777216,
max_rows: 100000,
max_columns: 128,
max_field: 65536,
max_codes: 100000,
max_output: 33554432,
}
}
///|
pub fn Limits::max_rows(self : Limits) -> Int32 {
self.max_rows
}
///|
fn check_text(
text : String,
limits : Limits,
path : String,
) -> Unit raise SdmxError {
if text.length() > limits.max_text {
raise Limit(resource=path, maximum=limits.max_text)
}
}
///|
fn check_field(
text : String,
limits : Limits,
path : String,
) -> Unit raise SdmxError {
if text.length() > limits.max_field {
raise Limit(resource=path, maximum=limits.max_field)
}
for ch in text {
if ch.to_int() == 0 {
raise Invalid(path~, reason="NUL is not permitted")
}
}
}
///|
fn identifier(value : String, path : String) -> Unit raise SdmxError {
if value.is_empty() || value.length() > 128 {
raise Invalid(path~, reason="expected identifier of length 1..128")
}
for ch in value {
let n = ch.to_int()
if !((n >= 65 && n <= 90) ||
(n >= 97 && n <= 122) ||
(n >= 48 && n <= 57) ||
ch == '_' ||
ch == '-' ||
ch == '.') {
raise Invalid(path~, reason="invalid identifier character")
}
}
}
///|
fn unique_names(values : Array[String], path : String) -> Unit raise SdmxError {
let seen : Map[String, Bool] = Map([])
for value in values {
identifier(value, path)
if seen.contains(value) {
raise Invalid(path~, reason="duplicate identifier: \{value}")
}
seen[value] = true
}
}
///|
// Length framing avoids collisions when dimension values contain separators.
fn framed_key(values : Array[String]) -> String {
let out = StringBuilder()
for value in values {
out.write_string(value.length().to_string())
out.write_string(":")
out.write_string(value)
}
out.to_string()
}