///|
pub(all) struct RuleSpec {
id : String
label : String
kind : PhiKind
pattern : String
placeholder : String
confidence : Int
contextual : Bool
} derive(Debug, Eq)
///|
pub(all) struct RulePatch {
id : String
enabled : Bool?
confidence : Int?
placeholder : String?
label : String?
} derive(Debug, Eq)
///|
pub fn rule_spec(
id~ : String,
label~ : String,
kind~ : PhiKind,
pattern~ : String,
placeholder~ : String,
confidence~ : Int,
contextual? : Bool = false,
) -> RuleSpec {
{ id, label, kind, pattern, placeholder, confidence, contextual }
}
///|
pub fn rule_from_spec(spec : RuleSpec) -> Rule {
rule(
spec.id,
spec.label,
spec.kind,
spec.pattern,
spec.placeholder,
spec.confidence,
contextual=spec.contextual,
)
}
///|
pub fn rule_spec_from_rule(item : Rule) -> RuleSpec {
{
id: item.id,
label: item.label,
kind: item.kind,
pattern: item.pattern,
placeholder: item.placeholder,
confidence: item.confidence,
contextual: item.contextual,
}
}
///|
pub fn rule_patch(
id~ : String,
enabled? : Bool,
confidence? : Int,
placeholder? : String,
label? : String,
) -> RulePatch {
{ id, enabled, confidence, placeholder, label }
}
///|
pub fn apply_rule_patch(item : Rule, patch : RulePatch) -> Rule {
{
..item,
enabled: match patch.enabled {
Some(value) => value
None => item.enabled
},
confidence: match patch.confidence {
Some(value) => value
None => item.confidence
},
placeholder: match patch.placeholder {
Some(value) => value
None => item.placeholder
},
label: match patch.label {
Some(value) => value
None => item.label
},
}
}
///|
pub fn apply_rule_patches(
rules : Array[Rule],
patches : Array[RulePatch],
) -> Array[Rule] {
rules.map(fn(item) {
for patch in patches {
if patch.id == item.id {
return apply_rule_patch(item, patch)
}
}
item
})
}
///|
pub fn custom_rule(
id : String,
label : String,
kind : PhiKind,
pattern : String,
placeholder : String,
confidence : Int,
) -> Rule {
rule(id, label, kind, pattern, placeholder, confidence)
}
///|
pub fn contextual_rule(
id : String,
label : String,
kind : PhiKind,
pattern : String,
placeholder : String,
confidence : Int,
) -> Rule {
rule(id, label, kind, pattern, placeholder, confidence, contextual=true)
}
///|
pub fn normalize_rule_confidence(item : Rule) -> Rule {
let confidence = if item.confidence < 0 {
0
} else if item.confidence > 100 {
100
} else {
item.confidence
}
{ ..item, confidence, }
}
///|
pub fn normalize_rules(rules : Array[Rule]) -> Array[Rule] {
rules.map(normalize_rule_confidence)
}
///|
pub fn deduplicate_rules(rules : Array[Rule]) -> Array[Rule] {
let result = []
let seen : Map[String, Unit] = Map([])
for item in rules {
if !seen.contains(item.id) {
seen[item.id] = ()
result.push(item)
}
}
result
}
///|
pub fn compose_rules(
base : Array[Rule],
additions : Array[Rule],
) -> Array[Rule] {
let combined = base.copy()
combined.append(additions)
deduplicate_rules(combined)
}
///|
pub fn replace_rule_by_id(
rules : Array[Rule],
replacement : Rule,
) -> Array[Rule] {
let result = rules.map(fn(item) {
if item.id == replacement.id {
replacement
} else {
item
}
})
if !result.any(fn(item) { item.id == replacement.id }) {
result.push(replacement)
}
result
}
///|
pub fn remove_rules_by_id(
rules : Array[Rule],
ids : Array[String],
) -> Array[Rule] {
rules.filter(fn(item) { !ids.contains(item.id) })
}
///|
pub fn disable_rules_by_kind(
rules : Array[Rule],
kind : PhiKind,
) -> Array[Rule] {
rules.map(fn(item) {
if item.kind == kind {
{ ..item, enabled: false }
} else {
item
}
})
}
///|
pub fn enable_rules_above(rules : Array[Rule], confidence : Int) -> Array[Rule] {
rules.map(fn(item) {
if item.confidence >= confidence {
{ ..item, enabled: true }
} else {
item
}
})
}
///|
pub fn rules_to_tsv(rules : Array[Rule]) -> String {
let lines = [
"id\tlabel\tkind\tconfidence\tenabled\tcontextual\tpattern\tplaceholder",
]
for item in rules {
lines.push(
[
item.id,
item.label,
phi_kind_name(item.kind),
"\{item.confidence}",
"\{item.enabled}",
"\{item.contextual}",
item.pattern,
item.placeholder,
].join("\t"),
)
}
lines.join("\n")
}
///|
pub fn rules_checksum(rules : Array[Rule]) -> String {
stable_hash(rules_to_tsv(rules))
}
///|
pub fn rule_diff(left : Array[Rule], right : Array[Rule]) -> Array[String] {
let changes = []
for item in left {
let mut found = false
for candidate in right {
if candidate.id == item.id {
found = true
if candidate.pattern != item.pattern {
changes.push("pattern:\{item.id}")
}
if candidate.confidence != item.confidence {
changes.push("confidence:\{item.id}")
}
if candidate.enabled != item.enabled {
changes.push("enabled:\{item.id}")
}
}
}
if !found {
changes.push("removed:\{item.id}")
}
}
for item in right {
if !left.any(fn(candidate) { candidate.id == item.id }) {
changes.push("added:\{item.id}")
}
}
changes
}
///|
pub fn rule_pack_with_patches(
pack : RulePack,
patches : Array[RulePatch],
) -> RulePack {
{ ..pack, rules: apply_rule_patches(pack.rules, patches) }
}
///|
pub fn pack_round_trip_text(pack : RulePack) -> Bool {
!pack_to_text(pack).is_empty() && !pack_checksum(pack).is_empty()
}