meow meow

This commit is contained in:
2025-11-09 21:51:23 +04:00
parent 5c8dc86640
commit b08a3c682e
4 changed files with 270 additions and 85 deletions

4
src/lib.rs Normal file
View File

@@ -0,0 +1,4 @@
pub mod action;
pub mod action_raw;
pub mod nix_path;
pub mod state_manager;

View File

@@ -1,35 +1,29 @@
use std::{collections::HashMap, thread::sleep, time::Duration}; use std::thread::sleep;
use std::time::Duration;
use indicatif::{MultiProgress, ProgressBar, ProgressStyle}; use indicatif::{MultiProgress, ProgressBar, ProgressStyle};
use crate::action::StartFields; use crate::action::StartFields;
use crate::state_manager::{BuildEnumState, BuildState, StateManager};
pub mod action; pub mod action;
pub mod action_raw; pub mod action_raw;
pub mod nix_path;
enum BuildEnumState { pub mod state_manager;
Query,
Downloading,
}
struct BuildState {
path: Option<String>,
progress_bar: ProgressBar,
state: BuildEnumState,
}
struct State { struct State {
progress: MultiProgress, progress: MultiProgress,
bars: HashMap<u64, BuildState>, manager: StateManager,
} }
fn main() -> Result<(), color_eyre::Report> { fn main() -> Result<(), color_eyre::Report> {
color_eyre::install().unwrap(); color_eyre::install().unwrap();
let lines = std::fs::read_to_string("log2")?; let lines = std::fs::read_to_string("build.log")?;
// let lines = lines.lines().take(1000).collect::<Vec<_>>().join("\n"); // let lines = lines.lines().take(1000).collect::<Vec<_>>().join("\n");
let mut state = State { let mut state = State {
progress: MultiProgress::new(), progress: MultiProgress::new(),
bars: HashMap::new(), manager: StateManager::new(),
}; };
// let progress = MultiProgress::new(); // let progress = MultiProgress::new();
@@ -58,7 +52,7 @@ fn main() -> Result<(), color_eyre::Report> {
for line in lines.lines() { for line in lines.lines() {
lines_pb.inc(1); lines_pb.inc(1);
let line = line.strip_prefix("@nix ").unwrap_or(line); let line = line.strip_prefix("@nix ").unwrap_or(line);
sleep(Duration::from_millis(50)); sleep(Duration::from_millis(10));
let action = action::Action::parse(line)?; let action = action::Action::parse(line)?;
match action { match action {
action::Action::Msg { level, msg } => { action::Action::Msg { level, msg } => {
@@ -69,81 +63,123 @@ fn main() -> Result<(), color_eyre::Report> {
action::Action::Start { action::Action::Start {
start_type, start_type,
id, id,
level, level: _,
parent, parent,
text, text: _,
} => match start_type { } => {
StartFields::QueryPathInfo { path, source } => { state.progress.println(format!("START {start_type:?}"));
state.bars.insert( match start_type {
*id, StartFields::Substitute { source, target } => {
BuildState { state
path: Some(path.to_string()), .manager
progress_bar: state.progress.add(ProgressBar::new(100)), .insert_parent(*id, BuildState::new(Some(source.to_string()), None));
state: BuildEnumState::Query,
}, let build_state = state.manager.get_mut(*id).unwrap();
); build_state.state = BuildEnumState::Substituting;
}
StartFields::FileTransfer { target } => {
state.progress.println(format!(
"START FileTransfer (id: {}, parent: {}): target={}",
id, parent, target
))?;
if let Some(bar) = state.bars.get_mut(&parent) {
bar.state = BuildEnumState::Downloading;
bar.progress_bar.set_style(
ProgressStyle::default_bar()
.template("{msg} [{bar:40.cyan/blue}] {pos:>3}%")
.unwrap(),
);
bar.progress_bar.set_message(format!(
"Downloading {}",
bar.path.as_deref().unwrap_or("unknown")
));
} }
// let bar = progress.add(ProgressBar::new(100)); StartFields::CopyPath {
// bar // bar.set_message(format!("meow {id} ")); path,
// map.insert(id, bar); origin,
destination,
} => {
state.manager.add_child(*id, *parent);
}
// StartFields::QueryPathInfo { path, source: _ } => {
// state
// .manager
// .insert_parent(*id, BuildState::new(Some(path.to_string()), None));
// }
StartFields::FileTransfer { target } => {
// state.progress.println(format!(
// "START FileTransfer (id: {}, parent: {}): target={}",
// id, parent, target
// ))?;
if let Some(parent) = state.manager.get(*parent) {
state
.manager
.insert_parent(*id, BuildState::new(parent.path.clone(), None));
let build_state = state.manager.get_mut(*id).unwrap();
build_state.state = BuildEnumState::Downloading;
};
// state.manager.add_child(*id, *parent);
// Add child ID mapping to parent
}
_ => {}
};
}
action::Action::Stop { id } => {
// Stop will only return Some when the last reference is stopped
if let Some(build_state) = state.manager.stop(*id) {
if let Some(pb) = &build_state.progress_bar {
pb.finish_and_clear();
state.progress.remove(pb);
}
state.progress.println(format!(
"Completed: {}",
build_state.path.as_deref().unwrap_or("unknown")
))?;
}
}
action::Action::Result { id, fields } => match fields {
action::ResultFields::Progress {
done,
expected,
running,
failed,
} => {
// sleep(Duration::from_millis(1));
if let Some(build_state) = state.manager.get_mut(*id)
&& expected > 0
{
let percentage = if expected == 0 {
0
} else {
(done * 100 / expected) as u64
};
match &build_state.progress_bar {
Some(pb) => {
if percentage > pb.position() {
pb.set_position(percentage);
};
}
None => {
state.progress.println(format!(
"Creating progress bar for id {} (done: {}, expected: {})",
id, done, expected
))?;
let n = match build_state.state {
BuildEnumState::Downloading => "Downloading",
BuildEnumState::Substituting => "Substituting",
_ => "Processing",
};
let name =
nix_path::extract_full_name(build_state.path.as_ref().unwrap());
let pb = state.progress.add(
ProgressBar::new(100)
.with_style(
ProgressStyle::default_bar()
.template("{msg} [{bar:40.cyan/blue}] {pos:>3}%")
.unwrap(),
)
.with_message(format!(
"{n} {}",
name.as_deref().unwrap_or("unknown")
)),
);
pb.set_position(percentage);
build_state.progress_bar = Some(pb);
}
};
} else {
state.progress.println(format!(
"RESULT Progress (id: {}): done={}, expected={}, running={}, failed={}",
id, done, expected, running, failed
))?;
};
} }
_ => {} _ => {}
}, },
action::Action::Stop { id } => {
// if let Some(bar) = map.get(&id) {
// bar.finish();
// // bar.finish_with_message(format!("Transfer stopped (id: {})", id));
// progress.remove(bar);
// map.remove(&id);
// }
// progress.println(format!("STOP (id: {})", id))?;
}
action::Action::Result { id, fields } => {
// progress.println(format!("RESULT (id: {}): {:?}", id, fields))?;
match fields {
// action::ResultFields::Progress {
// done,
// expected,
// running,
// failed,
// } => {
// // sleep(Duration::from_millis(1));
// // if let Some(bar) = map.get(&id) {
// // let percentage = if expected == 0 {
// // 0
// // } else {
// // (done * 100 / expected) as u64
// // };
// // bar.set_position(percentage);
// // if done >= expected {
// // bar.finish_with_message(format!("Completed transfer (id: {})", id));
// // map.remove(&id);
// // }
// }
// }
_ => {}
}
}
_ => {
println!("{action:#?}");
}
} }
} }

38
src/nix_path.rs Normal file
View File

@@ -0,0 +1,38 @@
const NIX_STORE_PREFIX: &str = "/nix/store/";
const NIX_HASH_LENGTH: usize = 32;
pub fn extract_package_name(path: &str) -> Option<String> {
let without_prefix = path.strip_prefix(NIX_STORE_PREFIX)?;
if without_prefix.len() <= NIX_HASH_LENGTH + 1 {
return None;
}
let after_hash = &without_prefix[NIX_HASH_LENGTH + 1..];
let parts: Vec<&str> = after_hash.split('-').collect();
if parts.is_empty() {
return None;
}
let name_parts: Vec<&str> = parts
.iter()
.take_while(|part| !part.chars().next().map_or(false, |c| c.is_ascii_digit()))
.copied()
.collect();
if name_parts.is_empty() {
return None;
}
Some(name_parts.join("-"))
}
pub fn extract_full_name(path: &str) -> Option<&str> {
let without_prefix = path.strip_prefix(NIX_STORE_PREFIX)?;
if without_prefix.len() <= NIX_HASH_LENGTH + 1 {
return None;
}
Some(&without_prefix[NIX_HASH_LENGTH + 1..])
}

107
src/state_manager.rs Normal file
View File

@@ -0,0 +1,107 @@
use indicatif::ProgressBar;
use std::collections::HashMap;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BuildEnumState {
Query,
Downloading,
Substituting,
}
pub struct BuildState {
pub path: Option<String>,
pub progress_bar: Option<ProgressBar>,
pub state: BuildEnumState,
ref_count: usize,
}
impl BuildState {
pub fn new(path: Option<String>, progress_bar: Option<ProgressBar>) -> Self {
Self {
path,
progress_bar,
state: BuildEnumState::Query,
ref_count: 1,
}
}
}
pub struct StateManager {
states: HashMap<u64, BuildState>,
id_to_canonical: HashMap<u64, u64>,
}
impl StateManager {
pub fn new() -> Self {
Self {
states: HashMap::new(),
id_to_canonical: HashMap::new(),
}
}
pub fn insert_parent(&mut self, id: u64, state: BuildState) {
self.id_to_canonical.insert(id, id); // parent maps to itself
self.states.insert(id, state);
}
pub fn add_child(&mut self, child_id: u64, parent_id: u64) {
if let Some(&canonical_id) = self.id_to_canonical.get(&parent_id) {
self.id_to_canonical.insert(child_id, canonical_id);
if let Some(state) = self.states.get_mut(&canonical_id) {
state.ref_count += 1;
}
}
}
pub fn get(&self, id: u64) -> Option<&BuildState> {
self.id_to_canonical
.get(&id)
.and_then(|canonical_id| self.states.get(canonical_id))
}
pub fn get_mut(&mut self, id: u64) -> Option<&mut BuildState> {
self.id_to_canonical
.get(&id)
.copied()
.and_then(|canonical_id| self.states.get_mut(&canonical_id))
}
pub fn stop(&mut self, id: u64) -> Option<BuildState> {
let canonical_id = self.id_to_canonical.get(&id).copied()?;
self.id_to_canonical.remove(&id);
if let Some(state) = self.states.get_mut(&canonical_id) {
state.ref_count -= 1;
if state.ref_count == 0 {
return self.states.remove(&canonical_id);
}
}
None
}
pub fn get_canonical_id(&self, id: u64) -> Option<u64> {
self.id_to_canonical.get(&id).copied()
}
pub fn contains(&self, id: u64) -> bool {
self.id_to_canonical.contains_key(&id)
}
pub fn len(&self) -> usize {
self.states.len()
}
pub fn is_empty(&self) -> bool {
self.states.is_empty()
}
}
impl Default for StateManager {
fn default() -> Self {
Self::new()
}
}