gostui-setup #1
117
Cargo.lock
generated
117
Cargo.lock
generated
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
231
src/app.rs
231
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<ConfigOption>,
|
||||
pub home_modules: Vec<ConfigOption>,
|
||||
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<Event>) -> 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<Event>) {
|
||||
@@ -33,108 +137,3 @@ pub fn handle_input_events(tx: mpsc::Sender<Event>) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run_background_thread(tx: mpsc::Sender<Event>) {
|
||||
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<Event>,
|
||||
) -> 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(),
|
||||
"<C>".blue().bold(),
|
||||
" Quit ".into(),
|
||||
"<Q>".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,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
64
src/cli.rs
Normal file
64
src/cli.rs
Normal file
@@ -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/<Twój-Host>/",
|
||||
long_about = "Potrzebne pliki znajdziesz w ~/garandos/hosts/<Twój-Host>/"
|
||||
)]
|
||||
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<ConfigOption>,
|
||||
pub home_modules: Vec<ConfigOption>,
|
||||
}
|
||||
|
||||
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<Root> = get_ast(&system_file);
|
||||
let home_ast: Parse<Root> = get_ast(&home_file);
|
||||
|
||||
let system_modules: Vec<ConfigOption> =
|
||||
collect_nix_options(&system_ast.syntax(), "", ConfigSource::System);
|
||||
let home_modules: Vec<ConfigOption> =
|
||||
collect_nix_options(&home_ast.syntax(), "", ConfigSource::Home);
|
||||
|
||||
NixModules {
|
||||
system_modules,
|
||||
home_modules,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_ast(file: &str) -> Parse<Root> {
|
||||
let ast: Parse<Root> = 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
|
||||
}
|
||||
@@ -1,2 +1,3 @@
|
||||
pub mod app;
|
||||
pub mod cli;
|
||||
pub mod nix;
|
||||
|
||||
77
src/main.rs
77
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<CrosstermBackend<io::Stdout>> = ratatui::init();
|
||||
fn main() -> Result<()> {
|
||||
let nix_modules: NixModules = get_modules();
|
||||
let mut terminal: Terminal<CrosstermBackend<Stdout>> = 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::<Event>();
|
||||
@@ -26,56 +29,12 @@ fn main() -> io::Result<()> {
|
||||
handle_input_events(tx_to_input_events);
|
||||
});
|
||||
|
||||
let tx_to_background_events: Sender<Event> = 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> = 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> = 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");
|
||||
// }
|
||||
|
||||
117
src/nix.rs
117
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<bool> {
|
||||
let options: Vec<(String, String, bool)> = collect_nix_options(node);
|
||||
let options: Vec<ConfigOption> = 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<ConfigOption>` – 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<ConfigOption> {
|
||||
let mut result: Vec<ConfigOption> = Vec::new();
|
||||
let mut category: String = String::new();
|
||||
|
||||
let children: Vec<NodeOrToken<SyntaxNode, SyntaxToken>> = 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(),
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
224
src/placeholder.txt
Normal file
224
src/placeholder.txt
Normal file
@@ -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<CrosstermBackend<io::Stdout>> = 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::<Event>();
|
||||
|
||||
// let tx_to_input_events: Sender<Event> = event_tx.clone();
|
||||
// thread::spawn(move || {
|
||||
// handle_input_events(tx_to_input_events);
|
||||
// });
|
||||
|
||||
// let tx_to_background_events: Sender<Event> = 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> = 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<ConfigOption> = 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<ConfigOption> = 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<Event>) {
|
||||
// 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<Event>) {
|
||||
// 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<Event>,
|
||||
// ) -> 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(),
|
||||
// "<C>".blue().bold(),
|
||||
// " Quit ".into(),
|
||||
// "<Q>".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,
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
Reference in New Issue
Block a user