rework state system
This commit is contained in:
@@ -75,7 +75,7 @@ impl Display for BuildStepId {
|
||||
// --- Action
|
||||
// ---
|
||||
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub enum StartFields<'a> {
|
||||
Unknown,
|
||||
CopyPath {
|
||||
|
||||
217
src/main.rs
217
src/main.rs
@@ -4,18 +4,13 @@ use std::time::Duration;
|
||||
use indicatif::{MultiProgress, ProgressBar, ProgressStyle};
|
||||
|
||||
use crate::action::StartFields;
|
||||
use crate::state_manager::{BuildEnumState, BuildState, StateManager};
|
||||
use crate::state_manager::{BuildEnumState, BuildState, State, StateManager};
|
||||
|
||||
pub mod action;
|
||||
pub mod action_raw;
|
||||
pub mod nix_path;
|
||||
pub mod state_manager;
|
||||
|
||||
struct State {
|
||||
progress: MultiProgress,
|
||||
manager: StateManager,
|
||||
}
|
||||
|
||||
fn main() -> Result<(), color_eyre::Report> {
|
||||
color_eyre::install().unwrap();
|
||||
let lines = std::fs::read_to_string("build.log")?;
|
||||
@@ -52,7 +47,7 @@ fn main() -> Result<(), color_eyre::Report> {
|
||||
for line in lines.lines() {
|
||||
lines_pb.inc(1);
|
||||
let line = line.strip_prefix("@nix ").unwrap_or(line);
|
||||
sleep(Duration::from_millis(10));
|
||||
sleep(Duration::from_millis(1));
|
||||
let action = action::Action::parse(line)?;
|
||||
match action {
|
||||
action::Action::Msg { level, msg } => {
|
||||
@@ -67,116 +62,136 @@ fn main() -> Result<(), color_eyre::Report> {
|
||||
parent,
|
||||
text: _,
|
||||
} => {
|
||||
state.progress.println(format!("START {start_type:?}"));
|
||||
match start_type {
|
||||
StartFields::Substitute { source, target } => {
|
||||
state
|
||||
.manager
|
||||
.insert_parent(*id, BuildState::new(Some(source.to_string()), None));
|
||||
|
||||
let build_state = state.manager.get_mut(*id).unwrap();
|
||||
build_state.state = BuildEnumState::Substituting;
|
||||
}
|
||||
StartFields::CopyPath {
|
||||
path,
|
||||
origin,
|
||||
destination,
|
||||
} => {
|
||||
state.manager.add_child(*id, *parent);
|
||||
}
|
||||
// StartFields::QueryPathInfo { path, source: _ } => {
|
||||
// state.progress.println(format!("START {start_type:?}"));
|
||||
if let Some(parent) = state.manager.get(*parent) {
|
||||
let mut child = parent.clone();
|
||||
child.merge(&mut state, start_type);
|
||||
child.tick(&mut state);
|
||||
state.manager.insert_parent(*id, child);
|
||||
};
|
||||
// match start_type {
|
||||
// StartFields::Substitute { source, target } => {
|
||||
// state
|
||||
// .manager
|
||||
// .insert_parent(*id, BuildState::new(Some(path.to_string()), None));
|
||||
// .insert_parent(*id, BuildState::new(Some(source.to_string()), None));
|
||||
//
|
||||
// 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(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;
|
||||
};
|
||||
|
||||
// StartFields::CopyPath {
|
||||
// path,
|
||||
// origin,
|
||||
// destination,
|
||||
// } => {
|
||||
// state.manager.add_child(*id, *parent);
|
||||
// Add child ID mapping to parent
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
// }
|
||||
// StartFields::QueryPathInfo { path, source } => {
|
||||
// state.progress.println(format!(
|
||||
// "START QueryPathInfo (id: {}, parent: {}): path={}",
|
||||
// id, parent, path
|
||||
// ))?;
|
||||
// }
|
||||
// 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 } => {
|
||||
state.manager.remove(*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")
|
||||
))?;
|
||||
}
|
||||
// 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::FetchStatus(status) => {
|
||||
state.progress.println(format!(
|
||||
"RESULT FetchStatus (id: {}): status={}",
|
||||
id, status
|
||||
))?;
|
||||
}
|
||||
action::ResultFields::Progress {
|
||||
done,
|
||||
expected,
|
||||
running,
|
||||
failed,
|
||||
} => {
|
||||
if expected == 0 {
|
||||
continue;
|
||||
};
|
||||
if let Some(child) = state.manager.get_mut(*id) {
|
||||
child.progress(done, expected);
|
||||
}
|
||||
// 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
|
||||
))?;
|
||||
};
|
||||
// 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
|
||||
// // ))?;
|
||||
// };
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
|
||||
@@ -1,107 +1,160 @@
|
||||
use indicatif::ProgressBar;
|
||||
use std::collections::HashMap;
|
||||
use indicatif::{MultiProgress, ProgressBar, ProgressStyle};
|
||||
use std::{borrow::Cow, collections::HashMap};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum BuildEnumState {
|
||||
Query,
|
||||
Downloading,
|
||||
Substituting,
|
||||
use crate::action::StartFields;
|
||||
|
||||
pub struct State<'a> {
|
||||
pub progress: MultiProgress,
|
||||
pub manager: StateManager<'a>,
|
||||
}
|
||||
|
||||
pub struct BuildState {
|
||||
impl<'a> State<'a> {
|
||||
pub fn add_pb(&mut self, pb: ProgressBar) -> ProgressBar {
|
||||
self.progress.add(pb)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum BuildEnumState<'a> {
|
||||
Init,
|
||||
Unknown,
|
||||
Realise,
|
||||
Builds,
|
||||
CopyPaths,
|
||||
Substitute {
|
||||
source: Cow<'a, str>,
|
||||
target: Cow<'a, str>,
|
||||
},
|
||||
SubstituteCopy {
|
||||
path: Cow<'a, str>,
|
||||
},
|
||||
SubstituteFetch {
|
||||
path: Cow<'a, str>,
|
||||
bar: ProgressBar,
|
||||
},
|
||||
Query {
|
||||
path: Cow<'a, str>,
|
||||
source: Cow<'a, str>,
|
||||
},
|
||||
QueryFetch {
|
||||
target: Cow<'a, str>,
|
||||
},
|
||||
}
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct BuildState<'a> {
|
||||
pub path: Option<String>,
|
||||
pub progress_bar: Option<ProgressBar>,
|
||||
pub state: BuildEnumState,
|
||||
pub state: BuildEnumState<'a>,
|
||||
ref_count: usize,
|
||||
}
|
||||
|
||||
impl BuildState {
|
||||
pub fn new(path: Option<String>, progress_bar: Option<ProgressBar>) -> Self {
|
||||
impl<'a> BuildState<'a> {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
path,
|
||||
progress_bar,
|
||||
state: BuildEnumState::Query,
|
||||
path: None,
|
||||
progress_bar: None,
|
||||
state: BuildEnumState::Init,
|
||||
ref_count: 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct StateManager {
|
||||
states: HashMap<u64, BuildState>,
|
||||
id_to_canonical: HashMap<u64, u64>,
|
||||
impl<'a> BuildState<'a> {
|
||||
pub fn merge(&mut self, state: &mut State, start_type: StartFields<'a>) {
|
||||
let state = match (&self.state, start_type.clone()) {
|
||||
(BuildEnumState::Init, StartFields::QueryPathInfo { path, source }) => {
|
||||
BuildEnumState::Query { path, source }
|
||||
}
|
||||
(BuildEnumState::Query { path, source }, StartFields::FileTransfer { target }) => {
|
||||
BuildEnumState::QueryFetch { target }
|
||||
}
|
||||
(BuildEnumState::Init, StartFields::Realise) => BuildEnumState::Realise,
|
||||
(BuildEnumState::Init, StartFields::Builds) => BuildEnumState::Builds,
|
||||
(BuildEnumState::Init, StartFields::CopyPaths) => BuildEnumState::CopyPaths,
|
||||
(BuildEnumState::Init, StartFields::Substitute { source, target }) => {
|
||||
BuildEnumState::Substitute { source, target }
|
||||
}
|
||||
(BuildEnumState::Substitute { .. }, StartFields::CopyPath { path, .. }) => {
|
||||
BuildEnumState::SubstituteCopy { path }
|
||||
}
|
||||
(BuildEnumState::SubstituteCopy { path }, StartFields::FileTransfer { .. }) => {
|
||||
BuildEnumState::SubstituteFetch {
|
||||
path: path.clone(),
|
||||
bar: state.add_pb(ProgressBar::new(100)),
|
||||
}
|
||||
}
|
||||
(_, StartFields::Unknown) => BuildEnumState::Unknown,
|
||||
_ => unimplemented!(
|
||||
"Unsupported state transition from {:?} with {:?}",
|
||||
self.state,
|
||||
start_type
|
||||
),
|
||||
};
|
||||
self.state = state;
|
||||
}
|
||||
pub fn tick(&mut self, state: &mut State<'a>) {
|
||||
match &self.state {
|
||||
BuildEnumState::QueryFetch { target } => {
|
||||
let pb = state.progress.add(ProgressBar::new_spinner());
|
||||
pb.set_message(format!("Fetching info for {}", target));
|
||||
pb.enable_steady_tick(std::time::Duration::from_millis(100));
|
||||
self.progress_bar = Some(pb);
|
||||
}
|
||||
BuildEnumState::SubstituteFetch { path, bar } => {
|
||||
bar.set_message(format!("Fetching substitute for {}", path));
|
||||
bar.set_style(
|
||||
ProgressStyle::default_bar()
|
||||
.template("{msg} [{bar:40.cyan/blue}] {pos:>3}%")
|
||||
.unwrap()
|
||||
.progress_chars("=> "),
|
||||
);
|
||||
// bar.enable_steady_tick(std::time::Duration::from_millis(100));
|
||||
// self.progress_bar = Some(bar.clone());
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
pub fn progress(&mut self, done: u64, expected: u64) {
|
||||
match &self.state {
|
||||
BuildEnumState::SubstituteFetch { bar, .. } => {
|
||||
let percentage = (done * 100 / expected) as u64;
|
||||
if percentage > bar.position() {
|
||||
bar.set_position(percentage);
|
||||
};
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl StateManager {
|
||||
pub struct StateManager<'a> {
|
||||
states: HashMap<u64, BuildState<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> StateManager<'a> {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
states: HashMap::new(),
|
||||
id_to_canonical: HashMap::new(),
|
||||
let mut states = HashMap::new();
|
||||
states.insert(0, BuildState::new());
|
||||
Self { states }
|
||||
}
|
||||
pub fn get(&self, id: u64) -> Option<&BuildState<'a>> {
|
||||
self.states.get(&id)
|
||||
}
|
||||
|
||||
pub fn insert_parent(&mut self, id: u64, state: BuildState) {
|
||||
self.id_to_canonical.insert(id, id); // parent maps to itself
|
||||
pub fn get_mut(&mut self, id: u64) -> Option<&mut BuildState<'a>> {
|
||||
self.states.get_mut(&id)
|
||||
}
|
||||
pub fn insert_parent(&mut self, id: u64, state: BuildState<'a>) {
|
||||
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_or_insert(&mut self, id: u64) -> &mut BuildState<'a> {
|
||||
self.states.entry(id).or_insert_with(BuildState::new)
|
||||
}
|
||||
pub fn remove(&mut self, id: u64) {
|
||||
if let Some(state) = self.states.get(&id) {
|
||||
if let Some(pb) = &state.progress_bar {
|
||||
pb.finish_and_clear();
|
||||
self.states.remove(&id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
self.states.remove(&id);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user