added distrobox logic, and protomotion logic

This commit is contained in:
2026-05-20 17:31:42 -05:00
parent 2575b268d6
commit 2bdf100ed6
2 changed files with 146 additions and 23 deletions
+14 -2
View File
@@ -35,6 +35,11 @@ pub fn run_tui(
let mut terminal = Terminal::new(backend)?;
let (event_tx, event_rx) = channel::<AppEvent>();
let input_tx = event_tx.clone();
let mut project_list_state = ListState::default();
if !state.projects.is_empty() {
project_list_state.select(Some(0));
state.selected_project = 0;
}
std::thread::spawn(move || {
loop {
if event::poll(Duration::from_millis(100)).unwrap_or(false) {
@@ -169,6 +174,9 @@ pub fn run_tui(
state.output.push("Error: USAGE -> np <org> <name>".into());
}
}
"current_project" | "cp" => {
let _ = state.execute_command(command, None);
}
command_name => {
if state.module_loader.commands.contains_key(command_name) {
state.output.push(format!(
@@ -221,7 +229,9 @@ pub fn run_tui(
{
if let Some(selected) = project_list_state.selected() {
if selected > 0 {
project_list_state.select(Some(selected - 1));
let new_index = selected - 1;
project_list_state.select(Some(new_index));
state.selected_project = new_index;
}
}
}
@@ -232,7 +242,9 @@ pub fn run_tui(
{
if let Some(selected) = project_list_state.selected() {
if selected + 1 < state.projects.len() {
project_list_state.select(Some(selected + 1));
let new_index = selected + 1;
project_list_state.select(Some(new_index));
state.selected_project = new_index;
}
}
}
+132 -21
View File
@@ -1,10 +1,12 @@
use fs_extra::dir::{CopyOptions, copy};
use ratatui::crossterm::event;
use rhai::{AST, Dynamic, Engine, Scope};
use std::collections::HashMap;
use std::error::Error;
use std::fs::{File, create_dir_all, read_dir, read_to_string};
use std::fs::{File, create_dir_all, read_dir, read_to_string, remove_dir_all};
use std::io::{self, Write};
use std::path::PathBuf;
use std::process::Command;
use std::sync::mpsc::Receiver;
use std::sync::{Arc, mpsc::Sender, mpsc::channel};
@@ -22,7 +24,7 @@ pub struct AppState {
pub log: Vec<String>,
pub output: Vec<String>,
pub selected_server: Option<Server>,
pub selected_project: Option<Project>,
pub selected_project: usize,
pub curent_intput: String,
pub module_loader: ModuleLoader,
pub output_scroll: u16,
@@ -47,7 +49,7 @@ impl AppState {
log: Vec::new(),
output: Vec::new(),
selected_server: None,
selected_project: None,
selected_project: 0,
curent_intput: String::new(),
module_loader: ModuleLoader::new(),
output_scroll: 0,
@@ -145,6 +147,48 @@ impl AppState {
)))?;
}
}
"current_project" | "cp" => {
let mut out_vec = Vec::new();
out_vec.push(format!(
"org: {}",
&self.projects[self.selected_project].org_name
));
out_vec.push(format!(
"name: {}",
&self.projects[self.selected_project].name
));
out_vec.push(format!(
"files: {}",
&self.projects[self.selected_project].files.display()
));
out_vec.push(format!(
"notes: {}",
&self.projects[self.selected_project].notes.display()
));
if let Some(db) = self.projects[self.selected_project].db.clone() {
out_vec.push(format!("distrobox: {}", db.name));
}
if self.projects[self.selected_project].current {
out_vec.push(format!("status: current"));
} else {
out_vec.push(format!("status: upcoming"));
}
for line in out_vec {
let _ = self.main_tx.send(ToolMessage::Output(line));
}
}
"promote_project" | "pp" => {
match self.promote_project() {
Ok(out) => {
let _ = self.main_tx.send(ToolMessage::Output(out));
}
Err(e) => {
let _ = self
.main_tx
.send(ToolMessage::Output(format!("error promoting project! {e}")));
}
};
}
_ => {
let ast = self
.module_loader
@@ -164,9 +208,7 @@ impl AppState {
for arg_requirement in &cmd_meta.args {
match arg_requirement.as_str() {
"project" => {
if let Some(ref proj) = self.selected_project {
scope.push("project", proj.clone());
}
scope.push("project", self.projects[self.selected_project].clone());
}
"projects" => {
let mut arr = rhai::Array::new();
@@ -176,16 +218,10 @@ impl AppState {
scope.push("projects", arr);
}
"host" => {
if let Some(host) =
self.selected_project.as_ref().and_then(|p| p.hosts.first())
{
scope.push("host", host.clone());
}
scope.push("host", "todo");
}
"hosts" => {
if let Some(ref proj) = self.selected_project {
scope.push("hosts", proj.hosts.clone());
}
scope.push("hosts", self.projects[self.selected_project].clone());
}
"config" => {
let mut map = rhai::Map::new();
@@ -248,19 +284,30 @@ impl AppState {
pub fn new_project(&mut self, org: String, name: String) -> Result<(), Box<dyn Error>> {
let template_box = self.config.get("template_box").unwrap();
let tools_dir = self.config.get("tools").unwrap();
let project_files = PathBuf::from(self.config.get("upcoming_files").unwrap())
.join(format!("{}/{}", org, name));
let project_notes = PathBuf::from(self.config.get("upcoming_notes").unwrap())
.join(format!("{}/{}", org, name));
create_dir_all(&project_files)?;
create_dir_all(&project_notes)?;
let db = DistroBox {
name: format!("{}-{}-{}", template_box, org, name),
volumes: vec![
project_files.display().to_string(),
project_notes.display().to_string(),
tools_dir.clone(),
],
created: false,
template: template_box.clone(),
};
let mut new_project = Project::new();
let mut project_conf_folder = self.config_file.clone();
project_conf_folder.pop();
project_conf_folder.push(format!("projects/{}-{}", org, name));
new_project.config_folder(project_conf_folder);
new_project.box_name(format!("{}-{}-{}", template_box, org, name));
new_project.name = name;
new_project.db = Some(db);
new_project.org_name(org);
new_project.files(project_files);
new_project.notes(project_notes);
@@ -268,6 +315,44 @@ impl AppState {
self.projects.push(new_project);
return Ok(());
}
pub fn promote_project(&mut self) -> Result<String, Box<dyn Error>> {
let mut return_string = String::from("Project promoted successfully!");
let mut new_project = self.projects[self.selected_project].clone();
let new_files_path = PathBuf::from(self.config.get("current_files").unwrap())
.join(format!("{}/{}", new_project.org_name, new_project.name));
let new_notes_path = PathBuf::from(self.config.get("current_notes").unwrap())
.join(format!("{}/{}", new_project.org_name, new_project.name));
let mut options = CopyOptions::new();
options.overwrite = true;
copy(new_project.files.clone(), new_files_path.clone(), &options)?;
copy(new_project.notes.clone(), new_notes_path.clone(), &options)?;
let cleanup_res = Command::new("rm")
.arg("-rf")
.arg(new_project.files.display().to_string())
.arg(new_project.notes.display().to_string())
.status();
if cleanup_res.is_err() {
return_string = format!(
"Error deleting {} and {}, please clean up manually. Otherwise Project promotion succeeded!",
new_files_path.display(),
new_notes_path.display()
);
}
new_project.files(new_files_path.clone());
new_project.notes(new_notes_path.clone());
if let Some(mut db) = new_project.db {
db.volumes = vec![
new_files_path.display().to_string(),
new_notes_path.display().to_string(),
self.config.get("tools").unwrap().to_string(),
];
db.create()?;
new_project.db = Some(db);
}
self.projects[self.selected_project] = new_project;
return Ok(return_string);
}
}
#[derive(Clone)]
@@ -285,7 +370,7 @@ pub struct Project {
pub files: PathBuf,
pub hosts: Vec<Host>,
pub current: bool,
pub boxname: String,
pub db: Option<DistroBox>,
}
impl Project {
@@ -298,7 +383,7 @@ impl Project {
files: PathBuf::new(),
hosts: Vec::new(),
current: false,
boxname: String::new(),
db: None,
}
}
@@ -326,10 +411,6 @@ impl Project {
self.config_folder = path;
}
pub fn box_name(&mut self, boxname: String) {
self.boxname = boxname;
}
pub fn load_config(&mut self) -> Result<(), Box<dyn Error>> {
let config_contents = read_to_string(&self.config_folder)?;
for line in config_contents.lines() {
@@ -656,3 +737,33 @@ enum AppEvent {
Worker(ToolMessage),
Mouse(event::MouseEvent),
}
#[derive(Debug, Clone)]
pub struct DistroBox {
pub name: String,
pub volumes: Vec<String>,
pub created: bool,
pub template: String,
}
impl DistroBox {
pub fn create(&mut self) -> Result<(), Box<dyn Error>> {
let mut create_command = Command::new("distrobox");
create_command
.arg("create")
.arg("--root")
.arg("--clone")
.arg(self.template.clone())
.arg("--name")
.arg(self.name.clone());
for volume in &self.volumes {
let folder_name = volume.split("/").last().unwrap();
create_command
.arg("--volume")
.arg(format!("{}:/{}:rw", volume, folder_name));
}
create_command.status()?;
self.created = true;
return Ok(());
}
}