fixup_features/
changes.rs
1use anyhow::{anyhow, Result};
4use toml_edit::{Array, Item, Table, Value};
5
6#[derive(Debug, Clone, Default)]
7pub struct Changes {
8 changes: Vec<Change>,
9}
10
11#[derive(Debug, Clone)]
12pub enum Change {
13 AddFeature(String),
14 AddEdge(String, String),
15 AddExternalEdge(String, String),
16 Annotate(String, String),
17}
18
19fn value_is_str(value: &Value, string: &str) -> bool {
20 matches! {
21 value, Value::String(s) if s.value() == string
22 }
23}
24
25impl Change {
26 fn apply(&self, features: &mut Table) -> Result<()> {
27 match self {
28 Change::AddFeature(feature_name) => match features.get(feature_name) {
29 Some(_) => {} None => {
31 assert!(!feature_name.contains('/'), "/ in {feature_name}");
32 features.insert(feature_name, Item::Value(Value::Array(Array::new())));
33 }
34 },
35 Change::AddEdge(from, to) => {
36 Change::AddFeature(to.to_string()).apply(features)?;
38 Change::AddExternalEdge(from.to_string(), to.to_string()).apply(features)?;
39 }
40 Change::AddExternalEdge(from, to) => {
41 Change::AddFeature(from.to_string()).apply(features)?;
43 assert!(!from.contains('/'), "/ in {from}");
44 let array = features
45 .get_mut(from)
46 .expect("but we just tried to add {from}!")
47 .as_array_mut()
48 .ok_or_else(|| anyhow!("features.{from} wasn't an array!"))?;
49 if !array.iter().any(|val| value_is_str(val, to)) {
50 array.push(to);
51 }
52 }
53 Change::Annotate(feature_name, annotation) => {
54 if features.get(feature_name).is_none() {
55 return Err(anyhow!(
56 "no such feature as {feature_name} to annotate with {annotation}"
57 ));
58 }
59 let mut key = features.key_mut(feature_name).expect("key not found!?");
60 let decor = key.leaf_decor_mut();
61 let prefix = match decor.prefix() {
62 Some(r) => r.as_str().expect("prefix not a string"), None => "",
64 };
65 if !prefix.contains(annotation) {
66 let mut new_prefix: String = prefix.to_string();
67 new_prefix.push('\n');
68 new_prefix.push_str(annotation);
69 decor.set_prefix(new_prefix);
70 }
71 }
72 }
73 Ok(())
74 }
75}
76
77impl Changes {
78 pub fn push(&mut self, change: Change) {
79 self.changes.push(change);
80 }
81 pub fn drop_annotations(&mut self) {
82 self.changes
83 .retain(|change| !matches!(change, Change::Annotate(_, _)));
84 }
85 pub fn apply(&self, features: &mut Table) -> Result<()> {
86 self.changes
87 .iter()
88 .try_for_each(|change| change.apply(features))
89 }
90}