///|
pub struct ZoneRecordSet {
owner_value : DomainName
type_value : String
record_values : Array[ZoneRecord]
} derive(Eq, Debug)
///|
pub struct ZoneRecordSets {
set_values : Array[ZoneRecordSet]
record_count_value : Int
duplicate_count_value : Int
} derive(Eq, Debug)
///|
fn record_set_error(message : String) -> ZoneError {
ZoneError::new(IntegrityViolation, message, SourceSpan::point(0, 0))
}
///|
fn record_set_matches(set : ZoneRecordSet, record : ZoneRecord) -> Bool {
set.owner() == record.owner() && set.record_type() == record.record_type()
}
///|
fn record_set_index(sets : Array[ZoneRecordSet], record : ZoneRecord) -> Int {
for index, set in sets {
if record_set_matches(set, record) {
return index
}
}
-1
}
///|
fn record_set_has_exact(
records : Array[ZoneRecord],
candidate : ZoneRecord,
) -> Bool {
for record in records {
if records_exactly_equal(record, candidate) {
return true
}
}
false
}
///|
fn record_set_sort_after(left : ZoneRecordSet, right : ZoneRecordSet) -> Bool {
let left_owner = left.owner().to_text()
let right_owner = right.owner().to_text()
if left_owner == right_owner {
return statistics_text_after(left.record_type(), right.record_type())
}
statistics_text_after(left_owner, right_owner)
}
///|
fn sort_record_sets(sets : Array[ZoneRecordSet]) -> Unit {
for index = 1; index < sets.length(); index = index + 1 {
let current = sets[index]
let mut position = index
while position > 0 && record_set_sort_after(sets[position - 1], current) {
sets[position] = sets[position - 1]
position = position - 1
}
sets[position] = current
}
}
///|
/// Group records by owner and type and count exact duplicate evidence.
pub fn build_zone_record_sets(
zone : ZoneDocument,
max_sets? : Int = 100000,
) -> Result[ZoneRecordSets, ZoneError] {
if max_sets < 0 {
return Err(record_set_error("maximum record set count cannot be negative"))
}
let sets : Array[ZoneRecordSet] = []
let mut duplicates = 0
let mut record_count = 0
for record in zone.records() {
record_count = record_count + 1
let index = record_set_index(sets, record)
if index < 0 {
if sets.length() >= max_sets {
return Err(
record_set_error("zone exceeds the configured record set limit"),
)
}
sets.push({
owner_value: record.owner(),
type_value: record.record_type(),
record_values: [record],
})
} else {
let current = sets[index]
if record_set_has_exact(current.records(), record) {
duplicates = duplicates + 1
}
let records = current.records()
records.push(record)
sets[index] = {
owner_value: current.owner(),
type_value: current.record_type(),
record_values: records,
}
}
}
sort_record_sets(sets)
Ok({
set_values: sets,
record_count_value: record_count,
duplicate_count_value: duplicates,
})
}
///|
pub fn ZoneRecordSet::owner(self : ZoneRecordSet) -> DomainName {
self.owner_value
}
///|
pub fn ZoneRecordSet::record_type(self : ZoneRecordSet) -> String {
self.type_value
}
///|
pub fn ZoneRecordSet::records(self : ZoneRecordSet) -> Array[ZoneRecord] {
self.record_values.copy()
}
///|
pub fn ZoneRecordSet::count(self : ZoneRecordSet) -> Int {
self.record_values.length()
}
///|
pub fn ZoneRecordSet::duplicate_count(self : ZoneRecordSet) -> Int {
let unique : Array[ZoneRecord] = []
let mut duplicates = 0
for record in self.record_values {
if record_set_has_exact(unique, record) {
duplicates = duplicates + 1
} else {
unique.push(record)
}
}
duplicates
}
///|
pub fn ZoneRecordSet::unique_record_count(self : ZoneRecordSet) -> Int {
self.count() - self.duplicate_count()
}
///|
pub fn ZoneRecordSet::unique_records(self : ZoneRecordSet) -> Array[ZoneRecord] {
let unique : Array[ZoneRecord] = []
for record in self.record_values {
if !record_set_has_exact(unique, record) {
unique.push(record)
}
}
unique
}
///|
pub fn ZoneRecordSet::duplicate_records(
self : ZoneRecordSet,
) -> Array[ZoneRecord] {
let unique : Array[ZoneRecord] = []
let duplicates : Array[ZoneRecord] = []
for record in self.record_values {
if record_set_has_exact(unique, record) {
duplicates.push(record)
} else {
unique.push(record)
}
}
duplicates
}
///|
pub fn ZoneRecordSets::sets(self : ZoneRecordSets) -> Array[ZoneRecordSet] {
self.set_values.copy()
}
///|
pub fn ZoneRecordSets::record_count(self : ZoneRecordSets) -> Int {
self.record_count_value
}
///|
pub fn ZoneRecordSets::set_count(self : ZoneRecordSets) -> Int {
self.set_values.length()
}
///|
pub fn ZoneRecordSets::duplicate_count(self : ZoneRecordSets) -> Int {
self.duplicate_count_value
}
///|
pub fn ZoneRecordSets::unique_record_count(self : ZoneRecordSets) -> Int {
self.record_count_value - self.duplicate_count_value
}
///|
pub fn ZoneRecordSets::has_duplicates(self : ZoneRecordSets) -> Bool {
self.duplicate_count_value > 0
}
///|
pub fn ZoneRecordSets::singleton_set_count(self : ZoneRecordSets) -> Int {
let mut count = 0
for set in self.set_values {
if set.count() == 1 {
count = count + 1
}
}
count
}
///|
pub fn ZoneRecordSets::multi_record_set_count(self : ZoneRecordSets) -> Int {
self.set_count() - self.singleton_set_count()
}
///|
pub fn ZoneRecordSets::largest_set_size(self : ZoneRecordSets) -> Int {
let mut largest = 0
for set in self.set_values {
if set.count() > largest {
largest = set.count()
}
}
largest
}
///|
pub fn ZoneRecordSets::lookup(
self : ZoneRecordSets,
owner : DomainName,
record_type : String,
) -> ZoneRecordSet? {
let expected = uppercase_ascii(record_type)
for set in self.set_values {
if set.owner() == owner && set.record_type() == expected {
return Some(set)
}
}
None
}
///|
pub fn ZoneRecordSets::for_owner(
self : ZoneRecordSets,
owner : DomainName,
) -> Array[ZoneRecordSet] {
let result : Array[ZoneRecordSet] = []
for set in self.set_values {
if set.owner() == owner {
result.push(set)
}
}
result
}
///|
pub fn ZoneRecordSets::for_type(
self : ZoneRecordSets,
record_type : String,
) -> Array[ZoneRecordSet] {
let expected = uppercase_ascii(record_type)
let result : Array[ZoneRecordSet] = []
for set in self.set_values {
if set.record_type() == expected {
result.push(set)
}
}
result
}
///|
pub fn render_record_sets_text(sets : ZoneRecordSets) -> String {
let mut result = "DNS zone record sets" +
"\nrecords: " +
sets.record_count().to_string() +
"\nsets: " +
sets.set_count().to_string() +
"\nunique records: " +
sets.unique_record_count().to_string() +
"\nduplicates: " +
sets.duplicate_count().to_string() +
"\nsingleton sets: " +
sets.singleton_set_count().to_string() +
"\nmulti-record sets: " +
sets.multi_record_set_count().to_string() +
"\nlargest set: " +
sets.largest_set_size().to_string()
for set in sets.sets() {
result = result +
"\n" +
set.owner().to_text() +
" " +
set.record_type() +
" count=" +
set.count().to_string() +
" unique=" +
set.unique_record_count().to_string()
}
result
}
///|
pub fn render_record_sets_json(sets : ZoneRecordSets) -> String {
let values : Array[Json] = []
for set in sets.sets() {
let records : Array[Json] = []
for record in set.records() {
records.push(Json::string(record.to_text()))
}
let duplicates : Array[Json] = []
for record in set.duplicate_records() {
duplicates.push(Json::string(record.to_text()))
}
values.push(
Json::object({
"owner": Json::string(set.owner().to_text()),
"record_type": Json::string(set.record_type()),
"count": Json::number(set.count().to_double()),
"unique_count": Json::number(set.unique_record_count().to_double()),
"duplicate_count": Json::number(set.duplicate_count().to_double()),
"records": Json::array(records),
"duplicates": Json::array(duplicates),
}),
)
}
Json::object({
"type": Json::string("dns_zone_record_sets"),
"record_count": Json::number(sets.record_count().to_double()),
"set_count": Json::number(sets.set_count().to_double()),
"unique_record_count": Json::number(sets.unique_record_count().to_double()),
"duplicate_count": Json::number(sets.duplicate_count().to_double()),
"has_duplicates": Json::boolean(sets.has_duplicates()),
"singleton_set_count": Json::number(sets.singleton_set_count().to_double()),
"multi_record_set_count": Json::number(
sets.multi_record_set_count().to_double(),
),
"largest_set_size": Json::number(sets.largest_set_size().to_double()),
"record_sets": Json::array(values),
}).stringify()
}