mirror of
https://gitlab.redox-os.org/redox-os/redox.git
synced 2026-06-17 15:34:18 +08:00
Implement cook TUI
This commit is contained in:
parent
8e2ac316e4
commit
ab57937dd4
198
Cargo.lock
generated
198
Cargo.lock
generated
@ -52,6 +52,12 @@ dependencies = [
|
||||
"alloc-no-stdlib",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "allocator-api2"
|
||||
version = "0.2.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
|
||||
|
||||
[[package]]
|
||||
name = "android-tzdata"
|
||||
version = "0.1.1"
|
||||
@ -374,6 +380,21 @@ version = "1.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a"
|
||||
|
||||
[[package]]
|
||||
name = "cassowary"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53"
|
||||
|
||||
[[package]]
|
||||
name = "castaway"
|
||||
version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dec551ab6e7578819132c713a93c022a05d60159dc86e7a7050223577484c55a"
|
||||
dependencies = [
|
||||
"rustversion",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.29"
|
||||
@ -446,7 +467,7 @@ dependencies = [
|
||||
"ansi_term",
|
||||
"atty",
|
||||
"bitflags 1.3.2",
|
||||
"strsim",
|
||||
"strsim 0.8.0",
|
||||
"textwrap",
|
||||
"unicode-width 0.1.14",
|
||||
"vec_map",
|
||||
@ -458,6 +479,20 @@ version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75"
|
||||
|
||||
[[package]]
|
||||
name = "compact_str"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3b79c4069c6cad78e2e0cdfcbd26275770669fb39fd308a752dc110e83b9af32"
|
||||
dependencies = [
|
||||
"castaway",
|
||||
"cfg-if 1.0.1",
|
||||
"itoa",
|
||||
"rustversion",
|
||||
"ryu",
|
||||
"static_assertions",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "console"
|
||||
version = "0.15.11"
|
||||
@ -467,7 +502,7 @@ dependencies = [
|
||||
"encode_unicode",
|
||||
"libc",
|
||||
"once_cell",
|
||||
"unicode-width 0.2.1",
|
||||
"unicode-width 0.2.0",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
@ -602,6 +637,41 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling"
|
||||
version = "0.20.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"darling_macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling_core"
|
||||
version = "0.20.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e"
|
||||
dependencies = [
|
||||
"fnv",
|
||||
"ident_case",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"strsim 0.11.1",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling_macro"
|
||||
version = "0.20.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "deranged"
|
||||
version = "0.4.0"
|
||||
@ -974,9 +1044,17 @@ version = "0.15.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5"
|
||||
dependencies = [
|
||||
"allocator-api2",
|
||||
"equivalent",
|
||||
"foldhash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.1.19"
|
||||
@ -1211,6 +1289,12 @@ dependencies = [
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ident_case"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "1.0.3"
|
||||
@ -1267,10 +1351,19 @@ dependencies = [
|
||||
"console",
|
||||
"number_prefix",
|
||||
"portable-atomic",
|
||||
"unicode-width 0.2.1",
|
||||
"unicode-width 0.2.0",
|
||||
"web-time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indoc"
|
||||
version = "2.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706"
|
||||
dependencies = [
|
||||
"rustversion",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "inout"
|
||||
version = "0.1.4"
|
||||
@ -1280,6 +1373,19 @@ dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "instability"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "435d80800b936787d62688c927b6490e887c7ef5ff9ce922c6c6050fca75eb9a"
|
||||
dependencies = [
|
||||
"darling",
|
||||
"indoc",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "io-uring"
|
||||
version = "0.7.8"
|
||||
@ -1379,6 +1485,15 @@ version = "0.4.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
|
||||
|
||||
[[package]]
|
||||
name = "lru"
|
||||
version = "0.12.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38"
|
||||
dependencies = [
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lru-slab"
|
||||
version = "0.1.2"
|
||||
@ -1500,6 +1615,12 @@ dependencies = [
|
||||
"thiserror 1.0.69",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "paste"
|
||||
version = "1.0.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
|
||||
|
||||
[[package]]
|
||||
name = "pbr"
|
||||
version = "1.1.1"
|
||||
@ -1804,6 +1925,27 @@ version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "384c2842d4e069d5ccacf5fe1dca4ef8d07a5444329715f0fc3c61813502d4d1"
|
||||
|
||||
[[package]]
|
||||
name = "ratatui"
|
||||
version = "0.29.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eabd94c2f37801c20583fc49dd5cd6b0ba68c716787c2dd6ed18571e1e63117b"
|
||||
dependencies = [
|
||||
"bitflags 2.9.1",
|
||||
"cassowary",
|
||||
"compact_str",
|
||||
"indoc",
|
||||
"instability",
|
||||
"itertools",
|
||||
"lru",
|
||||
"paste",
|
||||
"strum",
|
||||
"termion",
|
||||
"unicode-segmentation",
|
||||
"unicode-truncate",
|
||||
"unicode-width 0.2.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rayon"
|
||||
version = "1.10.0"
|
||||
@ -1871,6 +2013,7 @@ dependencies = [
|
||||
"pkgar 0.1.19",
|
||||
"pkgar-core 0.1.19",
|
||||
"pkgar-keys 0.1.19",
|
||||
"ratatui",
|
||||
"redox-pkg",
|
||||
"redoxer",
|
||||
"regex",
|
||||
@ -2357,6 +2500,34 @@ version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||
|
||||
[[package]]
|
||||
name = "strum"
|
||||
version = "0.26.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06"
|
||||
dependencies = [
|
||||
"strum_macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strum_macros"
|
||||
version = "0.26.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustversion",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "subtle"
|
||||
version = "2.6.1"
|
||||
@ -2724,6 +2895,23 @@ version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-segmentation"
|
||||
version = "1.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-truncate"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b3644627a5af5fa321c95b9b235a72fd24cd29c648c2c379431e6628655627bf"
|
||||
dependencies = [
|
||||
"itertools",
|
||||
"unicode-segmentation",
|
||||
"unicode-width 0.1.14",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.14"
|
||||
@ -2732,9 +2920,9 @@ checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.2.1"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4a1a07cc7db3810833284e8d372ccdc6da29741639ecc70c9ec107df0fa6154c"
|
||||
checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd"
|
||||
|
||||
[[package]]
|
||||
name = "untrusted"
|
||||
|
||||
@ -36,6 +36,9 @@ serde = { version = "=1.0.197", features = ["derive"] }
|
||||
termion = "4"
|
||||
toml = "0.8"
|
||||
walkdir = "2.3.1"
|
||||
ratatui = { version = "0.29.0", default-features = false, features = [
|
||||
"termion",
|
||||
] }
|
||||
|
||||
[dev-dependencies]
|
||||
tempfile = "3"
|
||||
|
||||
272
src/bin/repo.rs
272
src/bin/repo.rs
@ -1,7 +1,10 @@
|
||||
use std::io::stdout;
|
||||
use std::path::PathBuf;
|
||||
use std::process;
|
||||
use std::str::FromStr;
|
||||
use std::sync::mpsc;
|
||||
use std::time::Duration;
|
||||
use std::{env, fs};
|
||||
use std::{process, thread};
|
||||
|
||||
use anyhow::{Context, anyhow, bail};
|
||||
use cookbook::WALK_DEPTH;
|
||||
@ -13,6 +16,12 @@ use cookbook::cook::package::package;
|
||||
use cookbook::recipe::CookRecipe;
|
||||
use pkg::PackageName;
|
||||
use pkg::package::PackageError;
|
||||
use ratatui::Terminal;
|
||||
use ratatui::layout::{Constraint, Direction, Layout};
|
||||
use ratatui::prelude::TermionBackend;
|
||||
use ratatui::style::{Color, Style};
|
||||
use ratatui::widgets::{Block, Borders, List, ListItem, Paragraph};
|
||||
use termion::screen::{ToAlternateScreen, ToMainScreen};
|
||||
|
||||
// A repo manager, to replace repo.sh
|
||||
|
||||
@ -25,6 +34,7 @@ const REPO_HELP_STR: &str = r#"
|
||||
unfetch delete recipe sources
|
||||
clean delete recipe artifacts
|
||||
push extract package into sysroot
|
||||
tree show tree of recipe packages
|
||||
|
||||
common flags:
|
||||
--cookbook=<cookbook_dir> the "recipes" folder, default to $PWD/recipes
|
||||
@ -40,6 +50,7 @@ const REPO_HELP_STR: &str = r#"
|
||||
-q, --quiet surpress build logs unless error
|
||||
"#;
|
||||
|
||||
#[derive(Clone)]
|
||||
struct CliConfig {
|
||||
cookbook_dir: PathBuf,
|
||||
repo_dir: PathBuf,
|
||||
@ -56,6 +67,7 @@ enum CliCommand {
|
||||
Unfetch,
|
||||
Clean,
|
||||
Push,
|
||||
Tree,
|
||||
}
|
||||
|
||||
impl FromStr for CliCommand {
|
||||
@ -68,6 +80,7 @@ impl FromStr for CliCommand {
|
||||
"unfetch" => Ok(CliCommand::Unfetch),
|
||||
"clean" => Ok(CliCommand::Clean),
|
||||
"push" => Ok(CliCommand::Push),
|
||||
"tree" => Ok(CliCommand::Tree),
|
||||
_ => Err(anyhow!("Unknown command '{}'", s)),
|
||||
}
|
||||
}
|
||||
@ -81,6 +94,7 @@ impl ToString for CliCommand {
|
||||
CliCommand::Unfetch => "unfetch".to_string(),
|
||||
CliCommand::Clean => "clean".to_string(),
|
||||
CliCommand::Push => "push".to_string(),
|
||||
CliCommand::Tree => "tree".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -119,13 +133,24 @@ fn main_inner() -> anyhow::Result<()> {
|
||||
|
||||
let (config, command, recipe_names) = parse_args(args)?;
|
||||
|
||||
if command == CliCommand::Cook && config.cook.tui {
|
||||
run_tui_cook(config, recipe_names)?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
for recipe in &recipe_names {
|
||||
match command {
|
||||
CliCommand::Fetch => handle_cook(recipe, &config, true, recipe.is_deps)?,
|
||||
CliCommand::Cook => handle_cook(recipe, &config, false, recipe.is_deps)?,
|
||||
CliCommand::Fetch => {
|
||||
handle_fetch(recipe, &config)?;
|
||||
}
|
||||
CliCommand::Cook => {
|
||||
let source_dir = handle_fetch(recipe, &config)?;
|
||||
handle_cook(recipe, &config, source_dir, recipe.is_deps)?
|
||||
}
|
||||
CliCommand::Unfetch => handle_clean(recipe, &config, true, true)?,
|
||||
CliCommand::Clean => handle_clean(recipe, &config, false, true)?,
|
||||
CliCommand::Push => handle_push(recipe, &config)?,
|
||||
CliCommand::Tree => todo!("tree command is WIP"),
|
||||
}
|
||||
}
|
||||
|
||||
@ -187,6 +212,7 @@ fn parse_args(args: Vec<String>) -> anyhow::Result<(CliConfig, CliCommand, Vec<C
|
||||
if command == CliCommand::Cook
|
||||
|| command == CliCommand::Fetch
|
||||
|| command == CliCommand::Push
|
||||
|| command == CliCommand::Tree
|
||||
{
|
||||
// because read_recipe is false below
|
||||
// some recipes on wip folders are invalid anyway
|
||||
@ -215,12 +241,7 @@ fn parse_args(args: Vec<String>) -> anyhow::Result<(CliConfig, CliCommand, Vec<C
|
||||
Ok((config, command, recipes))
|
||||
}
|
||||
|
||||
fn handle_cook(
|
||||
recipe: &CookRecipe,
|
||||
config: &CliConfig,
|
||||
fetch_only: bool,
|
||||
is_deps: bool,
|
||||
) -> anyhow::Result<()> {
|
||||
fn handle_fetch(recipe: &CookRecipe, config: &CliConfig) -> anyhow::Result<PathBuf> {
|
||||
let recipe_dir = &recipe.dir;
|
||||
let source_dir = match config.cook.offline {
|
||||
true => fetch_offline(recipe_dir, &recipe.recipe),
|
||||
@ -228,10 +249,16 @@ fn handle_cook(
|
||||
}
|
||||
.map_err(|e| anyhow!(e))?;
|
||||
|
||||
if fetch_only {
|
||||
return Ok(());
|
||||
}
|
||||
Ok(source_dir)
|
||||
}
|
||||
|
||||
fn handle_cook(
|
||||
recipe: &CookRecipe,
|
||||
config: &CliConfig,
|
||||
source_dir: PathBuf,
|
||||
is_deps: bool,
|
||||
) -> anyhow::Result<()> {
|
||||
let recipe_dir = &recipe.dir;
|
||||
let target_dir = create_target_dir(recipe_dir).map_err(|e| anyhow!(e))?;
|
||||
|
||||
let (stage_dir, auto_deps) = build(
|
||||
@ -288,3 +315,224 @@ fn handle_push(recipe: &CookRecipe, config: &CliConfig) -> anyhow::Result<()> {
|
||||
config.sysroot_dir.display(),
|
||||
))
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
enum RecipeStatus {
|
||||
Pending,
|
||||
Fetching,
|
||||
Fetched,
|
||||
Cooking,
|
||||
Done,
|
||||
Failed(String),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
enum StatusUpdate {
|
||||
StartFetch(PackageName),
|
||||
Fetched(PackageName),
|
||||
FailFetch(PackageName, String),
|
||||
StartCook(PackageName),
|
||||
Cooked(PackageName),
|
||||
FailCook(PackageName, String),
|
||||
}
|
||||
|
||||
struct TuiApp {
|
||||
recipes: Vec<(CookRecipe, RecipeStatus)>,
|
||||
fetch_queue: Vec<PackageName>,
|
||||
cook_queue: Vec<PackageName>,
|
||||
done: Vec<PackageName>,
|
||||
failed: Vec<PackageName>,
|
||||
}
|
||||
|
||||
impl TuiApp {
|
||||
fn new(recipes: Vec<CookRecipe>) -> Self {
|
||||
let recipe_names = recipes.iter().map(|r| r.name.clone()).collect();
|
||||
Self {
|
||||
recipes: recipes
|
||||
.into_iter()
|
||||
.map(|r| (r, RecipeStatus::Pending))
|
||||
.collect(),
|
||||
fetch_queue: recipe_names,
|
||||
cook_queue: Vec::new(),
|
||||
done: Vec::new(),
|
||||
failed: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
// Update the state based on a message from a worker thread
|
||||
fn update_status(&mut self, update: StatusUpdate) {
|
||||
let (name, new_status) = match update {
|
||||
StatusUpdate::StartFetch(name) => (name, RecipeStatus::Fetching),
|
||||
StatusUpdate::Fetched(name) => (name, RecipeStatus::Fetched),
|
||||
StatusUpdate::FailFetch(name, err) => (name, RecipeStatus::Failed(err)),
|
||||
StatusUpdate::StartCook(name) => (name, RecipeStatus::Cooking),
|
||||
StatusUpdate::Cooked(name) => (name, RecipeStatus::Done),
|
||||
StatusUpdate::FailCook(name, err) => (name, RecipeStatus::Failed(err)),
|
||||
};
|
||||
|
||||
if let Some((_, status)) = self.recipes.iter_mut().find(|(r, _)| r.name == name) {
|
||||
*status = new_status;
|
||||
}
|
||||
|
||||
// Re-compute the queues for display
|
||||
self.fetch_queue = self
|
||||
.recipes
|
||||
.iter()
|
||||
.filter(|(_, s)| *s == RecipeStatus::Pending || *s == RecipeStatus::Fetching)
|
||||
.map(|(r, _)| r.name.clone())
|
||||
.collect();
|
||||
self.cook_queue = self
|
||||
.recipes
|
||||
.iter()
|
||||
.filter(|(_, s)| *s == RecipeStatus::Fetched || *s == RecipeStatus::Cooking)
|
||||
.map(|(r, _)| r.name.clone())
|
||||
.collect();
|
||||
self.done = self
|
||||
.recipes
|
||||
.iter()
|
||||
.filter(|(_, s)| *s == RecipeStatus::Done)
|
||||
.map(|(r, _)| r.name.clone())
|
||||
.collect();
|
||||
self.failed = self
|
||||
.recipes
|
||||
.iter()
|
||||
.filter(|(_, s)| matches!(s, RecipeStatus::Failed(_)))
|
||||
.map(|(r, _)| r.name.clone())
|
||||
.collect();
|
||||
}
|
||||
}
|
||||
|
||||
fn run_tui_cook(config: CliConfig, recipes: Vec<CookRecipe>) -> anyhow::Result<()> {
|
||||
let (work_tx, work_rx) = mpsc::channel::<(CookRecipe, PathBuf)>();
|
||||
let (status_tx, status_rx) = mpsc::channel::<StatusUpdate>();
|
||||
|
||||
// ---- Cooker Thread ----
|
||||
let cooker_config = config.clone();
|
||||
let cooker_status_tx = status_tx.clone();
|
||||
let cooker_handle = thread::spawn(move || {
|
||||
for (recipe, source_dir) in work_rx {
|
||||
let name = recipe.name.clone();
|
||||
let is_deps = recipe.is_deps;
|
||||
cooker_status_tx
|
||||
.send(StatusUpdate::StartCook(name.clone()))
|
||||
.unwrap();
|
||||
|
||||
match handle_cook(&recipe, &cooker_config, source_dir, is_deps) {
|
||||
Ok(_) => cooker_status_tx.send(StatusUpdate::Cooked(name)).unwrap(),
|
||||
Err(e) => cooker_status_tx
|
||||
.send(StatusUpdate::FailCook(name, e.to_string()))
|
||||
.unwrap(),
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// ---- Fetcher Thread ----
|
||||
let fetcher_config = config.clone();
|
||||
let fetcher_handle = thread::spawn(move || {
|
||||
for recipe in recipes {
|
||||
let name = recipe.name.clone();
|
||||
status_tx
|
||||
.send(StatusUpdate::StartFetch(name.clone()))
|
||||
.unwrap();
|
||||
|
||||
match handle_fetch(&recipe, &fetcher_config) {
|
||||
Ok(source_dir) => {
|
||||
status_tx.send(StatusUpdate::Fetched(name)).unwrap();
|
||||
if work_tx.send((recipe, source_dir)).is_err() {
|
||||
// Cooker thread died
|
||||
break;
|
||||
}
|
||||
}
|
||||
Err(e) => status_tx
|
||||
.send(StatusUpdate::FailFetch(name, e.to_string()))
|
||||
.unwrap(),
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
print!("{}", ToAlternateScreen);
|
||||
// enable_raw_mode()?;
|
||||
let mut terminal = Terminal::new(TermionBackend::new(stdout()))?;
|
||||
terminal.clear()?;
|
||||
|
||||
let mut app = TuiApp::new(Vec::new());
|
||||
let total_recipes = app.recipes.len();
|
||||
let mut running = true;
|
||||
|
||||
while running {
|
||||
terminal.draw(|f| {
|
||||
let chunks = Layout::default()
|
||||
.direction(Direction::Horizontal)
|
||||
.constraints([Constraint::Percentage(50), Constraint::Percentage(50)].as_ref())
|
||||
.split(f.area());
|
||||
|
||||
// Left Pane
|
||||
let fetch_items: Vec<ListItem> = app
|
||||
.recipes
|
||||
.iter()
|
||||
.filter(|(_, s)| *s == RecipeStatus::Pending || *s == RecipeStatus::Fetching)
|
||||
.map(|(r, s)| {
|
||||
let style = if *s == RecipeStatus::Fetching {
|
||||
Style::default().fg(Color::Yellow)
|
||||
} else {
|
||||
Style::default()
|
||||
};
|
||||
ListItem::new(r.name.as_str()).style(style)
|
||||
})
|
||||
.collect();
|
||||
let fetch_list = List::new(fetch_items)
|
||||
.block(Block::default().title("Fetch Queue").borders(Borders::ALL));
|
||||
f.render_widget(fetch_list, chunks[0]);
|
||||
|
||||
// Right Pane
|
||||
let cook_items: Vec<ListItem> = app
|
||||
.recipes
|
||||
.iter()
|
||||
.filter(|(_, s)| {
|
||||
*s == RecipeStatus::Fetched
|
||||
|| *s == RecipeStatus::Cooking
|
||||
|| *s == RecipeStatus::Done
|
||||
|| matches!(s, RecipeStatus::Failed(_))
|
||||
})
|
||||
.map(|(r, s)| {
|
||||
let style = match s {
|
||||
RecipeStatus::Fetched => Style::default().fg(Color::Cyan),
|
||||
RecipeStatus::Cooking => Style::default().fg(Color::Yellow),
|
||||
RecipeStatus::Done => Style::default().fg(Color::Green),
|
||||
RecipeStatus::Failed(_) => Style::default().fg(Color::Red),
|
||||
_ => Style::default(),
|
||||
};
|
||||
ListItem::new(r.name.as_str()).style(style)
|
||||
})
|
||||
.collect();
|
||||
let cook_list = List::new(cook_items)
|
||||
.block(Block::default().title("Cook Queue").borders(Borders::ALL));
|
||||
f.render_widget(cook_list, chunks[1]);
|
||||
|
||||
let footer = Paragraph::new(format!(
|
||||
"Done: {}/{} | Failed: {}",
|
||||
app.done.len(),
|
||||
total_recipes,
|
||||
app.failed.len()
|
||||
));
|
||||
f.render_widget(footer, f.area());
|
||||
})?;
|
||||
|
||||
while let Ok(update) = status_rx.try_recv() {
|
||||
app.update_status(update);
|
||||
}
|
||||
|
||||
if fetcher_handle.is_finished() && cooker_handle.is_finished() {
|
||||
thread::sleep(Duration::from_secs(5));
|
||||
running = false;
|
||||
}
|
||||
}
|
||||
|
||||
// disable_raw_mode()?;
|
||||
print!("{}", ToMainScreen);
|
||||
|
||||
fetcher_handle.join().unwrap();
|
||||
cooker_handle.join().unwrap();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user