diff --git a/Cargo.lock b/Cargo.lock index 3b73717..8a7dd9d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,6 +8,56 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" +[[package]] +name = "anstream" +version = "0.6.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" + +[[package]] +name = "anstyle-parse" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys 0.61.2", +] + [[package]] name = "autocfg" version = "1.5.0" @@ -41,6 +91,52 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" +[[package]] +name = "clap" +version = "4.5.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9e340e012a1bf4935f5282ed1436d1489548e8f72308207ea5df0e23d2d03f8" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d76b5d13eaa18c901fd2f7fca939fefe3a0727a953561fefdf3b2922b8569d00" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.49" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a0b5487afeab2deb2ff4e03a807ad1a03ac532ff5a2cee5d86884440c7f7671" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d" + +[[package]] +name = "colorchoice" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" + [[package]] name = "compact_str" version = "0.8.1" @@ -213,9 +309,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" [[package]] -name = "garandos-tui" +name = "garandos_tui" version = "0.1.0" dependencies = [ + "clap", "crossterm 0.29.0", "ratatui", "rnix", @@ -272,6 +369,12 @@ dependencies = [ "syn", ] +[[package]] +name = "is_terminal_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" + [[package]] name = "itertools" version = "0.13.0" @@ -356,6 +459,12 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "once_cell_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" + [[package]] name = "parking_lot" version = "0.12.5" @@ -627,6 +736,12 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + [[package]] name = "wasi" version = "0.11.1+wasi-snapshot-preview1" diff --git a/Cargo.toml b/Cargo.toml index ea495e3..ef9d477 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,9 +1,10 @@ [package] -name = "garandos-tui" +name = "garandos_tui" version = "0.1.0" edition = "2024" [dependencies] +clap = { version = "4.5.53", features = ["derive"] } crossterm = "0.29.0" ratatui = "0.29.0" rnix = "0.12.0" diff --git a/src/app.rs b/src/app.rs index f1682fa..5f2e749 100644 --- a/src/app.rs +++ b/src/app.rs @@ -1,20 +1,124 @@ -use crossterm::{ - event, - event::{KeyCode, KeyEvent, KeyEventKind}, -}; +use crate::nix::{ConfigOption, ConfigSource}; +use crossterm::event::{self, KeyCode, KeyEvent, KeyEventKind}; use ratatui::{ DefaultTerminal, Frame, - prelude::{Buffer, Constraint, Layout, Rect, Stylize}, - style::{Color, Style}, - symbols::border, - text::Line, - widgets::{Block, Gauge, Widget}, + buffer::Buffer, + layout::{Constraint, Layout, Rect}, + style::Stylize, + text::{Line, Span}, + widgets::Widget, }; -use std::{io, sync::mpsc, thread, time::Duration}; +use std::{ + io::Result, + sync::mpsc::{self, Receiver}, +}; + +pub struct App { + pub exit: bool, + pub system_modules: Vec, + pub home_modules: Vec, + pub current_file: ConfigSource, + pub selected_index: usize, + pub last_action_status: String, +} pub enum Event { Input(KeyEvent), - Progress(f64), + EditFile, +} + +impl App { + pub fn run(&mut self, terminal: &mut DefaultTerminal, rx: Receiver) -> Result<()> { + while !self.exit { + let event: Event = match rx.recv() { + Ok(ev) => ev, + Err(_) => break, + }; + match event { + Event::Input(key_event) => self.handle_key_event(key_event)?, + // Event::EditFile => self.handle_file_edit_events()?, + _ => {} + } + terminal.draw(|frame: &mut Frame<'_>| self.draw(frame))?; + } + + Ok(()) + } + + fn draw(&self, frame: &mut Frame<'_>) { + frame.render_widget(self, frame.area()); + } + + fn handle_key_event(&mut self, key_event: KeyEvent) -> Result<()> { + // q - wyjście z programu + if key_event.kind == KeyEventKind::Press && key_event.code == KeyCode::Char('q') { + self.exit = true; + } + // ↑ - scroll w górę + else if key_event.kind == KeyEventKind::Press && key_event.code == KeyCode::Up { + self.selected_index = self.selected_index.saturating_sub(1); + } + // ↓ - scroll w dół + else if key_event.kind == KeyEventKind::Press && key_event.code == KeyCode::Down { + self.selected_index = self.selected_index.saturating_add(1); + } + // ENTER - zmień wartość boolen danej opcji + else if key_event.kind == KeyEventKind::Press && key_event.code == KeyCode::Enter { + } + // 1 - zmień plik na ConfigSource::System (pomiń wykonywaniej, jeżeli już jest wybrany) + else if key_event.kind == KeyEventKind::Press && key_event.code == KeyCode::Char('1') { + if self.current_file != ConfigSource::System { + self.current_file = ConfigSource::System; + } else { + self.current_file = ConfigSource::Home; + } + } + // 2 - zmień plik na ConfigSource::Home (pomiń wykonywaniej, jeżeli już jest wybrany) + else if key_event.kind == KeyEventKind::Press && key_event.code == KeyCode::Char('2') { + if self.current_file != ConfigSource::Home { + self.current_file = ConfigSource::Home; + } else { + self.current_file = ConfigSource::System; + } + } + + Ok(()) + } +} + +impl Widget for &App { + fn render(self, area: Rect, buf: &mut Buffer) + where + Self: Sized, + { + let vertical_layout: Layout = Layout::vertical([ + Constraint::Percentage(10), + Constraint::Percentage(80), + Constraint::Percentage(10), + ]); + + let [title_area, content_area, footer_area] = vertical_layout.areas(area); + + let (system_file, home_file) = match self.current_file { + ConfigSource::System => ("(System)".blue().bold(), "Home".reset()), + ConfigSource::Home => ("System".reset(), "(Home)".blue().bold()), + }; + let navbar: Line<'_> = Line::from(vec![ + Span::from("GarandOS TUI".bold().italic().yellow()), + Span::from(" * ".reset()), + system_file, + Span::from(" [1]".reset()), + Span::from(" * ".reset()), + home_file, + Span::from(" [2]".reset()), + Span::from(" *".reset()), + Span::from(" Quit".reset()), + Span::from(" [q]".reset()), + ]) + .left_aligned(); + + navbar.render(title_area, buf); + } } pub fn handle_input_events(tx: mpsc::Sender) { @@ -33,108 +137,3 @@ pub fn handle_input_events(tx: mpsc::Sender) { } } } - -pub fn run_background_thread(tx: mpsc::Sender) { - let mut progress: f64 = 0_f64; - const INCREMENT: f64 = 0.01_f64; - loop { - thread::sleep(Duration::from_millis(100)); - progress += INCREMENT; - progress = progress.min(1_f64); - if tx.send(Event::Progress(progress)).is_err() { - break; - } - } -} - -pub struct App { - pub exit: bool, - pub progress_bar_color: Color, - pub background_progress: f64, -} - -impl App { - pub fn run( - &mut self, - terminal: &mut DefaultTerminal, - rx: mpsc::Receiver, - ) -> io::Result<()> { - while !self.exit { - let event: Event = match rx.recv() { - Ok(ev) => ev, - Err(_) => break, - }; - match event { - Event::Input(key_event) => self.handle_key_event(key_event)?, - Event::Progress(progress) => self.background_progress = progress, - } - terminal.draw(|frame: &mut Frame<'_>| self.draw(frame))?; - } - - Ok(()) - } - - fn draw(&self, frame: &mut Frame<'_>) { - frame.render_widget(self, frame.area()); - } - - fn handle_key_event(&mut self, key_event: KeyEvent) -> io::Result<()> { - if key_event.kind == KeyEventKind::Press && key_event.code == KeyCode::Char('q') { - self.exit = true; - } else if key_event.kind == KeyEventKind::Press && key_event.code == KeyCode::Char('c') { - if self.progress_bar_color == Color::Green { - self.progress_bar_color = Color::Red; - } else { - self.progress_bar_color = Color::Green; - } - } - Ok(()) - } -} - -impl Widget for &App { - fn render(self, area: Rect, buf: &mut Buffer) - where - Self: Sized, - { - let vertical_layout: Layout = - Layout::vertical([Constraint::Percentage(20), Constraint::Percentage(80)]); - let [title_area, gauge_area] = vertical_layout.areas(area); - - Line::from("Process overview") - .bold() - .render(title_area, buf); - - let instructions: Line<'_> = Line::from(vec![ - " Change color ".into(), - "".blue().bold(), - " Quit ".into(), - "".blue().bold(), - ]) - .centered(); - - let block: Block<'_> = Block::bordered() - .title(Line::from(" Background processes ")) - .title_bottom(instructions) - .border_set(border::THICK); - - let progress_bar: Gauge<'_> = Gauge::default() - .gauge_style(Style::default().fg(self.progress_bar_color)) - .block(block) - .label(format!( - "Process Bar: {:.2}%", - self.background_progress * 100_f64 - )) - .ratio(self.background_progress); - - progress_bar.render( - Rect { - x: gauge_area.left(), - y: gauge_area.top(), - width: gauge_area.width, - height: 3, - }, - buf, - ); - } -} diff --git a/src/cli.rs b/src/cli.rs new file mode 100644 index 0000000..fe15034 --- /dev/null +++ b/src/cli.rs @@ -0,0 +1,64 @@ +use crate::nix::{ConfigOption, ConfigSource, collect_nix_options}; +use clap::Parser; +use rnix::{Parse, Root}; +use std::{fs, path::PathBuf}; + +#[derive(Parser, Debug)] +#[command( + version, + about = "Potrzebne pliki znajdziesz w ~/garandos/hosts//", + long_about = "Potrzebne pliki znajdziesz w ~/garandos/hosts//" +)] +pub struct Cli { + #[arg( + long, + help = "Ścieżka do pliku system-modules.nix", + value_name = "SYSTEM_MODULES" + )] + pub sf: PathBuf, + #[arg( + long, + help = "Ścieżka do pliku home-modules.nix", + value_name = "HOME_MODULES" + )] + pub hf: PathBuf, +} + +pub struct NixModules { + pub system_modules: Vec, + pub home_modules: Vec, +} + +pub fn get_modules() -> NixModules { + let args: Cli = Cli::parse(); + + let system_file: String = fs::read_to_string(&args.sf).expect("Failed to read system file"); + let home_file: String = fs::read_to_string(&args.hf).expect("Failed to read home file"); + + let system_ast: Parse = get_ast(&system_file); + let home_ast: Parse = get_ast(&home_file); + + let system_modules: Vec = + collect_nix_options(&system_ast.syntax(), "", ConfigSource::System); + let home_modules: Vec = + collect_nix_options(&home_ast.syntax(), "", ConfigSource::Home); + + NixModules { + system_modules, + home_modules, + } +} + +fn get_ast(file: &str) -> Parse { + let ast: Parse = Root::parse(&file); + + if !ast.errors().is_empty() { + eprintln!("Błędy parsowania:"); + for error in ast.errors() { + eprintln!(" - {}", error); + } + panic!("Błąd z parsowaniem pliku .nix") + } + + ast +} diff --git a/src/lib.rs b/src/lib.rs index f959912..027a671 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,2 +1,3 @@ pub mod app; +pub mod cli; pub mod nix; diff --git a/src/main.rs b/src/main.rs index e40a64e..7fb5ead 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,22 +1,25 @@ use garandos_tui::{ - app::{App, Event, handle_input_events, run_background_thread}, - // nix::{collect_nix_options, get_nix_value_by_path, toggle_bool_at_path}, + app::{App, Event, handle_input_events}, + cli::{NixModules, get_modules}, + nix::ConfigSource, }; -use ratatui::{Terminal, prelude::CrosstermBackend, style::Color}; -// use rnix::{Parse, Root, SyntaxNode}; -// use std::fs; +use ratatui::{Terminal, prelude::CrosstermBackend}; use std::{ - io, + io::{Result, Stdout}, sync::mpsc::{self, Sender}, thread, }; -fn main() -> io::Result<()> { - let mut terminal: Terminal> = ratatui::init(); +fn main() -> Result<()> { + let nix_modules: NixModules = get_modules(); + let mut terminal: Terminal> = ratatui::init(); let mut app: App = App { exit: false, - progress_bar_color: Color::Green, - background_progress: 0_f64, + system_modules: nix_modules.system_modules, + home_modules: nix_modules.home_modules, + current_file: ConfigSource::System, + selected_index: 0_usize, + last_action_status: "".to_string(), }; let (event_tx, event_rx) = mpsc::channel::(); @@ -26,56 +29,12 @@ fn main() -> io::Result<()> { handle_input_events(tx_to_input_events); }); - let tx_to_background_events: Sender = event_tx.clone(); - thread::spawn(move || { - run_background_thread(tx_to_background_events); - }); - - let app_result: Result<(), io::Error> = app.run(&mut terminal, event_rx); + // let tx_to_file_edit_events: Sender = event_tx.clone(); + // thread::spawn(move || { + // // handle_file_edit_events(tx_to_file_edit_events); + // }); + let app_result: Result<()> = app.run(&mut terminal, event_rx); ratatui::restore(); app_result } - -// fn main() { -// let content: String = -// fs::read_to_string("src/test.nix").expect("Nie można odczytać pliku src/test.nix"); - -// let ast: Parse = Root::parse(&content); - -// if !ast.errors().is_empty() { -// eprintln!("Błędy parsowania:"); -// for error in ast.errors() { -// eprintln!(" - {}", error); -// } -// eprintln!(); -// } - -// let node: SyntaxNode = ast.syntax(); -// let options: Vec<(String, String, bool)> = collect_nix_options(&node); - -// const OPTION: &str = "flatpak.enable"; - -// println!("Options:"); -// for (_, path, value) in options { -// if path == OPTION { -// println!("{} = {};", path, value); -// } -// } - -// // if let Some(value) = get_nix_value_by_path(&node, OPTION) { -// // println!("Flatseal ma wartość: {}", value); -// // } - -// let new_node: SyntaxNode = toggle_bool_at_path(&node, OPTION); -// let new_options: Vec<(String, String, bool)> = collect_nix_options(&new_node); - -// println!("\nNew Options:"); -// for (_, path, value) in new_options { -// if path == OPTION { -// println!("{} = {};", path, value); -// } -// } - -// fs::write("src/test.nix", new_node.to_string()).expect("Nie można zapisać tego pliku"); -// } diff --git a/src/nix.rs b/src/nix.rs index 51a16df..f15d361 100644 --- a/src/nix.rs +++ b/src/nix.rs @@ -1,46 +1,26 @@ use rnix::{NodeOrToken, Root, SyntaxKind, SyntaxNode, SyntaxToken, TextRange, TextSize}; use std::collections::HashMap; -/// Zbiera wszystkie opcje typu `bool` z drzewa składniowego Nix. -/// -/// Jest to niewielka funkcja‑wrapper, która deleguje właściwe zebranie opcji do -/// `collect_nix_options_with_path`, przekazując jako początkową ścieżkę i kategorię -/// pusty ciąg znaków. -/// -/// # Argumenty -/// -/// * `node` – węzeł korzenia drzewa składniowego, od którego rozpoczynamy przeszukiwanie. -/// -/// # Zwraca -/// -/// `Vec<(String, String, bool)>`, gdzie: -/// - pierwszy element (`String`) to nazwa kategorii (pobrana z najbliższego -/// blokowego komentarza `/* … */`); -/// - drugi element (`String`) to pełna ścieżka do opcji (np. `services.ssh.enable`); -/// - trzeci element (`bool`) to wartość logiczna tej opcji. -/// -/// # Przykład -/// -/// ``` -/// use rnix::{Root, SyntaxNode}; -/// // przykładowe źródło Nix -/// let src = r#" -/// /* Services */ -/// services.ssh.enable = true; -/// services.httpd.enable = false; -/// "#; -/// -/// // parsujemy źródło -/// let ast = Root::parse(src); -/// -/// // zbieramy wszystkie opcje bool -/// let options = collect_nix_options(&ast.syntax()); -/// assert_eq!(options.len(), 2); -/// assert!(options.iter().any(|(_, path, val)| path == "services.ssh.enable" && *val)); -/// assert!(options.iter().any(|(_, path, val)| path == "services.httpd.enable" && !*val)); -/// ``` -pub fn collect_nix_options(node: &SyntaxNode) -> Vec<(String, String, bool)> { - collect_nix_options_with_path(node, "", "") +pub struct ConfigOption { + pub category: String, + pub path: String, + pub value: bool, + pub source: ConfigSource, +} + +#[derive(PartialEq)] +pub enum ConfigSource { + System, + Home, +} + +impl Clone for ConfigSource { + fn clone(&self) -> Self { + match self { + ConfigSource::System => ConfigSource::System, + ConfigSource::Home => ConfigSource::Home, + } + } } /// Pobiera wartość logiczną opcji Nix wskazanej pełną ścieżką. @@ -72,11 +52,11 @@ pub fn collect_nix_options(node: &SyntaxNode) -> Vec<(String, String, bool)> { /// assert_eq!(missing, None); /// ``` pub fn get_nix_value_by_path(node: &SyntaxNode, query_path: &str) -> Option { - let options: Vec<(String, String, bool)> = collect_nix_options(node); + let options: Vec = collect_nix_options(node, "", ConfigSource::System); let map: HashMap<&str, bool> = options .iter() - .map(|(_, path, val)| (path.as_str(), *val)) + .map(|option| (option.path.as_str(), option.value)) .collect(); map.get(query_path).copied() @@ -199,10 +179,6 @@ pub fn toggle_bool_at_path(node: &SyntaxNode, query_path: &str) -> SyntaxNode { /// typu `bool`, uwzględniając aktualną ścieżkę oraz kategorię (pobrane z /// najbliższego blokowego komentarza). /// -/// Funkcja jest wewnętrzną implementacją używaną przez `collect_nix_options`. -/// Nie jest częścią publicznego API, ale jej zachowanie jest opisane poniżej, -/// aby ułatwić utrzymanie kodu. -/// /// # Argumenty /// /// * `node` – bieżący węzeł drzewa składniowego. @@ -213,15 +189,35 @@ pub fn toggle_bool_at_path(node: &SyntaxNode, query_path: &str) -> SyntaxNode { /// /// # Zwraca /// -/// `Vec<(String, String, bool)>` – wektor trójek `(kategoria, pełna_ścieżka, -/// wartość_bool)`. -fn collect_nix_options_with_path( +/// `Vec` – wektor opcji z kategorią, pełną ścieżką i wartością bool. +/// +/// # Przykład +/// +/// ``` +/// use rnix::{Root, SyntaxNode}; +/// // przykładowe źródło Nix +/// let src = r#" +/// /* Services */ +/// services.ssh.enable = true; +/// services.httpd.enable = false; +/// "#; +/// +/// // parsujemy źródło +/// let ast = Root::parse(src); +/// +/// // zbieramy wszystkie opcje bool +/// let options = collect_nix_options(&ast.syntax(), "", ConfigSource::System); +/// assert_eq!(options.len(), 2); +/// assert!(options.iter().any(|option| option.path == "services.ssh.enable" && option.value)); +/// assert!(options.iter().any(|option| option.path == "services.httpd.enable" && !option.value)); +/// ``` +pub fn collect_nix_options( node: &SyntaxNode, current_path: &str, - current_category: &str, -) -> Vec<(String, String, bool)> { - let mut result: Vec<(String, String, bool)> = Vec::new(); - let mut category: String = current_category.to_string(); + current_source: ConfigSource, +) -> Vec { + let mut result: Vec = Vec::new(); + let mut category: String = String::new(); let children: Vec> = node.children_with_tokens().collect(); @@ -263,10 +259,10 @@ fn collect_nix_options_with_path( } else { format!("{}.{}", current_path, attr_path) }; - result.extend(collect_nix_options_with_path( + result.extend(collect_nix_options( &grand_node, &new_path, - &category, + current_source.clone(), )); } _ => {} @@ -281,15 +277,20 @@ fn collect_nix_options_with_path( format!("{}.{}", current_path, attr_path) }; let bool_value: bool = value_node.text() == "true"; - result.push((category.clone(), full_path, bool_value)); + result.push(ConfigOption { + category: category.clone(), + path: full_path, + value: bool_value, + source: current_source.clone(), + }); } } NodeOrToken::Node(child_node) => { - result.extend(collect_nix_options_with_path( + result.extend(collect_nix_options( child_node, current_path, - &category, + current_source.clone(), )); } diff --git a/src/placeholder.txt b/src/placeholder.txt new file mode 100644 index 0000000..adc3b8b --- /dev/null +++ b/src/placeholder.txt @@ -0,0 +1,224 @@ +// main.rs +use clap::Parser; +use garandos_tui::{ + cli::Cli, + // app::{App, Event, handle_input_events, run_background_thread}, + nix::{ + ConfigOption, ConfigSource, collect_nix_options, get_nix_value_by_path, toggle_bool_at_path, + }, +}; +// use ratatui::{Terminal, prelude::CrosstermBackend, style::Color}; +use rnix::{Parse, Root, SyntaxNode}; +use std::fs; +// use std::{ +// io, +// sync::mpsc::{self, Sender}, +// thread, +// }; + +// fn main() -> io::Result<()> { +// let mut terminal: Terminal> = ratatui::init(); +// let mut app: App = App { +// exit: false, +// progress_bar_color: Color::Green, +// background_progress: 0_f64, +// }; + +// let (event_tx, event_rx) = mpsc::channel::(); + +// let tx_to_input_events: Sender = event_tx.clone(); +// thread::spawn(move || { +// handle_input_events(tx_to_input_events); +// }); + +// let tx_to_background_events: Sender = event_tx.clone(); +// thread::spawn(move || { +// run_background_thread(tx_to_background_events); +// }); + +// let app_result: Result<(), io::Error> = app.run(&mut terminal, event_rx); + +// ratatui::restore(); +// app_result +// } + +// fn main() { +// let args: Cli = Cli::parse(); + +// let system_modules_content: String = +// fs::read_to_string(args.system_file).expect("Nie można odczytać pliku {args.system_file}"); + +// let home_modules_content: String = +// fs::read_to_string(args.home_file).expect("Nie można odczytać pliku {args.home_file}"); + +// let ast: Parse = Root::parse(&home_modules_content); + +// if !ast.errors().is_empty() { +// eprintln!("Błędy parsowania:"); +// for error in ast.errors() { +// eprintln!(" - {}", error); +// } +// eprintln!(); +// } + +// let node: SyntaxNode = ast.syntax(); +// let options: Vec = collect_nix_options(&node, "", ConfigSource::System); + +// const OPTION: &str = "flatpak.enable"; + +// println!("Options:"); +// for option in options { +// // if option.path == OPTION { +// println!("{} = {};", option.path, option.value); +// // } +// } + +// // if let Some(value) = get_nix_value_by_path(&node, OPTION) { +// // println!("\n{OPTION}: {}", value); +// // } + +// // let new_node: SyntaxNode = toggle_bool_at_path(&node, OPTION); +// // let new_options: Vec = collect_nix_options(&new_node, "", ConfigSource::System); + +// // println!("\nNew Options:"); +// // for option in new_options { +// // if option.path == OPTION { +// // println!("{} = {};", option.path, option.value); +// // } +// // } + +// // fs::write("src/test.nix", new_node.to_string()).expect("Nie można zapisać tego pliku"); +// } + +// app.rs + +// use core::option::Option; +// use crossterm::{ +// event, +// event::{KeyCode, KeyEvent, KeyEventKind}, +// }; +// use ratatui::{ +// DefaultTerminal, Frame, +// prelude::{Buffer, Constraint, Layout, Rect, Stylize}, +// style::{Color, Style}, +// symbols::border, +// text::Line, +// widgets::{Block, Gauge, Widget}, +// }; +// use std::{io, sync::mpsc, thread, time::Duration}; + +// pub fn handle_input_events(tx: mpsc::Sender) { +// loop { +// match event::read() { +// Ok(ev) => { +// if let event::Event::Key(key_event) = ev { +// if tx.send(Event::Input(key_event)).is_err() { +// break; +// } +// } +// } +// Err(_) => { +// continue; +// } +// } +// } +// } + +// pub fn run_background_thread(tx: mpsc::Sender) { +// let mut progress: f64 = 0_f64; +// const INCREMENT: f64 = 0.01_f64; +// loop { +// thread::sleep(Duration::from_millis(100)); +// progress += INCREMENT; +// progress = progress.min(1_f64); +// if tx.send(Event::Progress(progress)).is_err() { +// break; +// } +// } +// } + +// impl App { +// pub fn run( +// &mut self, +// terminal: &mut DefaultTerminal, +// rx: mpsc::Receiver, +// ) -> io::Result<()> { +// while !self.exit { +// let event: Event = match rx.recv() { +// Ok(ev) => ev, +// Err(_) => break, +// }; +// match event { +// Event::Input(key_event) => self.handle_key_event(key_event)?, +// Event::Progress(progress) => self.background_progress = progress, +// } +// terminal.draw(|frame: &mut Frame<'_>| self.draw(frame))?; +// } + +// Ok(()) +// } + +// fn draw(&self, frame: &mut Frame<'_>) { +// frame.render_widget(self, frame.area()); +// } + +// fn handle_key_event(&mut self, key_event: KeyEvent) -> io::Result<()> { +// if key_event.kind == KeyEventKind::Press && key_event.code == KeyCode::Char('q') { +// self.exit = true; +// } else if key_event.kind == KeyEventKind::Press && key_event.code == KeyCode::Char('c') { +// if self.progress_bar_color == Color::Green { +// self.progress_bar_color = Color::Red; +// } else { +// self.progress_bar_color = Color::Green; +// } +// } +// Ok(()) +// } +// } + +// impl Widget for &App { +// fn render(self, area: Rect, buf: &mut Buffer) +// where +// Self: Sized, +// { +// let vertical_layout: Layout = +// Layout::vertical([Constraint::Percentage(20), Constraint::Percentage(80)]); +// let [title_area, gauge_area] = vertical_layout.areas(area); + +// Line::from("Process overview") +// .bold() +// .render(title_area, buf); + +// let instructions: Line<'_> = Line::from(vec![ +// " Change color ".into(), +// "".blue().bold(), +// " Quit ".into(), +// "".blue().bold(), +// ]) +// .centered(); + +// let block: Block<'_> = Block::bordered() +// .title(Line::from(" Background processes ")) +// .title_bottom(instructions) +// .border_set(border::THICK); + +// let progress_bar: Gauge<'_> = Gauge::default() +// .gauge_style(Style::default().fg(self.progress_bar_color)) +// .block(block) +// .label(format!( +// "Process Bar: {:.2}%", +// self.background_progress * 100_f64 +// )) +// .ratio(self.background_progress); + +// progress_bar.render( +// Rect { +// x: gauge_area.left(), +// y: gauge_area.top(), +// width: gauge_area.width, +// height: 3, +// }, +// buf, +// ); +// } +// }