1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
//! # Decision table model

use crate::errors::err_invalid_hit_policy;
use dsntk_common::DsntkError;

/// Represents a decision table.
#[derive(Debug)]
pub struct DecisionTable {
  /// Information item name.
  pub information_item_name: Option<String>,
  /// List of instances of input clause that compose this decision table.
  pub input_clauses: Vec<InputClause>,
  /// List of instances of output clause that compose this decision table.
  pub output_clauses: Vec<OutputClause>,
  /// List of instances of rule annotation clause that compose this decision table.
  pub rule_annotations: Vec<RuleAnnotationClause>,
  /// List of instances of decision rule that compose this decision table.
  pub rules: Vec<DecisionRule>,
  /// Hit policy associated with the instance of the decision table.
  pub hit_policy: HitPolicy,
  /// Optional aggregation type when the hit policy is `COLLECT`.
  pub aggregation: Option<BuiltinAggregator>,
  /// Preferred orientation representation of the instance of the decision table.
  pub preferred_orientation: DecisionTableOrientation,
  /// Optional output label for the description of the decision table output.
  pub output_label: Option<String>,
}

impl DecisionTable {
  /// Creates a new decision table.
  #[allow(clippy::too_many_arguments)]
  pub fn new(
    information_item_name: Option<String>,
    input_clauses: Vec<InputClause>,
    output_clauses: Vec<OutputClause>,
    rule_annotations: Vec<RuleAnnotationClause>,
    rules: Vec<DecisionRule>,
    hit_policy: HitPolicy,
    aggregation: Option<BuiltinAggregator>,
    preferred_orientation: DecisionTableOrientation,
    output_label: Option<String>,
  ) -> Self {
    Self {
      information_item_name,
      input_clauses,
      output_clauses,
      rule_annotations,
      rules,
      hit_policy,
      aggregation,
      preferred_orientation,
      output_label,
    }
  }
}

/// Represents an input clause.
#[derive(Debug)]
pub struct InputClause {
  /// The subject of this input clause, text representation of unary tests.
  pub input_expression: String,
  /// Optional unary tests that constrain the result of input expression of this input clause.
  pub allowed_input_values: Option<String>,
}

/// Represents an output clause.
#[derive(Debug)]
pub struct OutputClause {
  /// The name of the output component when the decision table contains more than one output clause.
  pub name: Option<String>,
  /// Unary tests that constrain the result of output entries corresponding to output clause.
  pub allowed_output_values: Option<String>,
  /// Default output expression, selected in incomplete table when no rules match for the decision table.
  pub default_output_entry: Option<String>,
}

/// Represents a rule annotation clause.
#[derive(Debug)]
pub struct RuleAnnotationClause {
  /// Name that is used as the name of the rule annotation column of the containing decision table.
  pub name: String,
}

/// Represents a decision rule.
#[derive(Debug)]
pub struct DecisionRule {
  /// Ordered list of input entries that compose decision rule.
  pub input_entries: Vec<InputEntry>,
  /// Ordered list of output entries that compose decision rule.
  pub output_entries: Vec<OutputEntry>,
  /// Ordered list of rule annotations that compose decision rule.
  pub annotation_entries: Vec<AnnotationEntry>,
}

/// Represents an input entry.
#[derive(Debug)]
pub struct InputEntry {
  /// Text representation of unary test that composes recognized input entry.
  pub text: String,
}

/// Represents an output entry.
#[derive(Debug)]
pub struct OutputEntry {
  /// Text representation of literal expression that composes recognized output entry.
  pub text: String,
}

/// Represents a rule annotation entry.
#[derive(Debug)]
pub struct AnnotationEntry {
  /// Text representation of the rule annotation.
  pub text: String,
}

/// Enumeration of hit policies.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum HitPolicy {
  /// `UNIQUE` hit policy.
  Unique,
  /// `ANY` hit policy.
  Any,
  /// `PRIORITY` hit policy.
  Priority,
  /// `FIRST` hit policy.
  First,
  /// `COLLECT` hit policy.
  Collect(BuiltinAggregator),
  /// `OUTPUT ORDER` hit policy.
  OutputOrder,
  /// `RULE ORDER` hit policy.
  RuleOrder,
}

impl TryFrom<&str> for HitPolicy {
  type Error = DsntkError;
  /// Creates a hit policy from text.
  fn try_from(value: &str) -> dsntk_common::Result<Self, Self::Error> {
    match value.trim() {
      "U" => Ok(HitPolicy::Unique),
      "A" => Ok(HitPolicy::Any),
      "P" => Ok(HitPolicy::Priority),
      "F" => Ok(HitPolicy::First),
      "R" => Ok(HitPolicy::RuleOrder),
      "O" => Ok(HitPolicy::OutputOrder),
      "C" => Ok(HitPolicy::Collect(BuiltinAggregator::List)),
      "C+" => Ok(HitPolicy::Collect(BuiltinAggregator::Sum)),
      "C#" => Ok(HitPolicy::Collect(BuiltinAggregator::Count)),
      "C<" => Ok(HitPolicy::Collect(BuiltinAggregator::Min)),
      "C>" => Ok(HitPolicy::Collect(BuiltinAggregator::Max)),
      other => Err(err_invalid_hit_policy(other)),
    }
  }
}

/// Enumeration of built-in aggregators.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum BuiltinAggregator {
  /// The result is a list of matching output entries.
  List,
  /// The result is the number of matching outputs.
  Count,
  /// The result is the sum of all matching outputs.
  Sum,
  /// The result is the smallest value of matching outputs.
  Min,
  /// The result is the largest value of matching outputs.
  Max,
}

/// Enumeration of preferred decision table orientations.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum DecisionTableOrientation {
  /// Decision table is presented horizontally, rules are presented as rows.
  RuleAsRow,
  /// Decision table is presented vertically, rules are presented as columns.
  RuleAsColumn,
  /// Decision table is presented as crosstab, rules are composed of two dimensions.
  CrossTable,
}