pub mod codes;
pub mod corrections;
mod preprocessor;
pub mod registry;
mod types;
pub use codes::{CodeCategory, CodeInfo, ModeAction, ModeBehavior};
pub use corrections::{
detect_accession_typo, detect_amino_acid_typo, detect_edit_type_typo, detect_missing_version,
detect_swapped_positions, detect_typos, find_closest_match, levenshtein_distance,
DetectedCorrection, FuzzyMatch, TypoSuggestion, TypoTokenType,
};
pub use preprocessor::{CorrectionWarning, InputPreprocessor, PreprocessResult};
pub use registry::{get_code_info, list_all_codes, list_error_codes, list_warning_codes};
pub use types::{ErrorMode, ErrorOverride, ErrorType, ResolvedAction};
use std::collections::HashMap;
#[derive(Debug, Clone)]
pub struct ErrorConfig {
pub mode: ErrorMode,
pub overrides: HashMap<ErrorType, ErrorOverride>,
}
impl ErrorConfig {
pub fn new(mode: ErrorMode) -> Self {
Self {
mode,
overrides: HashMap::new(),
}
}
pub fn strict() -> Self {
Self::new(ErrorMode::Strict)
}
pub fn lenient() -> Self {
Self::new(ErrorMode::Lenient)
}
pub fn silent() -> Self {
Self::new(ErrorMode::Silent)
}
pub fn with_override(mut self, error_type: ErrorType, override_: ErrorOverride) -> Self {
self.overrides.insert(error_type, override_);
self
}
pub fn set_override(&mut self, error_type: ErrorType, override_: ErrorOverride) {
self.overrides.insert(error_type, override_);
}
pub fn remove_override(&mut self, error_type: ErrorType) {
self.overrides.remove(&error_type);
}
pub fn action_for(&self, error_type: ErrorType) -> ResolvedAction {
let override_ = self.overrides.get(&error_type).copied().unwrap_or_default();
override_.resolve(self.mode)
}
pub fn should_reject(&self, error_type: ErrorType) -> bool {
self.action_for(error_type).should_reject()
}
pub fn should_correct(&self, error_type: ErrorType) -> bool {
self.action_for(error_type).should_correct()
}
pub fn should_warn(&self, error_type: ErrorType) -> bool {
self.action_for(error_type).should_warn()
}
pub fn preprocessor(&self) -> InputPreprocessor {
InputPreprocessor::new(self.clone())
}
}
impl Default for ErrorConfig {
fn default() -> Self {
Self::strict()
}
}
#[derive(Debug, Clone)]
pub struct ParseResultWithWarnings<T> {
pub result: T,
pub warnings: Vec<CorrectionWarning>,
pub original_input: String,
pub preprocessed_input: String,
}
impl<T> ParseResultWithWarnings<T> {
pub fn new(
result: T,
warnings: Vec<CorrectionWarning>,
original_input: String,
preprocessed_input: String,
) -> Self {
Self {
result,
warnings,
original_input,
preprocessed_input,
}
}
pub fn without_warnings(result: T, input: String) -> Self {
Self {
result,
warnings: Vec::new(),
original_input: input.clone(),
preprocessed_input: input,
}
}
pub fn had_corrections(&self) -> bool {
self.original_input != self.preprocessed_input
}
pub fn has_warnings(&self) -> bool {
!self.warnings.is_empty()
}
pub fn map<U, F: FnOnce(T) -> U>(self, f: F) -> ParseResultWithWarnings<U> {
ParseResultWithWarnings {
result: f(self.result),
warnings: self.warnings,
original_input: self.original_input,
preprocessed_input: self.preprocessed_input,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_error_config_default() {
let config = ErrorConfig::default();
assert_eq!(config.mode, ErrorMode::Strict);
assert!(config.overrides.is_empty());
}
#[test]
fn test_error_config_strict() {
let config = ErrorConfig::strict();
assert_eq!(config.mode, ErrorMode::Strict);
assert!(config.should_reject(ErrorType::WrongDashCharacter));
}
#[test]
fn test_error_config_lenient() {
let config = ErrorConfig::lenient();
assert_eq!(config.mode, ErrorMode::Lenient);
assert!(config.should_correct(ErrorType::WrongDashCharacter));
assert!(config.should_warn(ErrorType::WrongDashCharacter));
}
#[test]
fn test_error_config_silent() {
let config = ErrorConfig::silent();
assert_eq!(config.mode, ErrorMode::Silent);
assert!(config.should_correct(ErrorType::WrongDashCharacter));
assert!(!config.should_warn(ErrorType::WrongDashCharacter));
}
#[test]
fn test_error_config_with_override() {
let config = ErrorConfig::lenient()
.with_override(ErrorType::LowercaseAminoAcid, ErrorOverride::Reject);
assert!(config.should_reject(ErrorType::LowercaseAminoAcid));
assert!(config.should_correct(ErrorType::WrongDashCharacter));
}
#[test]
fn test_error_config_set_override() {
let mut config = ErrorConfig::strict();
config.set_override(ErrorType::WrongDashCharacter, ErrorOverride::SilentCorrect);
assert!(config.should_correct(ErrorType::WrongDashCharacter));
assert!(!config.should_warn(ErrorType::WrongDashCharacter));
}
#[test]
fn test_error_config_remove_override() {
let mut config = ErrorConfig::lenient()
.with_override(ErrorType::WrongDashCharacter, ErrorOverride::Reject);
assert!(config.should_reject(ErrorType::WrongDashCharacter));
config.remove_override(ErrorType::WrongDashCharacter);
assert!(config.should_correct(ErrorType::WrongDashCharacter));
}
#[test]
fn test_error_config_action_for() {
let config =
ErrorConfig::lenient().with_override(ErrorType::PositionZero, ErrorOverride::Reject);
assert_eq!(
config.action_for(ErrorType::WrongDashCharacter),
ResolvedAction::WarnCorrect
);
assert_eq!(
config.action_for(ErrorType::PositionZero),
ResolvedAction::Reject
);
}
#[test]
fn test_error_config_preprocessor() {
let config = ErrorConfig::lenient();
let preprocessor = config.preprocessor();
let result = preprocessor.preprocess("c.100\u{2013}200del");
assert!(result.success);
assert_eq!(result.preprocessed, "c.100-200del");
}
#[test]
fn test_parse_result_with_warnings_new() {
let result = ParseResultWithWarnings::new(
42,
vec![CorrectionWarning::new(
ErrorType::WrongDashCharacter,
"test",
None,
"",
"",
)],
"original".to_string(),
"preprocessed".to_string(),
);
assert_eq!(result.result, 42);
assert!(result.has_warnings());
assert!(result.had_corrections());
}
#[test]
fn test_parse_result_with_warnings_without_warnings() {
let result = ParseResultWithWarnings::without_warnings(42, "input".to_string());
assert_eq!(result.result, 42);
assert!(!result.has_warnings());
assert!(!result.had_corrections());
}
#[test]
fn test_parse_result_with_warnings_map() {
let result = ParseResultWithWarnings::without_warnings(42, "input".to_string());
let mapped = result.map(|x| x.to_string());
assert_eq!(mapped.result, "42");
}
}