gostui-setup #1
119
src/app.rs
119
src/app.rs
@@ -1,4 +1,6 @@
|
||||
use crate::nix::{ConfigOption, ConfigSource};
|
||||
use crate::nix::{
|
||||
ConfigOption, ConfigSource, collect_nix_options, get_nix_value_by_path, toggle_bool_at_path,
|
||||
};
|
||||
use crossterm::event::{self, KeyCode, KeyEvent, KeyEventKind};
|
||||
use ratatui::{
|
||||
DefaultTerminal, Frame,
|
||||
@@ -6,15 +8,22 @@ use ratatui::{
|
||||
layout::{Constraint, Layout, Rect},
|
||||
style::Stylize,
|
||||
text::{Line, Span},
|
||||
widgets::Widget,
|
||||
widgets::{Paragraph, Widget},
|
||||
};
|
||||
use rnix::{Parse, Root, SyntaxNode};
|
||||
use std::{
|
||||
fs,
|
||||
io::Result,
|
||||
path::PathBuf,
|
||||
sync::mpsc::{self, Receiver},
|
||||
};
|
||||
|
||||
pub struct App {
|
||||
pub exit: bool,
|
||||
pub system_path: PathBuf,
|
||||
pub home_path: PathBuf,
|
||||
pub system_ast: Parse<Root>,
|
||||
pub home_ast: Parse<Root>,
|
||||
pub system_modules: Vec<ConfigOption>,
|
||||
pub home_modules: Vec<ConfigOption>,
|
||||
pub current_file: ConfigSource,
|
||||
@@ -24,7 +33,7 @@ pub struct App {
|
||||
|
||||
pub enum Event {
|
||||
Input(KeyEvent),
|
||||
EditFile,
|
||||
// EditFile,
|
||||
}
|
||||
|
||||
impl App {
|
||||
@@ -37,7 +46,7 @@ impl App {
|
||||
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))?;
|
||||
}
|
||||
@@ -54,6 +63,14 @@ impl App {
|
||||
if key_event.kind == KeyEventKind::Press && key_event.code == KeyCode::Char('q') {
|
||||
self.exit = true;
|
||||
}
|
||||
// 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') {
|
||||
self.current_file = ConfigSource::System;
|
||||
}
|
||||
// 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') {
|
||||
self.current_file = ConfigSource::Home;
|
||||
}
|
||||
// ↑ - 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);
|
||||
@@ -64,21 +81,53 @@ impl App {
|
||||
}
|
||||
// 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;
|
||||
let modules: &Vec<ConfigOption> = match self.current_file {
|
||||
ConfigSource::System => &self.system_modules.clone(),
|
||||
ConfigSource::Home => &self.home_modules.clone(),
|
||||
};
|
||||
let node: &SyntaxNode = match self.current_file {
|
||||
ConfigSource::System => &self.system_ast.syntax(),
|
||||
ConfigSource::Home => &self.home_ast.syntax(),
|
||||
};
|
||||
if let Some(module) = modules.get(self.selected_index) {
|
||||
let new_node: SyntaxNode = toggle_bool_at_path(node, module.path.as_str());
|
||||
|
||||
let new_ast: Parse<Root> = Root::parse(&new_node.to_string().as_str());
|
||||
|
||||
let new_modules: Vec<ConfigOption> =
|
||||
collect_nix_options(&new_node, "", self.current_file.clone());
|
||||
|
||||
let new_module_value: bool =
|
||||
if let Some(value) = get_nix_value_by_path(&new_node, module.path.as_str()) {
|
||||
value
|
||||
} else {
|
||||
self.current_file = ConfigSource::Home;
|
||||
module.value.clone()
|
||||
};
|
||||
|
||||
self.last_action_status = if let Some(module) = modules.get(self.selected_index) {
|
||||
match self.current_file {
|
||||
ConfigSource::System => {
|
||||
self.system_modules = new_modules;
|
||||
self.system_ast = new_ast;
|
||||
fs::write(&self.system_path, new_node.to_string())
|
||||
.expect("Nie można zapisać pliku modułów systemowych");
|
||||
}
|
||||
ConfigSource::Home => {
|
||||
self.home_modules = new_modules;
|
||||
self.home_ast = new_ast;
|
||||
fs::write(&self.home_path, new_node.to_string())
|
||||
.expect("Nie można zapisać pliku modułów domowych");
|
||||
}
|
||||
// 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;
|
||||
};
|
||||
format!(
|
||||
"{} = {} -> {}",
|
||||
module.path.clone(),
|
||||
module.value.clone(),
|
||||
new_module_value
|
||||
)
|
||||
} else {
|
||||
self.current_file = ConfigSource::System;
|
||||
"Nothing changed".to_string()
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,18 +141,19 @@ impl Widget for &App {
|
||||
Self: Sized,
|
||||
{
|
||||
let vertical_layout: Layout = Layout::vertical([
|
||||
Constraint::Percentage(10),
|
||||
Constraint::Percentage(80),
|
||||
Constraint::Percentage(10),
|
||||
Constraint::Percentage(5),
|
||||
Constraint::Percentage(90),
|
||||
Constraint::Percentage(5),
|
||||
]);
|
||||
|
||||
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![
|
||||
let title: Line<'_> = Line::from(vec![
|
||||
Span::from("GarandOS TUI".bold().italic().yellow()),
|
||||
Span::from(" * ".reset()),
|
||||
system_file,
|
||||
@@ -117,7 +167,36 @@ impl Widget for &App {
|
||||
])
|
||||
.left_aligned();
|
||||
|
||||
navbar.render(title_area, buf);
|
||||
title.render(title_area, buf);
|
||||
}
|
||||
|
||||
{
|
||||
let footer_line1: Line<'_> = Line::from(vec![
|
||||
Span::from("Use "),
|
||||
Span::from("↑/↓".italic().green()),
|
||||
Span::from(" to navigate, "),
|
||||
Span::from("Enter".bold().white()),
|
||||
Span::from(" to toggle, "),
|
||||
Span::from("1/2".bold().blue()),
|
||||
Span::from(" to switch file, "),
|
||||
Span::from("q".bold().red()),
|
||||
Span::from(" to quit."),
|
||||
]);
|
||||
let last_action: Span<'_> = if self.last_action_status.is_empty() {
|
||||
Span::from("Nothing changed")
|
||||
} else {
|
||||
self.last_action_status.clone().into()
|
||||
};
|
||||
let footer_line2: Line<'_> = Line::from(vec![
|
||||
Span::from("Last action: "),
|
||||
last_action.italic().green(),
|
||||
Span::from("."),
|
||||
]);
|
||||
let footer: Paragraph<'_> =
|
||||
Paragraph::new(vec![footer_line1, footer_line2]).left_aligned();
|
||||
|
||||
footer.render(footer_area, buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
41
src/cli.rs
41
src/cli.rs
@@ -1,7 +1,6 @@
|
||||
use crate::nix::{ConfigOption, ConfigSource, collect_nix_options};
|
||||
use crate::nix::{NixModules, build_nix_modules};
|
||||
use clap::Parser;
|
||||
use rnix::{Parse, Root};
|
||||
use std::{fs, path::PathBuf};
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(
|
||||
@@ -24,41 +23,7 @@ pub struct Cli {
|
||||
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
|
||||
build_nix_modules(args.sf, args.hf)
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use garandos_tui::{
|
||||
app::{App, Event, handle_input_events},
|
||||
cli::{NixModules, get_modules},
|
||||
nix::ConfigSource,
|
||||
cli::get_modules,
|
||||
nix::{ConfigSource, NixModules},
|
||||
};
|
||||
use ratatui::{Terminal, prelude::CrosstermBackend};
|
||||
use std::{
|
||||
@@ -15,6 +15,10 @@ fn main() -> Result<()> {
|
||||
let mut terminal: Terminal<CrosstermBackend<Stdout>> = ratatui::init();
|
||||
let mut app: App = App {
|
||||
exit: false,
|
||||
system_path: nix_modules.system_path,
|
||||
home_path: nix_modules.home_path,
|
||||
system_ast: nix_modules.system_ast,
|
||||
home_ast: nix_modules.home_ast,
|
||||
system_modules: nix_modules.system_modules,
|
||||
home_modules: nix_modules.home_modules,
|
||||
current_file: ConfigSource::System,
|
||||
|
||||
51
src/nix.rs
51
src/nix.rs
@@ -1,6 +1,16 @@
|
||||
use rnix::{NodeOrToken, Root, SyntaxKind, SyntaxNode, SyntaxToken, TextRange, TextSize};
|
||||
use std::collections::HashMap;
|
||||
use rnix::{NodeOrToken, Parse, Root, SyntaxKind, SyntaxNode, SyntaxToken, TextRange, TextSize};
|
||||
use std::{collections::HashMap, fs, path::PathBuf};
|
||||
|
||||
pub struct NixModules {
|
||||
pub system_path: PathBuf,
|
||||
pub system_ast: Parse<Root>,
|
||||
pub system_modules: Vec<ConfigOption>,
|
||||
pub home_path: PathBuf,
|
||||
pub home_ast: Parse<Root>,
|
||||
pub home_modules: Vec<ConfigOption>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ConfigOption {
|
||||
pub category: String,
|
||||
pub path: String,
|
||||
@@ -8,18 +18,43 @@ pub struct ConfigOption {
|
||||
pub source: ConfigSource,
|
||||
}
|
||||
|
||||
#[derive(PartialEq)]
|
||||
#[derive(PartialEq, Clone)]
|
||||
pub enum ConfigSource {
|
||||
System,
|
||||
Home,
|
||||
}
|
||||
|
||||
impl Clone for ConfigSource {
|
||||
fn clone(&self) -> Self {
|
||||
match self {
|
||||
ConfigSource::System => ConfigSource::System,
|
||||
ConfigSource::Home => ConfigSource::Home,
|
||||
pub fn parse_nix(src: &str) -> Parse<Root> {
|
||||
let ast: Parse<Root> = Root::parse(src);
|
||||
if !ast.errors().is_empty() {
|
||||
eprintln!("Błędy parsowania:");
|
||||
for err in ast.errors() {
|
||||
eprintln!(" - {}", err);
|
||||
}
|
||||
panic!("Błąd parsowania pliku .nix");
|
||||
}
|
||||
ast
|
||||
}
|
||||
|
||||
pub fn build_nix_modules(system_path: PathBuf, home_path: PathBuf) -> NixModules {
|
||||
let system_src: String = fs::read_to_string(&system_path).expect("Failed to read system file");
|
||||
let home_src: String = fs::read_to_string(&home_path).expect("Failed to read home file");
|
||||
|
||||
let system_ast: Parse<Root> = parse_nix(&system_src);
|
||||
let home_ast: Parse<Root> = parse_nix(&home_src);
|
||||
|
||||
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_path,
|
||||
system_ast,
|
||||
system_modules,
|
||||
home_path,
|
||||
home_ast,
|
||||
home_modules,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user