///|
/// Return a query with no evidence constraints or page limit.
pub fn default_roman_index_query() -> RomanIndexQuery {
{
document_id: None,
source_text: None,
normalized_text: None,
canonical_text: None,
mode: None,
min_value: None,
max_value: None,
used_unicode: None,
normalized_input: None,
offset: 0,
limit: None,
}
}
///|
fn validate_roman_index_query(
query : RomanIndexQuery,
) -> Result[Unit, RomanIndexQueryError] {
match (query.min_value, query.max_value) {
(Some(minimum), Some(maximum)) if minimum > maximum =>
return Err(IndexQueryMinimumAboveMaximum(minimum, maximum))
_ => ()
}
if query.offset < 0 {
return Err(NegativeIndexQueryOffset(query.offset))
}
match query.limit {
Some(limit) if limit <= 0 => Err(NonPositiveIndexQueryLimit(limit))
_ => Ok(())
}
}
///|
fn index_entry_matches_document(
entry : RomanIndexEntry,
expected : String?,
) -> Bool {
match expected {
Some(document_id) => entry.document_id == document_id
None => true
}
}
///|
fn index_entry_matches_source(
entry : RomanIndexEntry,
expected : String?,
) -> Bool {
match expected {
Some(source_text) => entry.source_text == source_text
None => true
}
}
///|
fn index_entry_matches_normalized(
entry : RomanIndexEntry,
expected : String?,
) -> Bool {
match expected {
Some(normalized_text) => entry.normalized_text == normalized_text
None => true
}
}
///|
fn index_entry_matches_canonical(
entry : RomanIndexEntry,
expected : String?,
) -> Bool {
match expected {
Some(canonical_text) => entry.canonical == canonical_text
None => true
}
}
///|
fn index_entry_matches_mode(
entry : RomanIndexEntry,
expected : RomanMode?,
) -> Bool {
match expected {
Some(mode) => entry.mode == mode
None => true
}
}
///|
fn index_entry_matches_minimum(entry : RomanIndexEntry, minimum : Int?) -> Bool {
match minimum {
Some(value) => entry.value >= value
None => true
}
}
///|
fn index_entry_matches_maximum(entry : RomanIndexEntry, maximum : Int?) -> Bool {
match maximum {
Some(value) => entry.value <= value
None => true
}
}
///|
fn index_entry_matches_unicode(
entry : RomanIndexEntry,
expected : Bool?,
) -> Bool {
match expected {
Some(used_unicode) => entry.used_unicode_compatibility == used_unicode
None => true
}
}
///|
fn index_entry_matches_normalization(
entry : RomanIndexEntry,
expected : Bool?,
) -> Bool {
match expected {
Some(normalized_input) => entry.normalized_input == normalized_input
None => true
}
}
///|
fn roman_index_entry_matches_query(
entry : RomanIndexEntry,
query : RomanIndexQuery,
) -> Bool {
index_entry_matches_document(entry, query.document_id) &&
index_entry_matches_source(entry, query.source_text) &&
index_entry_matches_normalized(entry, query.normalized_text) &&
index_entry_matches_canonical(entry, query.canonical_text) &&
index_entry_matches_mode(entry, query.mode) &&
index_entry_matches_minimum(entry, query.min_value) &&
index_entry_matches_maximum(entry, query.max_value) &&
index_entry_matches_unicode(entry, query.used_unicode) &&
index_entry_matches_normalization(entry, query.normalized_input)
}
///|
fn index_query_page_has_capacity(page_length : Int, limit : Int?) -> Bool {
match limit {
Some(maximum) => page_length < maximum
None => true
}
}
///|
/// Filter before paginating and report the complete filtered count.
pub fn query_roman_index_page(
index : RomanDocumentIndex,
query : RomanIndexQuery,
) -> Result[RomanIndexQueryPage, RomanIndexQueryError] {
match validate_roman_index_query(query) {
Err(error) => return Err(error)
Ok(_) => ()
}
let entries : Array[RomanIndexEntry] = []
let mut total_matches = 0L
let mut matched_position = 0
for entry in index.entries {
if roman_index_entry_matches_query(entry, query) {
if matched_position >= query.offset &&
index_query_page_has_capacity(entries.length(), query.limit) {
entries.push(entry)
}
matched_position = matched_position + 1
total_matches = total_matches + 1L
}
}
Ok({ entries, total_matches, offset: query.offset, limit: query.limit })
}
///|
/// Filter and paginate entries while preserving complete index order.
pub fn query_roman_index(
index : RomanDocumentIndex,
query : RomanIndexQuery,
) -> Result[Array[RomanIndexEntry], RomanIndexQueryError] {
match query_roman_index_page(index, query) {
Ok(page) => Ok(page.entries)
Err(error) => Err(error)
}
}
///|
/// Count all filtered entries without applying query pagination.
pub fn count_roman_index_matches(
index : RomanDocumentIndex,
query : RomanIndexQuery,
) -> Result[Int64, RomanIndexQueryError] {
match validate_roman_index_query(query) {
Err(error) => return Err(error)
Ok(_) => ()
}
match query_roman_index_page(index, { ..query, offset: 0, limit: None }) {
Ok(page) => Ok(page.total_matches)
Err(error) => Err(error)
}
}
///|
/// Find an entry by its stable global ordinal.
pub fn find_roman_index_entry_by_ordinal(
index : RomanDocumentIndex,
ordinal : Int,
) -> RomanIndexEntry? {
for entry in index.entries {
if entry.ordinal == ordinal {
return Some(entry)
}
}
None
}
///|
/// Find retained document metadata by exact ID.
pub fn find_roman_index_document(
index : RomanDocumentIndex,
document_id : String,
) -> RomanIndexDocumentMetadata? {
for document in index.documents {
if document.id == document_id {
return Some(document)
}
}
None
}
///|
/// Return all entries for an exact document ID in source order.
pub fn roman_index_entries_for_document(
index : RomanDocumentIndex,
document_id : String,
) -> Array[RomanIndexEntry] {
let entries : Array[RomanIndexEntry] = []
for entry in index.entries {
if entry.document_id == document_id {
entries.push(entry)
}
}
entries
}
///|
/// Return retained rejection details for one document in source order.
pub fn roman_index_rejections_for_document(
index : RomanDocumentIndex,
document_id : String,
) -> Array[RomanIndexRejectionSummary] {
let rejections : Array[RomanIndexRejectionSummary] = []
for rejection in index.rejections {
if rejection.document_id == document_id {
rejections.push(rejection)
}
}
rejections
}
///|
/// Return the first entry having an exact numeric value.
pub fn first_roman_index_entry_for_value(
index : RomanDocumentIndex,
value : Int,
) -> RomanIndexEntry? {
for entry in index.entries {
if entry.value == value {
return Some(entry)
}
}
None
}
///|
/// Return all entries having an exact numeric value.
pub fn roman_index_entries_for_value(
index : RomanDocumentIndex,
value : Int,
) -> Array[RomanIndexEntry] {
let entries : Array[RomanIndexEntry] = []
for entry in index.entries {
if entry.value == value {
entries.push(entry)
}
}
entries
}
///|
/// Return entries inside an inclusive numeric range.
pub fn roman_index_entries_in_value_range(
index : RomanDocumentIndex,
minimum : Int,
maximum : Int,
) -> Result[Array[RomanIndexEntry], RomanIndexQueryError] {
query_roman_index(index, {
..default_roman_index_query(),
min_value: Some(minimum),
max_value: Some(maximum),
})
}
///|
/// Return entries having exact source spelling.
pub fn roman_index_entries_for_source_text(
index : RomanDocumentIndex,
source_text : String,
) -> Array[RomanIndexEntry] {
let entries : Array[RomanIndexEntry] = []
for entry in index.entries {
if entry.source_text == source_text {
entries.push(entry)
}
}
entries
}
///|
/// Return entries having exact normalized spelling.
pub fn roman_index_entries_for_normalized_text(
index : RomanDocumentIndex,
normalized_text : String,
) -> Array[RomanIndexEntry] {
let entries : Array[RomanIndexEntry] = []
for entry in index.entries {
if entry.normalized_text == normalized_text {
entries.push(entry)
}
}
entries
}
///|
/// Return entries having exact profile-canonical spelling.
pub fn roman_index_entries_for_canonical_text(
index : RomanDocumentIndex,
canonical_text : String,
) -> Array[RomanIndexEntry] {
let entries : Array[RomanIndexEntry] = []
for entry in index.entries {
if entry.canonical == canonical_text {
entries.push(entry)
}
}
entries
}
///|
/// Group equal values in order of the first matching entry.
pub fn group_roman_index_entries_by_value(
index : RomanDocumentIndex,
) -> Array[RomanIndexValueGroup] {
let groups : Array[RomanIndexValueGroup] = []
for entry in index.entries {
let mut group_index = -1
for position = 0; position < groups.length(); position = position + 1 {
if groups[position].value == entry.value {
group_index = position
break
}
}
if group_index < 0 {
groups.push({ value: entry.value, entries: [entry] })
} else {
groups[group_index].entries.push(entry)
}
}
groups
}
///|
/// Group equal canonical spellings in first occurrence order.
pub fn group_roman_index_entries_by_canonical(
index : RomanDocumentIndex,
) -> Array[RomanIndexCanonicalGroup] {
let groups : Array[RomanIndexCanonicalGroup] = []
for entry in index.entries {
let mut group_index = -1
for position = 0; position < groups.length(); position = position + 1 {
if groups[position].canonical == entry.canonical {
group_index = position
break
}
}
if group_index < 0 {
groups.push({ canonical: entry.canonical, entries: [entry] })
} else {
groups[group_index].entries.push(entry)
}
}
groups
}
///|
/// Group entries by document according to the first accepted entry.
pub fn group_roman_index_entries_by_document(
index : RomanDocumentIndex,
) -> Array[RomanIndexDocumentGroup] {
let groups : Array[RomanIndexDocumentGroup] = []
for entry in index.entries {
let mut group_index = -1
for position = 0; position < groups.length(); position = position + 1 {
if groups[position].document_id == entry.document_id {
group_index = position
break
}
}
if group_index < 0 {
groups.push({ document_id: entry.document_id, entries: [entry] })
} else {
groups[group_index].entries.push(entry)
}
}
groups
}
///|
/// Group entries by notation mode in first occurrence order.
pub fn group_roman_index_entries_by_mode(
index : RomanDocumentIndex,
) -> Array[RomanIndexModeGroup] {
let groups : Array[RomanIndexModeGroup] = []
for entry in index.entries {
let mut group_index = -1
for position = 0; position < groups.length(); position = position + 1 {
if groups[position].mode == entry.mode {
group_index = position
break
}
}
if group_index < 0 {
groups.push({ mode: entry.mode, entries: [entry] })
} else {
groups[group_index].entries.push(entry)
}
}
groups
}
///|
/// Group entries by Unicode compatibility evidence.
pub fn group_roman_index_entries_by_unicode(
index : RomanDocumentIndex,
) -> Array[RomanIndexUnicodeGroup] {
let groups : Array[RomanIndexUnicodeGroup] = []
for entry in index.entries {
let mut group_index = -1
for position = 0; position < groups.length(); position = position + 1 {
if groups[position].used_unicode == entry.used_unicode_compatibility {
group_index = position
break
}
}
if group_index < 0 {
groups.push({
used_unicode: entry.used_unicode_compatibility,
entries: [entry],
})
} else {
groups[group_index].entries.push(entry)
}
}
groups
}
///|
/// Group entries by whether parsing changed their input spelling.
pub fn group_roman_index_entries_by_normalization(
index : RomanDocumentIndex,
) -> Array[RomanIndexNormalizationGroup] {
let groups : Array[RomanIndexNormalizationGroup] = []
for entry in index.entries {
let mut group_index = -1
for position = 0; position < groups.length(); position = position + 1 {
if groups[position].normalized_input == entry.normalized_input {
group_index = position
break
}
}
if group_index < 0 {
groups.push({ normalized_input: entry.normalized_input, entries: [entry] })
} else {
groups[group_index].entries.push(entry)
}
}
groups
}