use crate::domain::BoundaryFlags;
#[derive(Debug, Clone, PartialEq)]
pub struct BoundaryContext {
pub text: String,
pub position: usize,
pub boundary_char: char,
pub preceding_context: String,
pub following_context: String,
}
#[derive(Debug, Clone, PartialEq)]
pub enum BoundaryDecision {
Boundary(BoundaryFlags),
NotBoundary,
NeedsMoreContext,
}
#[derive(Debug, Clone, PartialEq)]
pub struct AbbreviationResult {
pub is_abbreviation: bool,
pub length: usize,
pub confidence: f32,
}
#[derive(Debug, Clone, PartialEq)]
pub struct QuotationContext {
pub text: String,
pub position: usize,
pub quote_char: char,
pub inside_quotes: bool,
}
#[derive(Debug, Clone, PartialEq)]
pub enum QuotationDecision {
QuoteStart,
QuoteEnd,
Regular,
}
pub trait LanguageRules: Send + Sync {
fn detect_sentence_boundary(&self, context: &BoundaryContext) -> BoundaryDecision;
fn process_abbreviation(&self, text: &str, position: usize) -> AbbreviationResult;
fn handle_quotation(&self, context: &QuotationContext) -> QuotationDecision;
fn language_code(&self) -> &str;
fn language_name(&self) -> &str;
fn get_enclosure_char(&self, ch: char) -> Option<crate::domain::enclosure::EnclosureChar>;
fn get_enclosure_type_id(&self, ch: char) -> Option<usize>;
fn enclosure_type_count(&self) -> usize;
fn is_potential_terminator(&self, ch: char) -> bool {
matches!(ch, '.' | '!' | '?' | '。' | '!' | '?')
}
fn symmetric_enclosure_types(&self) -> Vec<bool> {
vec![false; self.enclosure_type_count()]
}
fn enclosure_suppressor(
&self,
) -> Option<&dyn crate::domain::enclosure_suppressor::EnclosureSuppressor> {
None
}
}
pub trait LanguageRuleSet: Send + Sync {
fn primary_rules(&self) -> &dyn LanguageRules;
fn fallback_rules(&self) -> &dyn LanguageRules;
fn detect_language_rules(&self, text: &str) -> &dyn LanguageRules;
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_boundary_context_creation() {
let context = BoundaryContext {
text: "Hello world. This is a test.".to_string(),
position: 11,
boundary_char: '.',
preceding_context: "Hello world".to_string(),
following_context: " This is a".to_string(),
};
assert_eq!(context.position, 11);
assert_eq!(context.boundary_char, '.');
}
#[test]
fn test_boundary_decision_variants() {
let strong_boundary = BoundaryDecision::Boundary(BoundaryFlags::STRONG);
let not_boundary = BoundaryDecision::NotBoundary;
let needs_context = BoundaryDecision::NeedsMoreContext;
match strong_boundary {
BoundaryDecision::Boundary(flags) => assert_eq!(flags, BoundaryFlags::STRONG),
_ => panic!("Expected boundary decision"),
}
assert_eq!(not_boundary, BoundaryDecision::NotBoundary);
assert_eq!(needs_context, BoundaryDecision::NeedsMoreContext);
}
#[test]
fn test_abbreviation_result() {
let result = AbbreviationResult {
is_abbreviation: true,
length: 3,
confidence: 0.95,
};
assert!(result.is_abbreviation);
assert_eq!(result.length, 3);
assert!((result.confidence - 0.95).abs() < f32::EPSILON);
}
}