use crate::errors::*;
use crate::recognizer::Recognizer;
use crate::{AnnotationEntry, DecisionRule, DecisionTable, HitPolicy, InputClause, InputEntry, OutputClause, OutputEntry, RuleAnnotationClause};
use dsntk_common::Result;
struct Size {
input_clauses_count: usize,
input_values_count: usize,
output_clauses_count: usize,
output_components_count: usize,
output_values_count: usize,
annotation_clauses_count: usize,
rule_count: usize,
}
fn size_err(details: &str) -> Result<Size> {
Err(err_invalid_size(details))
}
fn validate_size(recognizer: &Recognizer) -> Result<Size> {
let input_clauses_count = recognizer.input_clause_count;
let input_expression_count = recognizer.input_expressions.len();
if input_expression_count != input_clauses_count {
return size_err(&format!(
"number of input expressions ({input_expression_count}) must be equal to the number of input clauses ({input_clauses_count})"
));
}
let input_values_count = recognizer.allowed_input_values.len();
if input_values_count > 0 && input_values_count != input_clauses_count {
return size_err(&format!(
"number of input values ({input_values_count}) must be equal to the number of input clauses ({input_clauses_count})"
));
}
let output_clauses_count = recognizer.output_clause_count;
if output_clauses_count == 0 {
return size_err("decision table must have minimum one output clause");
}
let output_components_count = recognizer.output_components.len();
if output_clauses_count > 1 {
if output_components_count != output_clauses_count {
return size_err(&format!(
"number of output components ({output_components_count}) must be equal to the number of output clauses ({output_clauses_count})"
));
}
} else if output_components_count != 0 {
return size_err("number of output components must be zero");
}
let output_values_count = recognizer.allowed_output_values.len();
if output_values_count > 0 && output_values_count != output_clauses_count {
return size_err(&format!(
"number of output values ({output_values_count}) must be equal to the number of output clauses ({output_clauses_count})"
));
}
let rule_count = recognizer.rule_count;
if rule_count == 0 {
return size_err("decision table must contain minimum one rule");
}
let input_entries_row_count = recognizer.input_entries.len();
if input_entries_row_count != rule_count {
return size_err(&format!(
"number of input entries ({input_entries_row_count}) must be equal to the number of rules ({rule_count})",
));
}
for (row_index, row) in recognizer.input_entries.iter().enumerate() {
if row.len() != input_clauses_count {
return size_err(&format!(
"number of input entries ({}) must be equal to the number of input clauses ({input_clauses_count}) in row {row_index}",
row.len(),
));
}
}
let output_entries_row_count = recognizer.output_entries.len();
if output_entries_row_count != rule_count {
return size_err(&format!(
"number of output entries ({output_entries_row_count}) must be equal to the number of rules ({rule_count})"
));
}
for (row_index, row) in recognizer.output_entries.iter().enumerate() {
if row.len() != output_clauses_count {
return size_err(&format!(
"number of output entries ({}) must be equal to the number of output clauses ({output_clauses_count}) in row {row_index}",
row.len()
));
}
}
let annotation_clauses_count = recognizer.annotation_clause_count;
if annotation_clauses_count > 0 {
let annotation_entries_row_count = recognizer.annotation_entries.len();
if annotation_entries_row_count != rule_count {
return size_err(&format!(
"number of annotation entries ({annotation_entries_row_count}) must be equal to the number of rules ({rule_count})"
));
}
for (row_index, row) in recognizer.annotation_entries.iter().enumerate() {
if row.len() != annotation_clauses_count {
return size_err(&format!(
"number of annotation entries ({}) must be equal to the number of annotation clauses ({}) in row {}",
row.len(),
annotation_clauses_count,
row_index
));
}
}
}
Ok(Size {
input_clauses_count,
input_values_count,
output_clauses_count,
output_components_count,
output_values_count,
annotation_clauses_count,
rule_count,
})
}
pub fn recognize(text: &str, trace: bool) -> Result<DecisionTable> {
let recognizer = Recognizer::recognize(text, trace)?;
let size = validate_size(&recognizer)?;
let information_item_name = recognizer.information_item_name.clone();
let hit_policy = recognizer.hit_policy;
let aggregation = if let HitPolicy::Collect(built_in_aggregator) = hit_policy {
Some(built_in_aggregator)
} else {
None
};
let preferred_orientation = recognizer.orientation;
let output_label = recognizer.output_label.clone();
let mut input_clauses = vec![];
for i in 0..size.input_clauses_count {
input_clauses.push(InputClause {
input_expression: recognizer.input_expressions[i].clone(),
allowed_input_values: if size.input_values_count > 0 {
recognizer.allowed_input_values[i].clone()
} else {
None
},
});
}
let mut output_clauses = vec![];
for i in 0..size.output_clauses_count {
output_clauses.push(OutputClause {
name: if size.output_components_count > 0 {
recognizer.output_components[i].clone()
} else {
None
},
allowed_output_values: if size.output_values_count > 0 {
recognizer.allowed_output_values[i].clone()
} else {
None
},
default_output_entry: None,
});
}
let mut annotations = vec![];
for i in 0..recognizer.annotation_clause_count {
annotations.push(RuleAnnotationClause {
name: recognizer.annotations[i].clone(),
});
}
let mut rules = vec![];
for rule_index in 0..size.rule_count {
let mut input_entries = vec![];
for column_index in 0..size.input_clauses_count {
let input_entry = InputEntry {
text: recognizer.input_entries[rule_index][column_index].clone(),
};
input_entries.push(input_entry);
}
let mut output_entries = vec![];
for column_index in 0..size.output_clauses_count {
let output_entry = OutputEntry {
text: recognizer.output_entries[rule_index][column_index].clone(),
};
output_entries.push(output_entry);
}
let mut annotation_entries = vec![];
for column_index in 0..size.annotation_clauses_count {
let annotation_entry = AnnotationEntry {
text: recognizer.annotation_entries[rule_index][column_index].clone(),
};
annotation_entries.push(annotation_entry);
}
rules.push(DecisionRule {
input_entries,
output_entries,
annotation_entries,
});
}
Ok(DecisionTable::new(
information_item_name,
input_clauses,
output_clauses,
annotations,
rules,
hit_policy,
aggregation,
preferred_orientation,
output_label,
))
}