///|
pub struct ZoneSelection {
document_value : ZoneDocument
selected_count_value : Int
excluded_count_value : Int
} derive(Eq, Debug)
///|
pub struct ZoneTtlRewrite {
document_value : ZoneDocument
changed_count_value : Int
unchanged_count_value : Int
} derive(Eq, Debug)
///|
fn transformation_error(message : String) -> ZoneError {
ZoneError::new(IntegrityViolation, message, SourceSpan::point(0, 0))
}
///|
fn transformed_document(
source : ZoneDocument,
records : Array[ZoneRecord],
) -> ZoneDocument {
{
record_values: records,
origin_value: source.origin(),
default_ttl_value: source.default_ttl(),
directive_count_value: source.directive_count(),
}
}
///|
fn domain_is_at_or_below(owner : DomainName, suffix : DomainName) -> Bool {
if !owner.is_absolute() || !suffix.is_absolute() {
return false
}
let owner_labels = owner.labels()
let suffix_labels = suffix.labels()
if owner_labels.length() < suffix_labels.length() {
return false
}
let offset = owner_labels.length() - suffix_labels.length()
for index, label in suffix_labels {
if owner_labels[offset + index] != label {
return false
}
}
true
}
///|
fn type_filter_contains(types : Array[String], record_type : String) -> Bool {
if types.length() == 0 {
return true
}
for item in types {
if item == record_type {
return true
}
}
false
}
///|
fn normalized_types_contain(
types : Array[String],
record_type : String,
) -> Bool {
for item in types {
if item == record_type {
return true
}
}
false
}
///|
fn normalize_type_filter(
types : Array[String],
) -> Result[Array[String], ZoneError] {
let normalized : Array[String] = []
for value in types {
if !valid_generic_type(value) {
return Err(
transformation_error(
"record type filters must use visible DNS type characters",
),
)
}
let expected = uppercase_ascii(value)
if !normalized_types_contain(normalized, expected) {
normalized.push(expected)
}
}
Ok(normalized)
}
///|
/// Select records by an optional absolute owner subtree and type allow-list.
pub fn select_zone_records(
zone : ZoneDocument,
owner_subtree? : DomainName? = None,
record_types? : Array[String] = [],
) -> Result[ZoneSelection, ZoneError] {
match owner_subtree {
Some(value) if !value.is_absolute() =>
return Err(
transformation_error("record selection subtree must be absolute"),
)
_ => ()
}
let types = match normalize_type_filter(record_types) {
Ok(value) => value
Err(error) => return Err(error)
}
let selected : Array[ZoneRecord] = []
let mut excluded = 0
for record in zone.records() {
let owner_matches = match owner_subtree {
Some(suffix) => domain_is_at_or_below(record.owner(), suffix)
None => true
}
if owner_matches && type_filter_contains(types, record.record_type()) {
selected.push(record)
} else {
excluded = excluded + 1
}
}
Ok({
document_value: transformed_document(zone, selected),
selected_count_value: selected.length(),
excluded_count_value: excluded,
})
}
///|
fn rewrite_record_ttl(record : ZoneRecord, ttl : Ttl) -> ZoneRecord {
{
owner_value: record.owner(),
ttl_value: ttl,
class_value: record.record_class(),
type_value: record.record_type(),
data_value: record.data(),
explicit_ttl_value: true,
span_value: record.span(),
}
}
///|
fn bounded_ttl(ttl : Ttl, minimum : Ttl?, maximum : Ttl?) -> Ttl {
match minimum {
Some(value) if ttl.seconds() < value.seconds() => return value
_ => ()
}
match maximum {
Some(value) if ttl.seconds() > value.seconds() => return value
_ => ()
}
ttl
}
///|
/// Clamp record TTLs while preserving owner, class, type and record data.
pub fn clamp_zone_ttls(
zone : ZoneDocument,
minimum? : Ttl? = None,
maximum? : Ttl? = None,
) -> Result[ZoneTtlRewrite, ZoneError] {
match (minimum, maximum) {
(Some(lower), Some(upper)) if lower.seconds() > upper.seconds() =>
return Err(transformation_error("minimum TTL cannot exceed maximum TTL"))
_ => ()
}
let records : Array[ZoneRecord] = []
let mut changed = 0
let mut unchanged = 0
for record in zone.records() {
let ttl = bounded_ttl(record.ttl(), minimum, maximum)
if ttl == record.ttl() {
unchanged = unchanged + 1
records.push(record)
} else {
changed = changed + 1
records.push(rewrite_record_ttl(record, ttl))
}
}
Ok({
document_value: transformed_document(zone, records),
changed_count_value: changed,
unchanged_count_value: unchanged,
})
}
///|
pub fn ZoneSelection::document(self : ZoneSelection) -> ZoneDocument {
self.document_value
}
///|
pub fn ZoneSelection::selected_count(self : ZoneSelection) -> Int {
self.selected_count_value
}
///|
pub fn ZoneSelection::excluded_count(self : ZoneSelection) -> Int {
self.excluded_count_value
}
///|
pub fn ZoneTtlRewrite::document(self : ZoneTtlRewrite) -> ZoneDocument {
self.document_value
}
///|
pub fn ZoneTtlRewrite::changed_count(self : ZoneTtlRewrite) -> Int {
self.changed_count_value
}
///|
pub fn ZoneTtlRewrite::unchanged_count(self : ZoneTtlRewrite) -> Int {
self.unchanged_count_value
}