///|
pub struct ZoneBatchInput {
label_value : String
text_value : String
initial_origin_value : DomainName?
initial_ttl_value : Ttl?
} derive(Eq, Debug)
///|
pub struct ZoneBatchItem {
label_value : String
document_value : ZoneDocument?
validation_value : ZoneValidation?
statistics_value : ZoneStatistics?
parse_error_value : ZoneError?
} derive(Eq, Debug)
///|
pub struct ZoneBatch {
item_values : Array[ZoneBatchItem]
accepted_count_value : Int
rejected_count_value : Int
parse_failed_count_value : Int
total_record_count_value : Int
} derive(Eq, Debug)
///|
fn batch_configuration_error(message : String) -> ZoneError {
ZoneError::new(IntegrityViolation, message, SourceSpan::point(0, 0))
}
///|
fn batch_label_is_valid(label : String) -> Bool {
if label.length() == 0 || label.length() > 128 {
return false
}
for index = 0; index < label.length(); index = index + 1 {
let code = label[index].to_int()
if code < 33 || code > 126 {
return false
}
}
true
}
///|
pub fn ZoneBatchInput::new(
label : String,
text : String,
initial_origin? : DomainName? = None,
initial_ttl? : Ttl? = None,
) -> Result[ZoneBatchInput, ZoneError] {
if !batch_label_is_valid(label) {
return Err(
batch_configuration_error(
"batch labels must contain 1 to 128 visible ASCII characters",
),
)
}
match initial_origin {
Some(value) if !value.is_absolute() =>
return Err(
batch_configuration_error(
"batch input initial origin must be an absolute domain name",
),
)
_ => ()
}
Ok({
label_value: label,
text_value: text,
initial_origin_value: initial_origin,
initial_ttl_value: initial_ttl,
})
}
///|
pub fn ZoneBatchInput::label(self : ZoneBatchInput) -> String {
self.label_value
}
///|
pub fn ZoneBatchInput::text(self : ZoneBatchInput) -> String {
self.text_value
}
///|
pub fn ZoneBatchInput::initial_origin(self : ZoneBatchInput) -> DomainName? {
self.initial_origin_value
}
///|
pub fn ZoneBatchInput::initial_ttl(self : ZoneBatchInput) -> Ttl? {
self.initial_ttl_value
}
///|
fn batch_contains_label(items : Array[ZoneBatchInput], label : String) -> Bool {
for item in items {
if item.label() == label {
return true
}
}
false
}
///|
fn validate_batch_inputs(
inputs : Array[ZoneBatchInput],
max_items : Int,
max_total_text_units : Int,
) -> Result[Unit, ZoneError] {
if max_items < 0 || max_total_text_units < 0 {
return Err(batch_configuration_error("batch limits cannot be negative"))
}
if inputs.length() > max_items {
return Err(
batch_configuration_error("batch exceeds the configured item limit"),
)
}
let seen : Array[ZoneBatchInput] = []
let mut text_units = 0
for input in inputs {
if batch_contains_label(seen, input.label()) {
return Err(batch_configuration_error("batch input labels must be unique"))
}
seen.push(input)
text_units = text_units + input.text().length()
if text_units > max_total_text_units {
return Err(
batch_configuration_error(
"batch exceeds the configured total text size limit",
),
)
}
}
Ok(())
}
///|
/// Parse and validate independent in-memory zone texts without fail-fast loss.
pub fn process_zone_batch(
inputs : Array[ZoneBatchInput],
policy : ZonePolicy,
max_items? : Int = 100,
max_total_text_units? : Int = 1000000,
max_tokens_per_item? : Int = 100000,
max_token_bytes? : Int = 65535,
) -> Result[ZoneBatch, ZoneError] {
match validate_batch_inputs(inputs, max_items, max_total_text_units) {
Ok(_) => ()
Err(error) => return Err(error)
}
if max_tokens_per_item < 0 || max_token_bytes < 0 {
return Err(
batch_configuration_error("batch parser limits cannot be negative"),
)
}
let items : Array[ZoneBatchItem] = []
let mut accepted = 0
let mut rejected = 0
let mut parse_failed = 0
let mut total_records = 0
for input in inputs {
match
parse_zone(
input.text(),
initial_origin=input.initial_origin(),
initial_ttl=input.initial_ttl(),
max_tokens=max_tokens_per_item,
max_token_bytes~,
) {
Err(error) => {
parse_failed = parse_failed + 1
items.push({
label_value: input.label(),
document_value: None,
validation_value: None,
statistics_value: None,
parse_error_value: Some(error),
})
}
Ok(document) => {
let validation = match validate_zone(document, policy) {
Ok(value) => value
Err(error) => return Err(error)
}
let statistics = calculate_zone_statistics(document)
total_records = total_records + statistics.record_count()
if validation.accepted() {
accepted = accepted + 1
} else {
rejected = rejected + 1
}
items.push({
label_value: input.label(),
document_value: Some(document),
validation_value: Some(validation),
statistics_value: Some(statistics),
parse_error_value: None,
})
}
}
}
Ok({
item_values: items,
accepted_count_value: accepted,
rejected_count_value: rejected,
parse_failed_count_value: parse_failed,
total_record_count_value: total_records,
})
}
///|
pub fn ZoneBatchItem::label(self : ZoneBatchItem) -> String {
self.label_value
}
///|
pub fn ZoneBatchItem::document(self : ZoneBatchItem) -> ZoneDocument? {
self.document_value
}
///|
pub fn ZoneBatchItem::validation(self : ZoneBatchItem) -> ZoneValidation? {
self.validation_value
}
///|
pub fn ZoneBatchItem::statistics(self : ZoneBatchItem) -> ZoneStatistics? {
self.statistics_value
}
///|
pub fn ZoneBatchItem::parse_error(self : ZoneBatchItem) -> ZoneError? {
self.parse_error_value
}
///|
pub fn ZoneBatchItem::parsed(self : ZoneBatchItem) -> Bool {
self.document_value is Some(_)
}
///|
pub fn ZoneBatchItem::accepted(self : ZoneBatchItem) -> Bool {
match self.validation_value {
Some(value) => value.accepted()
None => false
}
}
///|
pub fn ZoneBatch::items(self : ZoneBatch) -> Array[ZoneBatchItem] {
self.item_values.copy()
}
///|
pub fn ZoneBatch::accepted_count(self : ZoneBatch) -> Int {
self.accepted_count_value
}
///|
pub fn ZoneBatch::rejected_count(self : ZoneBatch) -> Int {
self.rejected_count_value
}
///|
pub fn ZoneBatch::parse_failed_count(self : ZoneBatch) -> Int {
self.parse_failed_count_value
}
///|
pub fn ZoneBatch::total_record_count(self : ZoneBatch) -> Int {
self.total_record_count_value
}