diff --git a/Cargo.lock b/Cargo.lock index 137f179..3b73717 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,6 +8,12 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + [[package]] name = "bitflags" version = "2.10.0" @@ -58,6 +64,12 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "countme" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7704b5fdd17b18ae31c4c1da5a2e0305a2bf17b5249300a9ee9ed7b72114c636" + [[package]] name = "crossterm" version = "0.28.1" @@ -206,8 +218,15 @@ version = "0.1.0" dependencies = [ "crossterm 0.29.0", "ratatui", + "rnix", ] +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" + [[package]] name = "hashbrown" version = "0.15.5" @@ -313,7 +332,16 @@ version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" dependencies = [ - "hashbrown", + "hashbrown 0.15.5", +] + +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", ] [[package]] @@ -405,6 +433,34 @@ dependencies = [ "bitflags", ] +[[package]] +name = "rnix" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f15e00b0ab43abd70d50b6f8cd021290028f9b7fdd7cdfa6c35997173bc1ba9" +dependencies = [ + "rowan", +] + +[[package]] +name = "rowan" +version = "0.15.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4f1e4a001f863f41ea8d0e6a0c34b356d5b733db50dadab3efef640bafb779b" +dependencies = [ + "countme", + "hashbrown 0.14.5", + "memoffset", + "rustc-hash", + "text-size", +] + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + [[package]] name = "rustix" version = "0.38.44" @@ -530,6 +586,12 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "text-size" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f18aa187839b2bdb1ad2fa35ead8c4c2976b64e4363c386d45ac0f7ee85c9233" + [[package]] name = "unicode-ident" version = "1.0.22" diff --git a/Cargo.toml b/Cargo.toml index abd0dc5..ea495e3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,3 +6,4 @@ edition = "2024" [dependencies] crossterm = "0.29.0" ratatui = "0.29.0" +rnix = "0.12.0" diff --git a/src/main.rs b/src/main.rs index 699dc96..696b5bc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,167 +1,24 @@ -use crossterm::{ - event, - event::{KeyCode, KeyEvent, KeyEventKind}, -}; -use ratatui::{ - DefaultTerminal, Frame, Terminal, - prelude::{Buffer, Constraint, CrosstermBackend, Layout, Rect, Stylize}, - style::{Color, Style}, - symbols::border, - text::Line, - widgets::{Block, Gauge, Widget}, -}; -use std::{ - io, - sync::mpsc::{self, Sender}, - thread, - time::Duration, -}; +use rnix::{Parse, Root}; +use std::fs; -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, - }; +mod nix; - let (event_tx, event_rx) = mpsc::channel::(); +fn main() { + let content: String = + fs::read_to_string("src/test.nix").expect("Nie można odczytać pliku src/test.nix"); - let tx_to_input_events: Sender = event_tx.clone(); - thread::spawn(move || { - handle_input_events(tx_to_input_events); - }); + let ast: Parse = Root::parse(&content); - 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 -} - -enum Event { - Input(KeyEvent), - Progress(f64), -} - -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; - } + if !ast.errors().is_empty() { + eprintln!("Błędy parsowania:"); + for error in ast.errors() { + eprintln!(" - {}", error); } - } -} - -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 { - exit: bool, - progress_bar_color: Color, - background_progress: f64, -} - -impl App { - 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, - ); + eprintln!(); + } + + let options: Vec<(String, bool)> = nix::collect_nix_options_with_path(&ast.syntax(), ""); + for (path, value) in options { + println!("{} = {};", path, value); } } diff --git a/src/nix.rs b/src/nix.rs new file mode 100644 index 0000000..cfb3f79 --- /dev/null +++ b/src/nix.rs @@ -0,0 +1,68 @@ +use rnix::{NodeOrToken, SyntaxKind, SyntaxNode}; + +pub fn collect_nix_options_with_path(node: &SyntaxNode, current_path: &str) -> Vec<(String, bool)> { + let mut result: Vec<(String, bool)> = Vec::new(); + + for child in node.children_with_tokens() { + if let NodeOrToken::Node(child_node) = child { + match child_node.kind() { + SyntaxKind::NODE_ATTRPATH_VALUE => { + let mut attr_path: String = String::new(); + let mut value_node: Option = None; + + for grand in child_node.children_with_tokens() { + if let NodeOrToken::Node(grand_node) = grand { + match grand_node.kind() { + SyntaxKind::NODE_ATTRPATH => { + attr_path = grand_node.text().to_string(); + } + SyntaxKind::NODE_IDENT | SyntaxKind::NODE_LITERAL => { + let text: String = grand_node.text().to_string(); + if text == "true" || text == "false" { + value_node = Some(grand_node); + } + } + SyntaxKind::NODE_ATTR_SET => { + let new_path: String = if current_path.is_empty() { + attr_path.clone() + } else { + format!("{}.{}", current_path, attr_path) + }; + + result.extend(collect_nix_options_with_path( + &grand_node, + &new_path, + )); + } + _ => {} + } + } + } + + if let Some(value_node) = value_node { + let full_path: String = if current_path.is_empty() { + attr_path + } else { + format!("{}.{}", current_path, attr_path) + }; + + let value_text: String = value_node.text().to_string(); + let mut bool_value: bool = false; + + if value_text == "true" { + bool_value = true; + } + + result.push((full_path, bool_value)); + } + } + + _ => { + result.extend(collect_nix_options_with_path(&child_node, current_path)); + } + } + } + } + + result +} diff --git a/src/test.nix b/src/test.nix new file mode 100644 index 0000000..d3f98b4 --- /dev/null +++ b/src/test.nix @@ -0,0 +1,76 @@ +_: { + /* + Container & Packaging + */ + docker.enable = true; # Docker: container runtime and management + flatpak = { + enable = true; # Flatpak: universal packaging system for Linux + packages = { + sober = false; # Roblox client + warehouse = true; # Flatpak manager + flatseal = true; # Flatpak permissions manager + }; + }; + + /* + Gaming + */ + gamemode.enable = true; # GameMode: optimizes system performance for gaming + gamescope.enable = false; # Gamescope: micro‑compositor for games + steam.enable = true; # Steam: platform for buying and playing games + + packages = { + /* + Container & Packaging + */ + distrobox.enable = false; # Distrobox: containerized development environments + lazydocker.enable = false; # Lazydocker: simple TUI for Docker + + /* + Gaming + */ + prismlauncher.enable = false; # Prism Launcher: Minecraft modded launcher + spaceCadetPinball.enable = true; # SpaceCadet Pinball: classic pinball game + ttySolitaire.enable = true; # TTY Solitaire: terminal‑based solitaire game + + /* + Development Tools + */ + exercism.enable = true; # Exercism: coding practice platform + lazygit.enable = false; # Lazygit: simple TUI for Git + opencode.enable = true; # OpenCode: tools for coding and development + jan.enable = true; # Jan: AI chat UI + + /* + Communication & Collaboration + */ + mattermost.enable = true; # Mattermost: open‑source Slack alternative + slack.enable = true; # Slack: team communication and collaboration tool + tutanota.enable = true; # Tutanota: secure email client + + /* + Productivity / Knowledge Management + */ + bitwarden.enable = false; # Bitwarden: password manager (desktop) + iotas.enable = true; # Iotas: lightweight notes manager + logseq.enable = false; # Logseq: knowledge base and outliner + + /* + Media & Graphics + */ + affinity.enable = false; # Affinity: professional graphics suite + eyeOfGnome.enable = true; # Eye of GNOME: image viewer + freetube.enable = false; # FreeTube: privacy‑friendly YouTube client + gimp.enable = false; # GIMP: GNU Image Manipulation Program + kdenlive.enable = false; # Kdenlive: video editing software + plex.enable = true; # Plex: media player and server client + + /* + Utilities / Misc + */ + eddieAirVPN.enable = true; # Eddie AirVPN: VPN client + galculator.enable = true; # Galculator: simple calculator + gedit.enable = false; # Gedit: GNOME text editor + winboat.enable = false; # Winboat: Windows remote desktop via RDP + }; +}