dsntk_recognizer/
model.rs

1//! # Decision table model
2
3use crate::errors::err_invalid_hit_policy;
4use dsntk_common::DsntkError;
5use std::fmt;
6use std::fmt::Display;
7
8/// Represents a decision table.
9#[derive(Debug, Clone)]
10pub struct DecisionTable {
11  /// Information item name.
12  pub information_item_name: Option<String>,
13  /// List of instances of input clause that compose this decision table.
14  pub input_clauses: Vec<InputClause>,
15  /// List of instances of output clause that compose this decision table.
16  pub output_clauses: Vec<OutputClause>,
17  /// List of instances of rule annotation clause that compose this decision table.
18  pub annotation_clauses: Vec<AnnotationClause>,
19  /// List of instances of decision rule that compose this decision table.
20  pub rules: Vec<DecisionRule>,
21  /// Hit policy associated with the instance of the decision table.
22  pub hit_policy: HitPolicy,
23  /// Optional aggregation type when the hit policy is [HitPolicy::Collect].
24  pub aggregation: Option<BuiltinAggregator>,
25  /// Preferred orientation representation of the instance of the decision table.
26  pub preferred_orientation: DecisionTableOrientation,
27  /// Optional output label for the description of the decision table output.
28  pub output_label: Option<String>,
29}
30
31impl DecisionTable {
32  /// Creates a new decision table.
33  #[allow(clippy::too_many_arguments)]
34  pub fn new(
35    information_item_name: Option<String>,
36    input_clauses: Vec<InputClause>,
37    output_clauses: Vec<OutputClause>,
38    annotation_clauses: Vec<AnnotationClause>,
39    rules: Vec<DecisionRule>,
40    hit_policy: HitPolicy,
41    aggregation: Option<BuiltinAggregator>,
42    preferred_orientation: DecisionTableOrientation,
43    output_label: Option<String>,
44  ) -> Self {
45    Self {
46      information_item_name,
47      input_clauses,
48      output_clauses,
49      annotation_clauses,
50      rules,
51      hit_policy,
52      aggregation,
53      preferred_orientation,
54      output_label,
55    }
56  }
57}
58
59/// Represents an input clause.
60#[derive(Debug, Clone, PartialEq, Eq)]
61pub struct InputClause {
62  /// The subject of this input clause, text representation of unary tests.
63  pub input_expression: String,
64  /// Optional unary tests that constrain the result of input expression of this input clause.
65  pub allowed_input_values: Option<String>,
66}
67
68/// Represents an output clause.
69#[derive(Debug, Clone, PartialEq, Eq)]
70pub struct OutputClause {
71  /// The name of the output component when the decision table contains more than one output clause.
72  pub output_component_name: Option<String>,
73  /// Unary tests that constrain the result of output entries corresponding to output clause.
74  pub allowed_output_values: Option<String>,
75  /// Default output expression, selected in incomplete table when no rules match for the decision table.
76  pub default_output_value: Option<String>,
77}
78
79/// Represents a rule annotation clause.
80#[derive(Debug, Clone, PartialEq, Eq)]
81pub struct AnnotationClause {
82  /// Text that is used as the name of the rule annotation column
83  /// of the containing decision table.
84  pub name: String,
85}
86
87/// Represents a decision rule.
88#[derive(Debug, Clone, PartialEq, Eq)]
89pub struct DecisionRule {
90  /// Ordered list of input entries that compose decision rule.
91  pub input_entries: Vec<InputEntry>,
92  /// Ordered list of output entries that compose decision rule.
93  pub output_entries: Vec<OutputEntry>,
94  /// Ordered list of rule annotations that compose decision rule.
95  pub annotation_entries: Vec<AnnotationEntry>,
96}
97
98/// Represents an input entry.
99#[derive(Debug, Clone, PartialEq, Eq)]
100pub struct InputEntry {
101  /// Text representation of unary test that composes recognized input entry.
102  pub text: String,
103}
104
105/// Represents an output entry.
106#[derive(Debug, Clone, PartialEq, Eq)]
107pub struct OutputEntry {
108  /// Text representation of literal expression that composes recognized output entry.
109  pub text: String,
110}
111
112/// Represents a rule annotation entry.
113#[derive(Debug, Clone, PartialEq, Eq)]
114pub struct AnnotationEntry {
115  /// Text representation of the rule annotation.
116  pub text: String,
117}
118
119/// Enumeration of hit policies.
120#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
121pub enum HitPolicy {
122  /// `UNIQUE` hit policy.
123  Unique,
124  /// `ANY` hit policy.
125  Any,
126  /// `PRIORITY` hit policy.
127  Priority,
128  /// `FIRST` hit policy.
129  First,
130  /// `COLLECT` hit policy.
131  Collect(BuiltinAggregator),
132  /// `OUTPUT ORDER` hit policy.
133  OutputOrder,
134  /// `RULE ORDER` hit policy.
135  RuleOrder,
136}
137
138impl HitPolicy {
139  /// Returns optional aggregation associated with this hit policy.
140  pub fn aggregation(&self) -> Option<BuiltinAggregator> {
141    match self {
142      HitPolicy::Collect(aggregation) => Some(*aggregation),
143      _ => None,
144    }
145  }
146}
147
148impl Display for HitPolicy {
149  /// Converts hit policy into text.
150  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
151    write!(
152      f,
153      "{}",
154      match self {
155        HitPolicy::Unique => "U",
156        HitPolicy::Any => "A",
157        HitPolicy::Priority => "P",
158        HitPolicy::First => "F",
159        HitPolicy::RuleOrder => "R",
160        HitPolicy::OutputOrder => "O",
161        HitPolicy::Collect(aggregator) => match aggregator {
162          BuiltinAggregator::List => "C",
163          BuiltinAggregator::Sum => "C+",
164          BuiltinAggregator::Count => "C#",
165          BuiltinAggregator::Min => "C<",
166          BuiltinAggregator::Max => "C>",
167        },
168      }
169    )
170  }
171}
172
173impl TryFrom<&str> for HitPolicy {
174  type Error = DsntkError;
175  /// Creates a hit policy from [str].
176  fn try_from(value: &str) -> dsntk_common::Result<Self, Self::Error> {
177    match value.trim() {
178      "U" => Ok(HitPolicy::Unique),
179      "A" => Ok(HitPolicy::Any),
180      "P" => Ok(HitPolicy::Priority),
181      "F" => Ok(HitPolicy::First),
182      "R" => Ok(HitPolicy::RuleOrder),
183      "O" => Ok(HitPolicy::OutputOrder),
184      "C" => Ok(HitPolicy::Collect(BuiltinAggregator::List)),
185      "C+" => Ok(HitPolicy::Collect(BuiltinAggregator::Sum)),
186      "C#" => Ok(HitPolicy::Collect(BuiltinAggregator::Count)),
187      "C<" => Ok(HitPolicy::Collect(BuiltinAggregator::Min)),
188      "C>" => Ok(HitPolicy::Collect(BuiltinAggregator::Max)),
189      other => Err(err_invalid_hit_policy(other)),
190    }
191  }
192}
193
194impl TryFrom<&String> for HitPolicy {
195  type Error = DsntkError;
196  /// Creates a hit policy from reference to [String].
197  fn try_from(value: &String) -> dsntk_common::Result<Self, Self::Error> {
198    Self::try_from(value.as_str())
199  }
200}
201
202/// Enumeration of built-in aggregators for [HitPolicy].
203#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
204pub enum BuiltinAggregator {
205  /// The result is a list of matching output entries.
206  List,
207  /// The result is the number of matching outputs.
208  Count,
209  /// The result is the sum of all matching outputs.
210  Sum,
211  /// The result is the smallest value of matching outputs.
212  Min,
213  /// The result is the largest value of matching outputs.
214  Max,
215}
216
217/// Enumeration of preferred decision table orientations.
218#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
219pub enum DecisionTableOrientation {
220  /// Decision table is presented horizontally, rules are presented as rows.
221  RulesAsRows,
222  /// Decision table is presented vertically, rules are presented as columns.
223  RulesAsColumns,
224  /// Decision table is presented as crosstab, rules are composed of two dimensions.
225  CrossTable,
226}
227
228impl Display for DecisionTableOrientation {
229  /// Converts decision table orientation into text.
230  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
231    write!(
232      f,
233      "{}",
234      match self {
235        DecisionTableOrientation::RulesAsRows => "rules-as-rows",
236        DecisionTableOrientation::RulesAsColumns => "rules-as-columns",
237        DecisionTableOrientation::CrossTable => "cross-table",
238      }
239    )
240  }
241}