1use std::{path::{PathBuf, Path}, mem::replace, io::{BufWriter, BufReader, Write}, fs::{File, self}, process::Command};
2use flexar::{prelude::*, compile_error::CompileError};
3use hashbrown::HashMap;
4use serde::{Serialize, Deserialize};
5use crate::{safe_unwrap, lexer::Token, errors::{SyntaxError, RuntimeError}, visitor::{ActionTree, DbgValue, Value}, patt_unwrap, target_lang::{json, toml, nix}, recur};
6
7#[derive(Debug, Serialize, Deserialize)]
8pub struct ConffTree {
9 pub conf_files: Box<[ConfFile]>,
10 pub include: Box<[(Box<[u8]>, PathBuf)]>,
11}
12
13impl ConffTree {
14 pub fn from_att(mut att: ActionTree) -> Self {
16 let mut out = Vec::new();
17
18 for (conff_type, path, file_path) in att.conff_list.iter_mut() {
19 let file_path = PathBuf::from(file_path.to_string());
20 let conff_type = *conff_type;
21
22 let table = {
24 let mut current = &mut att.uni_table;
25 for (_, key) in path.iter() {
26 current = patt_unwrap!((current.get_mut(key)) Some((_, DbgValue::Table(x))) => x); }
28
29 current.clone()
30 };
31
32 let shell = {
34 let mut shell_cmds = Vec::new();
35 for (s_path, cmd) in att.shell_list.iter_mut() {
36 if path.iter().map(|(_, x)| x).collect::<Vec<_>>() == s_path.iter().map(|(_, x)| x).collect::<Vec<_>>() {shell_cmds.push(
37 replace(cmd, Box::new([]))
38 )}
39 }
40 shell_cmds.into_boxed_slice()
41 };
42
43 use ConffType as CFT;
46 match conff_type {
47 CFT::Json => json::check_table(&table),
48 CFT::Toml => toml::check_table(&table),
49 CFT::Nix => (), }
51
52 out.push(ConfFile {
53 conff_type,
54 table: table.into_iter().map(|(k, (_, x))| (k, x.into())).collect(),
55 path: file_path,
56 shell,
57 })
58 }
59
60 let mut include = Vec::new();
62 for (path, target) in att.included.into_iter() {
63 recur! {
64 walk_dir(path: PathBuf, target: PathBuf, include: &mut Vec<(Box<[u8]>, PathBuf)>) <- (path, target, &mut include) {
65 if path.is_dir() {
66 let dir = safe_unwrap!(fs::read_dir(&path) => RT014, path.to_string_lossy());
67 for item in dir {
68 let item = safe_unwrap!(item => RT015, path.to_string_lossy());
69 walk_dir(item.path(), target.join(item.file_name()), include);
70 }
71 } else if path.is_symlink() {
72 let sym_path = safe_unwrap!(path.read_link() => RT008, path.to_string_lossy());
73 if !sym_path.exists() { return }; walk_dir(sym_path, target, include);
75 } else {
76 include.push((lz4_flex::block::compress_prepend_size(&safe_unwrap!(fs::read(&path) => RT008, path.to_string_lossy())).into_boxed_slice(), target));
77 }
78 }
79 }
80 }
81
82 Self {
83 conf_files: out.into_boxed_slice(),
84 include: include.into_boxed_slice(),
85 }
86 }
87
88 pub fn compile(&self, path: impl AsRef<std::path::Path>) { let buffer = BufWriter::new(safe_unwrap!(File::create(&path) => RT003, path.as_ref().to_string_lossy()));
90 safe_unwrap!(bincode::serialize_into(buffer, self) => RT003, path.as_ref().to_string_lossy());
91 }
92
93 pub fn load_compiled(path: impl AsRef<std::path::Path>) -> Self { let buffer = BufReader::new(safe_unwrap!(File::open(&path) => RT005, path.as_ref().to_string_lossy()));
95 safe_unwrap!(bincode::deserialize_from(buffer) => RT002, path.as_ref().to_string_lossy())
96 }
97
98 pub fn generate(&self) {
100 self.conf_files.iter()
102 .for_each(|x| x.generate());
103
104 for (contents, path) in self.include.iter() {
106 let _ = fs::create_dir_all(path.parent().unwrap_or(Path::new("")));
107 safe_unwrap!(fs::write(path,
108 safe_unwrap!(lz4_flex::block::decompress_size_prepended(contents)
109 => RT013, path.to_string_lossy()
110 )) => RT009, path.to_string_lossy()
111 );
112 }
113 }
114}
115
116#[derive(Debug, Serialize, Deserialize)]
117pub struct ConfFile {
118 pub conff_type: ConffType,
119 pub table: HashMap<Box<str>, Value>,
120 pub path: PathBuf,
121 pub shell: Box<[Box<[Box<str>]>]>,
122}
123
124impl ConfFile {
125 pub fn generate(&self) { use ConffType as C;
127 match self.conff_type {
128 C::Json => safe_unwrap!(json::generate(&self.path, &self.table) => RT006, self.path.to_string_lossy()),
129 C::Toml => safe_unwrap!(toml::generate(&self.path, &self.table) => RT006, self.path.to_string_lossy()),
130 C::Nix => safe_unwrap!(nix::generate(&self.path, &self.table) => RT006, self.path.to_string_lossy()),
131 }
132 self.execute_shell();
133 }
134
135 pub fn execute_shell(&self) { for cmd in self.shell.iter() {
137 let cmd_display = cmd.join(" ");
138
139 let mut user_out = String::new();
141 println!("\n{}", flexar::colour_format![
142 cyan("Are you sure you want to run this cmd `"),
143 none(&cmd_display),
144 cyan("`?\n"),
145 blue("("),
146 yellow("rewrite the command below"),
147 blue(")"),
148 ]);
149 print!("{}", flexar::colour_format![blue("-> ")]);
150 std::io::stdout().flush().unwrap();
151 std::io::stdin().read_line(&mut user_out).unwrap();
152
153 if user_out.trim() != cmd_display.trim() {
155 println!("{}", flexar::colour_format![
156 cyan("Shell commands "),
157 red("do not"),
158 cyan(" match, "),
159 yellow("skipping"),
160 cyan("...\n")
161 ]);
162 continue;
163 }
164
165 println!("{}", flexar::colour_format![
167 blue("\n==="),
168 cyan(" shell cmd `"),
169 none(&cmd_display),
170 cyan("` stdout start "),
171 blue("==="),
172 ]);
173
174 let status = safe_unwrap!(Command::new(cmd[0].as_ref())
175 .args(cmd[1..].iter().map(|x| x.as_ref()))
176 .status() => RT004, cmd_display);
177 if !status.success() {
178 return flexar::compiler_error!((RT001, Position::new_oneline("<shell>", &cmd_display, None))).throw();
179 }
180
181 println!("{}", flexar::colour_format![
182 blue("==="),
183 cyan(" shell cmd `"),
184 none(&cmd_display),
185 cyan("` stdout end "),
186 blue("===\n"),
187 ]);
188 }
189 }
190}
191
192#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
193pub enum ConffType {
194 Toml,
195 Json,
196 Nix,
197}
198
199impl ConffType {
200 pub fn parse(parxt: &mut Parxt<'_, Token>) -> Result<Node<Self>, (u8, CompileError)> {
201 if let Some(Token::Ident(ident)) = parxt.current() {
202 let out = Ok(
203 Node { position: parxt.position(), node: match ident.as_ref() {
204 "toml" => Self::Toml,
205 "json" => Self::Json,
206 "nix" => Self::Nix,
207 _ => return Err((1, compiler_error!((SY017, parxt.position()) parxt.current_token()))),
208 }
209 });
210 parxt.advance();
211 return out;
212 }
213
214 Err((0, compiler_error!((SY018, parxt.position()) parxt.current_token())))
215 }
216}