use crate::transducer::OperationType;
#[derive(Clone, Debug)]
pub struct OperationSet {
operations: Vec<OperationType>,
}
impl OperationSet {
#[inline]
pub fn new() -> Self {
Self {
operations: Vec::new(),
}
}
#[inline]
pub fn with_capacity(capacity: usize) -> Self {
Self {
operations: Vec::with_capacity(capacity),
}
}
pub fn add(&mut self, op: OperationType) {
self.operations.push(op);
}
#[inline]
pub fn operations(&self) -> &[OperationType] {
&self.operations
}
#[inline]
pub fn len(&self) -> usize {
self.operations.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.operations.is_empty()
}
#[inline]
pub fn iter(&self) -> impl Iterator<Item = &OperationType> {
self.operations.iter()
}
pub fn standard() -> Self {
OperationSetBuilder::new().with_standard_ops().build()
}
pub fn with_transposition() -> Self {
OperationSetBuilder::new()
.with_standard_ops()
.with_transposition()
.build()
}
pub fn with_merge_split() -> Self {
OperationSetBuilder::new()
.with_standard_ops()
.with_merge()
.with_split()
.build()
}
}
impl Default for OperationSet {
#[inline]
fn default() -> Self {
Self::standard()
}
}
#[derive(Clone, Debug)]
pub struct OperationSetBuilder {
operations: Vec<OperationType>,
}
impl OperationSetBuilder {
#[inline]
pub fn new() -> Self {
Self {
operations: Vec::new(),
}
}
pub fn with_operation(mut self, op: OperationType) -> Self {
self.operations.push(op);
self
}
pub fn with_match(self) -> Self {
self.with_operation(OperationType::new(1, 1, 0.0, "match"))
}
pub fn with_substitution(self) -> Self {
self.with_operation(OperationType::new(1, 1, 1.0, "substitute"))
}
pub fn with_insertion(self) -> Self {
self.with_operation(OperationType::new(0, 1, 1.0, "insert"))
}
pub fn with_deletion(self) -> Self {
self.with_operation(OperationType::new(1, 0, 1.0, "delete"))
}
pub fn with_transposition(self) -> Self {
self.with_operation(OperationType::new(2, 2, 1.0, "transpose"))
}
pub fn with_merge(self) -> Self {
self.with_operation(OperationType::new(1, 2, 1.0, "merge"))
}
pub fn with_split(self) -> Self {
self.with_operation(OperationType::new(2, 1, 1.0, "split"))
}
pub fn with_standard_ops(self) -> Self {
self.with_match()
.with_substitution()
.with_insertion()
.with_deletion()
}
#[inline]
pub fn build(self) -> OperationSet {
OperationSet {
operations: self.operations,
}
}
}
impl Default for OperationSetBuilder {
#[inline]
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::transducer::SubstitutionSet;
#[test]
fn test_empty_operation_set() {
let ops = OperationSet::new();
assert_eq!(ops.len(), 0);
assert!(ops.is_empty());
}
#[test]
fn test_standard_operations() {
let ops = OperationSet::standard();
assert_eq!(ops.len(), 4);
assert!(!ops.is_empty());
let names: Vec<_> = ops.iter().map(|op| op.name()).collect();
assert!(names.contains(&"match"));
assert!(names.contains(&"substitute"));
assert!(names.contains(&"insert"));
assert!(names.contains(&"delete"));
}
#[test]
fn test_with_transposition() {
let ops = OperationSet::with_transposition();
assert_eq!(ops.len(), 5);
let names: Vec<_> = ops.iter().map(|op| op.name()).collect();
assert!(names.contains(&"transpose"));
}
#[test]
fn test_with_merge_split() {
let ops = OperationSet::with_merge_split();
assert_eq!(ops.len(), 6);
let names: Vec<_> = ops.iter().map(|op| op.name()).collect();
assert!(names.contains(&"merge"));
assert!(names.contains(&"split"));
}
#[test]
fn test_builder_standard() {
let ops = OperationSetBuilder::new().with_standard_ops().build();
assert_eq!(ops.len(), 4);
}
#[test]
fn test_builder_custom() {
let ops = OperationSetBuilder::new()
.with_match()
.with_operation(OperationType::new(1, 1, 0.5, "cheap_subst"))
.with_insertion()
.build();
assert_eq!(ops.len(), 3);
let custom_op = ops
.operations()
.iter()
.find(|op| op.name() == "cheap_subst")
.expect("Custom operation should exist");
assert_eq!(custom_op.weight(), 0.5);
}
#[test]
fn test_builder_phonetic() {
let mut phonetic = SubstitutionSet::new();
phonetic.allow_str("ph", "f");
let ops = OperationSetBuilder::new()
.with_match()
.with_operation(OperationType::with_restriction(
2, 1, 0.15, phonetic, "ph_to_f",
))
.with_standard_ops()
.build();
assert_eq!(ops.len(), 6);
let ph_op = ops
.operations()
.iter()
.find(|op| op.name() == "ph_to_f")
.expect("Phonetic operation should exist");
assert!(ph_op.is_restricted());
assert_eq!(ph_op.consume_x(), 2);
assert_eq!(ph_op.consume_y(), 1);
}
#[test]
fn test_default_is_standard() {
let ops = OperationSet::default();
assert_eq!(ops.len(), 4);
}
#[test]
fn test_iteration() {
let ops = OperationSet::standard();
let count = ops.iter().count();
assert_eq!(count, 4);
}
#[test]
fn test_add_operation() {
let mut ops = OperationSet::new();
ops.add(OperationType::new(1, 1, 0.0, "match"));
assert_eq!(ops.len(), 1);
}
}