use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
#[derive(
Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, JsonSchema,
)]
#[serde(rename_all = "kebab-case")]
pub enum EvidenceClass {
DirectEntry,
RelatedEntry,
EntryMention,
ContextMention,
}
impl EvidenceClass {
pub const ALL: [Self; 4] = [
Self::DirectEntry,
Self::RelatedEntry,
Self::EntryMention,
Self::ContextMention,
];
#[must_use]
pub const fn title(self) -> &'static str {
match self {
Self::DirectEntry => "Direct entries",
Self::RelatedEntry => "Explicitly related entries",
Self::EntryMention => "Mentions in other entries",
Self::ContextMention => "Mentions in ordinary content",
}
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "kebab-case")]
pub enum EvidenceOrder {
#[default]
ClassThenSource,
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct EvidenceCount {
pub total: u32,
pub returned: u32,
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct EvidenceCounts {
pub direct_entry: EvidenceCount,
pub related_entry: EvidenceCount,
pub entry_mention: EvidenceCount,
pub context_mention: EvidenceCount,
}
impl EvidenceCounts {
#[must_use]
pub const fn get(&self, class: EvidenceClass) -> EvidenceCount {
match class {
EvidenceClass::DirectEntry => self.direct_entry,
EvidenceClass::RelatedEntry => self.related_entry,
EvidenceClass::EntryMention => self.entry_mention,
EvidenceClass::ContextMention => self.context_mention,
}
}
pub fn record(&mut self, class: EvidenceClass, returned: bool) {
let count = match class {
EvidenceClass::DirectEntry => &mut self.direct_entry,
EvidenceClass::RelatedEntry => &mut self.related_entry,
EvidenceClass::EntryMention => &mut self.entry_mention,
EvidenceClass::ContextMention => &mut self.context_mention,
};
count.total = count.total.saturating_add(1);
count.returned = count.returned.saturating_add(u32::from(returned));
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct ExplanationPreview {
pub block_path: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub source: Option<mant_ir::SourceSpan>,
pub text: String,
pub match_start_char: u32,
pub match_end_char: u32,
pub content_ranges: Vec<super::ExplanationContentRange>,
pub clipped_before: bool,
pub clipped_after: bool,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn class_order_counts_and_closed_preview_are_explicit() {
assert!(EvidenceClass::ALL.windows(2).all(|w| w[0] < w[1]));
let mut counts = EvidenceCounts::default();
counts.record(EvidenceClass::DirectEntry, true);
counts.record(EvidenceClass::ContextMention, false);
assert_eq!(
counts.direct_entry,
EvidenceCount {
total: 1,
returned: 1
}
);
assert_eq!(
counts.context_mention,
EvidenceCount {
total: 1,
returned: 0
}
);
assert_eq!(
serde_json::to_value(EvidenceOrder::default()).unwrap(),
"class-then-source"
);
assert!(
serde_json::from_value::<EvidenceCounts>(
serde_json::json!({"directEntry":{"total":0,"returned":0}})
)
.is_err()
);
assert!(
serde_json::from_value::<ExplanationPreview>(serde_json::json!({
"blockPath":"root/b0", "text":"日本", "matchStartChar":0, "matchEndChar":2,
"clippedBefore":false, "clippedAfter":false, "unknown":true
}))
.is_err()
);
}
}