1use std::collections::HashMap;
2
3use crate::change::Change;
4use crate::error::FlakeEditError;
5use crate::input::Input;
6use crate::walk::Walker;
7
8pub struct FlakeEdit {
9 changes: Vec<Change>,
10 walker: Walker,
11}
12
13#[derive(Default, Debug)]
14pub enum Outputs {
15 #[default]
16 None,
17 Multiple(Vec<String>),
19 Any(Vec<String>),
21}
22
23pub type InputMap = HashMap<String, Input>;
24
25#[derive(Default, Debug)]
26pub enum OutputChange {
27 #[default]
28 None,
29 Add(String),
30 Remove(String),
31}
32
33impl FlakeEdit {
34 pub fn new(changes: Vec<Change>, walker: Walker) -> Self {
35 Self { changes, walker }
36 }
37
38 pub fn from_text(stream: &str) -> Result<Self, FlakeEditError> {
39 let walker = Walker::new(stream);
40 Ok(Self::new(Vec::new(), walker))
41 }
42
43 pub fn changes(&self) -> &[Change] {
44 self.changes.as_ref()
45 }
46
47 pub fn add_change(&mut self, change: Change) {
48 self.changes.push(change);
49 }
50
51 pub fn curr_list(&self) -> &InputMap {
52 &self.walker.inputs
53 }
54
55 pub fn list(&mut self) -> &InputMap {
58 self.walker.inputs.clear();
59 assert!(self.walker.walk(&Change::None).is_none());
60 &self.walker.inputs
61 }
62 pub fn apply_change(&mut self, change: Change) -> Result<Option<String>, FlakeEditError> {
65 match change {
66 Change::None => Ok(None),
67 Change::Add { .. } => {
69 if let Some(input_id) = change.id() {
71 if self.walker.inputs.is_empty() {
73 self.walker.walk(&Change::None);
74 }
75
76 let input_id_string = input_id.to_string();
77 if self.walker.inputs.contains_key(&input_id_string) {
78 return Err(FlakeEditError::DuplicateInput(input_id_string));
79 }
80 }
81
82 if let Some(maybe_changed_node) = self.walker.walk(&change.clone()) {
83 let outputs = self.walker.list_outputs();
84 match outputs {
85 Outputs::Multiple(out) => {
86 let id = change.id().unwrap().to_string();
87 if !out.contains(&id) {
88 self.walker.root = maybe_changed_node.clone();
89 if let Some(maybe_changed_node) =
90 self.walker.change_outputs(OutputChange::Add(id))
91 {
92 return Ok(Some(maybe_changed_node.to_string()));
93 }
94 }
95 }
96 Outputs::None | Outputs::Any(_) => {}
97 }
98 Ok(Some(maybe_changed_node.to_string()))
99 } else {
100 self.walker.add_toplevel = true;
101 let maybe_changed_node = self.walker.walk(&change);
102 Ok(maybe_changed_node.map(|n| n.to_string()))
103 }
104 }
105 Change::Remove { .. } => {
106 let mut res = None;
109 while let Some(changed_node) = self.walker.walk(&change) {
110 if res == Some(changed_node.clone()) {
111 break;
113 }
114 res = Some(changed_node.clone());
115 self.walker.root = changed_node.clone();
116 }
117 let outputs = self.walker.list_outputs();
119 match outputs {
120 Outputs::Multiple(out) | Outputs::Any(out) => {
121 let id = change.id().unwrap().to_string();
122 if out.contains(&id) {
123 if let Some(changed_node) =
124 self.walker.change_outputs(OutputChange::Remove(id))
125 {
126 res = Some(changed_node.clone());
127 self.walker.root = changed_node.clone();
128 }
129 }
130 }
131 Outputs::None => {}
132 }
133 Ok(res.map(|n| n.to_string()))
134 }
135 Change::Pin { .. } => todo!(),
136 Change::Change { .. } => {
137 if let Some(maybe_changed_node) = self.walker.walk(&change) {
138 Ok(Some(maybe_changed_node.to_string()))
139 } else {
140 panic!("No change");
141 }
142 }
143 }
144 }
145
146 pub fn walker(&self) -> &Walker {
147 &self.walker
148 }
149}