diff --git a/src/nix_util/nix_output_stream.rs b/src/nix_util/nix_output_stream.rs index 76b6c65..8ee8a16 100644 --- a/src/nix_util/nix_output_stream.rs +++ b/src/nix_util/nix_output_stream.rs @@ -1,6 +1,7 @@ use serde::Deserialize; use serde::Serialize; use serde_json::Value; +use tracing::warn; use super::output_stream::OutputLineStream; @@ -30,7 +31,8 @@ impl NixOutputStream { let payload = &line[4..]; if let Ok(action) = serde_json::from_str(&payload) { - return Ok(Some(NixMessage::Action(action))); + let parsed_action = parse_action(action)?; + return Ok(Some(NixMessage::Action(parsed_action))); } if let Ok(parsed) = serde_json::from_str(&payload) { return Ok(Some(NixMessage::Generic(parsed, line))); @@ -39,6 +41,297 @@ impl NixOutputStream { } } +fn parse_action(raw: RawNixAction) -> Result { + let original_json = serde_json::to_string(&raw)?; + match raw { + RawNixAction::Msg { + level, + msg, + raw_msg, + file, + line, + column, + trace, + } => Ok(NixAction::Msg(MsgMessage { + level, + msg, + raw_msg, + file, + line, + column, + trace, + })), + RawNixAction::Start { + id, + fields, + level, + parent, + text, + r#type, + } => { + match r#type { + 0 => { + // TODO: Add warnings for all unused fields + Ok(NixAction::Start(ActivityStartMessage::Unknown( + ActivityStartUnknown { + id, + parent, + level, + text, + }, + ))) + } + 100 => { + // TODO: Add warnings for all unused fields + // TODO: Haven't seen any of these. + warn!("Found CopyPath: {original_json}"); + Ok(NixAction::Start(ActivityStartMessage::CopyPath( + ActivityStartCopyPath { id, parent }, + ))) + } + 101 => { + // TODO: Add warnings for all unused fields + match &fields { + Some(f) if f.len() > 1 => { + warn!( + "Found more than one field in ActivityFileTransfer: {original_json}" + ); + } + _ => {} + }; + let url = string_field(&fields, 0).to_owned(); + Ok(NixAction::Start(ActivityStartMessage::FileTransfer( + ActivityStartFileTransfer { + id, + parent, + level, + text, + url, + }, + ))) + } + 102 => { + // TODO: Add warnings for all unused fields + match fields { + Some(f) if f.len() > 0 => { + warn!("Found fields in ActivityRealize: {original_json}"); + } + _ => {} + }; + if text.len() > 0 { + warn!("Found Realize with text: {original_json}"); + } + if parent != 0 { + warn!("Found Realize with non-zero parent: {original_json}"); + } + Ok(NixAction::Start(ActivityStartMessage::Realize( + ActivityStartRealize { + id, + parent, + level, + text, + }, + ))) + } + 103 => { + // TODO: Add warnings for all unused fields + match fields { + Some(f) if f.len() > 0 => { + warn!("Found fields in CopyPaths: {original_json}"); + } + _ => {} + }; + if text.len() > 0 { + warn!("Found CopyPaths with text: {original_json}"); + } + if parent != 0 { + warn!("Found CopyPaths with non-zero parent: {original_json}"); + } + Ok(NixAction::Start(ActivityStartMessage::CopyPaths( + ActivityStartCopyPaths { + id, + parent, + level, + text, + }, + ))) + } + 104 => { + // TODO: Add warnings for all unused fields + // println!("{}", original_json); + match fields { + Some(f) if f.len() > 0 => { + warn!("Found fields in Builds: {original_json}"); + } + _ => {} + }; + if text.len() > 0 { + warn!("Found Builds with text: {original_json}"); + } + Ok(NixAction::Start(ActivityStartMessage::Builds( + ActivityStartBuilds { + id, + parent, + level, + text, + }, + ))) + } + 105 => { + // TODO: Add warnings for all unused fields + // TODO: What are the other fields ["/nix/store/j54kvd8mlj8cl9ycvlkh5987fqvzl4p5-m4-1.4.20.tar.bz2.drv","",1,1] + let path = string_field(&fields, 0).to_owned(); + Ok(NixAction::Start(ActivityStartMessage::Build( + ActivityStartBuild { + id, + parent, + level, + text, + path, + }, + ))) + } + 106 => { + // TODO: Add warnings for all unused fields + // TODO: Haven't seen any of these. + warn!("Found OptimizeStore: {original_json}"); + Ok(NixAction::Start(ActivityStartMessage::OptimizeStore( + ActivityStartOptimizeStore { id, parent }, + ))) + } + 107 => { + // TODO: Add warnings for all unused fields + // TODO: Haven't seen any of these. + warn!("Found VerifyPath: {original_json}"); + Ok(NixAction::Start(ActivityStartMessage::VerifyPaths( + ActivityStartVerifyPaths { id, parent }, + ))) + } + 108 => { + // TODO: Add warnings for all unused fields + // TODO: Haven't seen any of these. + warn!("Found Subtitute: {original_json}"); + Ok(NixAction::Start(ActivityStartMessage::Substitute( + ActivityStartSubstitute { id, parent }, + ))) + } + 109 => { + // TODO: Add warnings for all unused fields + // TODO: Haven't seen any of these. + warn!("Found QueryPathInfo: {original_json}"); + Ok(NixAction::Start(ActivityStartMessage::QueryPathInfo( + ActivityStartQueryPathInfo { id, parent }, + ))) + } + 110 => { + // TODO: Add warnings for all unused fields + // TODO: Haven't seen any of these. + warn!("Found PostBuildHook: {original_json}"); + Ok(NixAction::Start(ActivityStartMessage::PostBuildHook( + ActivityStartPostBuildHook { id, parent }, + ))) + } + 111 => { + // TODO: Add warnings for all unused fields + // TODO: Haven't seen any of these. + warn!("Found BuildWaiting: {original_json}"); + Ok(NixAction::Start(ActivityStartMessage::BuildWaiting( + ActivityStartBuildWaiting { id, parent }, + ))) + } + 112 => { + // TODO: Add warnings for all unused fields + // TODO: Haven't seen any of these. + warn!("Found FetchTree: {original_json}"); + Ok(NixAction::Start(ActivityStartMessage::FetchTree( + ActivityStartFetchTree { id, parent }, + ))) + } + _ => { + panic!("Unhandled start activity: {original_json}"); + } + } + } + RawNixAction::Stop { id } => Ok(NixAction::Stop(StopMessage { id })), + RawNixAction::Result { id, fields, r#type } => { + match r#type { + 100 => { + // TODO: Add warnings for all unused fields + // TODO: Haven't seen any of these. + warn!("Found FileLinked: {original_json}"); + Ok(NixAction::Result(ActivityResultMessage::FileLinked( + ActivityResultFileLinked {}, + ))) + } + 101 => { + // TODO: Add warnings for all unused fields + // The first field is a string containing the log line + Ok(NixAction::Result(ActivityResultMessage::BuildLogLine( + ActivityResultBuildLogLine {}, + ))) + } + 102 => { + // TODO: Add warnings for all unused fields + // TODO: Haven't seen any of these. + warn!("Found UntrustedPath: {original_json}"); + Ok(NixAction::Result(ActivityResultMessage::UntrustedPath( + ActivityResultUntrustedPath {}, + ))) + } + 103 => { + // TODO: Add warnings for all unused fields + // TODO: Haven't seen any of these. + warn!("Found CorruptedPath: {original_json}"); + Ok(NixAction::Result(ActivityResultMessage::CorruptedPath( + ActivityResultCorruptedPath {}, + ))) + } + 104 => { + // TODO: Add warnings for all unused fields + // The first field is the phase name + Ok(NixAction::Result(ActivityResultMessage::SetPhase( + ActivityResultSetPhase {}, + ))) + } + 105 => { + // TODO: Add warnings for all unused fields + // Fields numerator, denominator, running?, failed? + Ok(NixAction::Result(ActivityResultMessage::Progress( + ActivityResultProgress {}, + ))) + } + 106 => { + // TODO: Add warnings for all unused fields + // Fields activity type?, expected? + Ok(NixAction::Result(ActivityResultMessage::SetExpected( + ActivityResultSetExpected {}, + ))) + } + 107 => { + // TODO: Add warnings for all unused fields + // TODO: Haven't seen any of these. + warn!("Found PostBuildLogLine: {original_json}"); + Ok(NixAction::Result(ActivityResultMessage::PostBuildLogLine( + ActivityResultPostBuildLogLine {}, + ))) + } + 108 => { + // TODO: Add warnings for all unused fields + // TODO: Haven't seen any of these. + warn!("Found FetchStatus: {original_json}"); + // println!("{original_json}"); + Ok(NixAction::Result(ActivityResultMessage::FetchStatus( + ActivityResultFetchStatus {}, + ))) + } + _ => { + panic!("Unhandled result: {original_json}"); + } + } + } + } +} + #[derive(Debug, Clone)] pub(crate) enum NixMessage { ParseFailure(String), @@ -48,7 +341,7 @@ pub(crate) enum NixMessage { #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(tag = "action", rename_all = "lowercase", deny_unknown_fields)] -pub(crate) enum NixAction { +pub(crate) enum RawNixAction { Msg { level: u8, msg: String, @@ -64,6 +357,9 @@ pub(crate) enum NixAction { #[serde(default, skip_serializing_if = "Option::is_none")] column: Option, + + #[serde(default, skip_serializing_if = "Option::is_none")] + trace: Option>, }, Start { id: u64, @@ -92,3 +388,293 @@ pub(crate) enum Field { Number(u64), Text(String), } + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub(crate) struct TraceEntry { + #[serde(default, skip_serializing_if = "Option::is_none")] + raw_msg: Option, + + #[serde(default, skip_serializing_if = "Option::is_none")] + file: Option, + + #[serde(default, skip_serializing_if = "Option::is_none")] + line: Option, + + #[serde(default, skip_serializing_if = "Option::is_none")] + column: Option, +} + +#[derive(Debug, Clone)] +pub(crate) enum NixAction { + Msg(MsgMessage), + Start(ActivityStartMessage), + Stop(StopMessage), + Result(ActivityResultMessage), +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub(crate) struct MsgMessage { + level: u8, + msg: String, + + #[serde(default, skip_serializing_if = "Option::is_none")] + raw_msg: Option, + + #[serde(default, skip_serializing_if = "Option::is_none")] + file: Option, + + #[serde(default, skip_serializing_if = "Option::is_none")] + line: Option, + + #[serde(default, skip_serializing_if = "Option::is_none")] + column: Option, + + #[serde(default, skip_serializing_if = "Option::is_none")] + trace: Option>, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub(crate) struct StopMessage { + pub(crate) id: u64, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub(crate) enum ActivityStartMessage { + Unknown(ActivityStartUnknown), + CopyPath(ActivityStartCopyPath), + FileTransfer(ActivityStartFileTransfer), + Realize(ActivityStartRealize), + CopyPaths(ActivityStartCopyPaths), + Builds(ActivityStartBuilds), + Build(ActivityStartBuild), + OptimizeStore(ActivityStartOptimizeStore), + VerifyPaths(ActivityStartVerifyPaths), + Substitute(ActivityStartSubstitute), + QueryPathInfo(ActivityStartQueryPathInfo), + PostBuildHook(ActivityStartPostBuildHook), + BuildWaiting(ActivityStartBuildWaiting), + FetchTree(ActivityStartFetchTree), +} + +impl ActivityStartMessage { + fn get_type(&self) -> u8 { + match self { + ActivityStartMessage::Unknown(_) => 0, + ActivityStartMessage::CopyPath(_) => 100, + ActivityStartMessage::FileTransfer(_) => 101, + ActivityStartMessage::Realize(_) => 102, + ActivityStartMessage::CopyPaths(_) => 103, + ActivityStartMessage::Builds(_) => 104, + ActivityStartMessage::Build(_) => 105, + ActivityStartMessage::OptimizeStore(_) => 106, + ActivityStartMessage::VerifyPaths(_) => 107, + ActivityStartMessage::Substitute(_) => 108, + ActivityStartMessage::QueryPathInfo(_) => 109, + ActivityStartMessage::PostBuildHook(_) => 110, + ActivityStartMessage::BuildWaiting(_) => 111, + ActivityStartMessage::FetchTree(_) => 112, + } + } + + pub(crate) fn get_id(&self) -> u64 { + match self { + ActivityStartMessage::Unknown(activity_start_unknown) => activity_start_unknown.id, + ActivityStartMessage::CopyPath(activity_start_copy_path) => activity_start_copy_path.id, + ActivityStartMessage::FileTransfer(activity_start_file_transfer) => { + activity_start_file_transfer.id + } + ActivityStartMessage::Realize(activity_start_realize) => activity_start_realize.id, + ActivityStartMessage::CopyPaths(activity_start_copy_paths) => { + activity_start_copy_paths.id + } + ActivityStartMessage::Builds(activity_start_builds) => activity_start_builds.id, + ActivityStartMessage::Build(activity_start_build) => activity_start_build.id, + ActivityStartMessage::OptimizeStore(activity_start_optimize_store) => { + activity_start_optimize_store.id + } + ActivityStartMessage::VerifyPaths(activity_start_verify_paths) => { + activity_start_verify_paths.id + } + ActivityStartMessage::Substitute(activity_start_substitute) => { + activity_start_substitute.id + } + ActivityStartMessage::QueryPathInfo(activity_start_query_path_info) => { + activity_start_query_path_info.id + } + ActivityStartMessage::PostBuildHook(activity_start_post_build_hook) => { + activity_start_post_build_hook.id + } + ActivityStartMessage::BuildWaiting(activity_start_build_waiting) => { + activity_start_build_waiting.id + } + ActivityStartMessage::FetchTree(activity_start_fetch_tree) => { + activity_start_fetch_tree.id + } + } + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub(crate) struct ActivityStartUnknown { + pub(crate) id: u64, + pub(crate) parent: u64, + pub(crate) level: u8, + pub(crate) text: String, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub(crate) struct ActivityStartCopyPath { + pub(crate) id: u64, + pub(crate) parent: u64, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub(crate) struct ActivityStartFileTransfer { + pub(crate) id: u64, + pub(crate) parent: u64, + pub(crate) level: u8, + pub(crate) text: String, + pub(crate) url: String, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub(crate) struct ActivityStartRealize { + pub(crate) id: u64, + pub(crate) parent: u64, + pub(crate) level: u8, + pub(crate) text: String, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub(crate) struct ActivityStartCopyPaths { + pub(crate) id: u64, + pub(crate) parent: u64, + pub(crate) level: u8, + pub(crate) text: String, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub(crate) struct ActivityStartBuilds { + pub(crate) id: u64, + pub(crate) parent: u64, + pub(crate) level: u8, + pub(crate) text: String, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub(crate) struct ActivityStartBuild { + pub(crate) id: u64, + pub(crate) parent: u64, + pub(crate) level: u8, + pub(crate) text: String, + pub(crate) path: String, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub(crate) struct ActivityStartOptimizeStore { + pub(crate) id: u64, + pub(crate) parent: u64, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub(crate) struct ActivityStartVerifyPaths { + pub(crate) id: u64, + pub(crate) parent: u64, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub(crate) struct ActivityStartSubstitute { + pub(crate) id: u64, + pub(crate) parent: u64, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub(crate) struct ActivityStartQueryPathInfo { + pub(crate) id: u64, + pub(crate) parent: u64, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub(crate) struct ActivityStartPostBuildHook { + pub(crate) id: u64, + pub(crate) parent: u64, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub(crate) struct ActivityStartBuildWaiting { + pub(crate) id: u64, + pub(crate) parent: u64, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub(crate) struct ActivityStartFetchTree { + pub(crate) id: u64, + pub(crate) parent: u64, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub(crate) enum ActivityResultMessage { + FileLinked(ActivityResultFileLinked), + BuildLogLine(ActivityResultBuildLogLine), + UntrustedPath(ActivityResultUntrustedPath), + CorruptedPath(ActivityResultCorruptedPath), + SetPhase(ActivityResultSetPhase), + Progress(ActivityResultProgress), + SetExpected(ActivityResultSetExpected), + PostBuildLogLine(ActivityResultPostBuildLogLine), + FetchStatus(ActivityResultFetchStatus), +} + +impl ActivityResultMessage { + fn get_type(&self) -> u8 { + match self { + ActivityResultMessage::FileLinked(_) => 100, + ActivityResultMessage::BuildLogLine(_) => 101, + ActivityResultMessage::UntrustedPath(_) => 102, + ActivityResultMessage::CorruptedPath(_) => 103, + ActivityResultMessage::SetPhase(_) => 104, + ActivityResultMessage::Progress(_) => 105, + ActivityResultMessage::SetExpected(_) => 106, + ActivityResultMessage::PostBuildLogLine(_) => 107, + ActivityResultMessage::FetchStatus(_) => 108, + } + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub(crate) struct ActivityResultFileLinked {} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub(crate) struct ActivityResultBuildLogLine {} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub(crate) struct ActivityResultUntrustedPath {} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub(crate) struct ActivityResultCorruptedPath {} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub(crate) struct ActivityResultSetPhase {} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub(crate) struct ActivityResultProgress {} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub(crate) struct ActivityResultSetExpected {} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub(crate) struct ActivityResultPostBuildLogLine {} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub(crate) struct ActivityResultFetchStatus {} + +fn string_field(fields: &Option>, ind: usize) -> &String { + match fields { + Some(fields) => match &fields[ind] { + Field::Number(_n) => panic!("Expected field {ind} to be text, but it is a number."), + Field::Text(t) => t, + }, + None => panic!("Expected fields but no fields present."), + } +} diff --git a/src/nix_util/running_build.rs b/src/nix_util/running_build.rs index eddd2f9..f93332d 100644 --- a/src/nix_util/running_build.rs +++ b/src/nix_util/running_build.rs @@ -1,21 +1,20 @@ +use std::borrow::Cow; use std::collections::BTreeMap; use sqlx::Row; use tokio::process::Child; use tracing::error; -use tracing::warn; use crate::Result; use crate::database::db_handle::DbHandle; +use crate::nix_util::nix_output_stream::ActivityStartMessage; use crate::nix_util::nix_output_stream::NixAction; use crate::nix_util::nix_output_stream::NixOutputStream; use crate::nix_util::output_stream::OutputStream; -use super::nix_output_stream::Field; +use super::nix_output_stream::ActivityResultMessage; use super::nix_output_stream::NixMessage; -const ACTIVITY_TYPE_: i32 = 10; - pub(crate) struct RunningBuild<'db> { db_handle: &'db DbHandle, activity_tree: BTreeMap, @@ -92,230 +91,143 @@ impl<'db> RunningBuild<'db> { } NixMessage::Action(nix_action) => nix_action, }; - match &message { - NixAction::Msg { - level, - msg, - raw_msg, - file, - line, - column, - } => { + match message { + NixAction::Msg(msg_message) => { // For now we can ignore the messages. } - NixAction::Start { - id, - fields, - level, - parent, - text, - r#type, - } => { - let entry = self.activity_tree.entry(*id); + NixAction::Start(activity_start_message) => { + let id = activity_start_message.get_id(); + let entry = self.activity_tree.entry(id); let entry = match entry { std::collections::btree_map::Entry::Vacant(vacant_entry) => vacant_entry, std::collections::btree_map::Entry::Occupied(_occupied_entry) => { panic!("Started an already started activity: {id}.") } }; - match r#type { - 0 => { + match activity_start_message { + ActivityStartMessage::Unknown(activity_start_unknown) => { entry.insert(Activity::Unknown(ActivityUnknown { - id: *id, - parent: *parent, + id: activity_start_unknown.id, + parent: activity_start_unknown.parent, state: ActivityState::default(), - level: *level, - text: text.to_owned(), + level: activity_start_unknown.level, + text: activity_start_unknown.text, })); } - 100 => { - // TODO: Haven't seen any of these. - warn!("Found CopyPath: {}", serde_json::to_string(&message)?); + ActivityStartMessage::CopyPath(activity_start_copy_path) => { entry.insert(Activity::CopyPath(ActivityCopyPath { - id: *id, - parent: *parent, + id: activity_start_copy_path.id, + parent: activity_start_copy_path.parent, state: ActivityState::default(), })); } - 101 => { - match fields { - Some(f) if f.len() > 1 => { - warn!( - "Found more than one field in ActivityFileTransfer: {}", - serde_json::to_string(&message)? - ); - } - _ => {} - }; + ActivityStartMessage::FileTransfer(activity_start_file_transfer) => { entry.insert(Activity::FileTransfer(ActivityFileTransfer { - id: *id, - parent: *parent, + id: activity_start_file_transfer.id, + parent: activity_start_file_transfer.parent, state: ActivityState::default(), - level: *level, - text: text.to_owned(), - url: string_field(fields, 0).to_owned(), + level: activity_start_file_transfer.level, + text: activity_start_file_transfer.text, + url: activity_start_file_transfer.url, })); } - 102 => { - match fields { - Some(f) if f.len() > 0 => { - warn!( - "Found fields in ActivityRealize: {}", - serde_json::to_string(&message)? - ); - } - _ => {} - }; - if text.len() > 0 { - warn!( - "Found Realize with text: {}", - serde_json::to_string(&message)? - ); - } + ActivityStartMessage::Realize(activity_start_realize) => { entry.insert(Activity::Realize(ActivityRealize { - id: *id, - parent: *parent, + id: activity_start_realize.id, + parent: activity_start_realize.parent, state: ActivityState::default(), - level: *level, - text: text.to_owned(), + level: activity_start_realize.level, + text: activity_start_realize.text, })); } - 103 => { - match fields { - Some(f) if f.len() > 0 => { - warn!( - "Found fields in CopyPaths: {}", - serde_json::to_string(&message)? - ); - } - _ => {} - }; - if text.len() > 0 { - warn!( - "Found CopyPaths with text: {}", - serde_json::to_string(&message)? - ); - } + ActivityStartMessage::CopyPaths(activity_start_copy_paths) => { entry.insert(Activity::CopyPaths(ActivityCopyPaths { - id: *id, - parent: *parent, + id: activity_start_copy_paths.id, + parent: activity_start_copy_paths.parent, state: ActivityState::default(), - level: *level, - text: text.to_owned(), + level: activity_start_copy_paths.level, + text: activity_start_copy_paths.text, })); } - 104 => { - match fields { - Some(f) if f.len() > 0 => { - warn!( - "Found fields in Builds: {}", - serde_json::to_string(&message)? - ); - } - _ => {} - }; - if text.len() > 0 { - warn!( - "Found Builds with text: {}", - serde_json::to_string(&message)? - ); - } + ActivityStartMessage::Builds(activity_start_builds) => { entry.insert(Activity::Builds(ActivityBuilds { - id: *id, - parent: *parent, + id: activity_start_builds.id, + parent: activity_start_builds.parent, state: ActivityState::default(), - level: *level, - text: text.to_owned(), + level: activity_start_builds.level, + text: activity_start_builds.text, })); } - 105 => { - // TODO: What are the other fields ["/nix/store/j54kvd8mlj8cl9ycvlkh5987fqvzl4p5-m4-1.4.20.tar.bz2.drv","",1,1] + ActivityStartMessage::Build(activity_start_build) => { entry.insert(Activity::Build(ActivityBuild { - id: *id, - parent: *parent, + id: activity_start_build.id, + parent: activity_start_build.parent, state: ActivityState::default(), - level: *level, - text: text.to_owned(), - path: string_field(fields, 0).to_owned(), + level: activity_start_build.level, + text: activity_start_build.text, + path: activity_start_build.path, })); } - 106 => { - // TODO: Haven't seen any of these. - warn!("Found OptimizeStore: {}", serde_json::to_string(&message)?); + ActivityStartMessage::OptimizeStore(activity_start_optimize_store) => { entry.insert(Activity::OptimizeStore(ActivityOptimizeStore { - id: *id, - parent: *parent, + id: activity_start_optimize_store.id, + parent: activity_start_optimize_store.parent, state: ActivityState::default(), })); } - 107 => { - // TODO: Haven't seen any of these. - warn!("Found VerifyPath: {}", serde_json::to_string(&message)?); + ActivityStartMessage::VerifyPaths(activity_start_verify_paths) => { entry.insert(Activity::VerifyPaths(ActivityVerifyPaths { - id: *id, - parent: *parent, + id: activity_start_verify_paths.id, + parent: activity_start_verify_paths.parent, state: ActivityState::default(), })); } - 108 => { - // TODO: Haven't seen any of these. - warn!("Found Subtitute: {}", serde_json::to_string(&message)?); + ActivityStartMessage::Substitute(activity_start_substitute) => { entry.insert(Activity::Substitute(ActivitySubstitute { - id: *id, - parent: *parent, + id: activity_start_substitute.id, + parent: activity_start_substitute.parent, state: ActivityState::default(), })); } - 109 => { - // TODO: Haven't seen any of these. - warn!("Found QueryPathInfo: {}", serde_json::to_string(&message)?); + ActivityStartMessage::QueryPathInfo(activity_start_query_path_info) => { entry.insert(Activity::QueryPathInfo(ActivityQueryPathInfo { - id: *id, - parent: *parent, + id: activity_start_query_path_info.id, + parent: activity_start_query_path_info.parent, state: ActivityState::default(), })); } - 110 => { - // TODO: Haven't seen any of these. - warn!("Found PostBuildHook: {}", serde_json::to_string(&message)?); + ActivityStartMessage::PostBuildHook(activity_start_post_build_hook) => { entry.insert(Activity::PostBuildHook(ActivityPostBuildHook { - id: *id, - parent: *parent, + id: activity_start_post_build_hook.id, + parent: activity_start_post_build_hook.parent, state: ActivityState::default(), })); } - 111 => { - // TODO: Haven't seen any of these. - warn!("Found BuildWaiting: {}", serde_json::to_string(&message)?); + ActivityStartMessage::BuildWaiting(activity_start_build_waiting) => { entry.insert(Activity::BuildWaiting(ActivityBuildWaiting { - id: *id, - parent: *parent, + id: activity_start_build_waiting.id, + parent: activity_start_build_waiting.parent, state: ActivityState::default(), })); } - 112 => { - // TODO: Haven't seen any of these. - warn!("Found FetchTree: {}", serde_json::to_string(&message)?); + ActivityStartMessage::FetchTree(activity_start_fetch_tree) => { entry.insert(Activity::FetchTree(ActivityFetchTree { - id: *id, - parent: *parent, + id: activity_start_fetch_tree.id, + parent: activity_start_fetch_tree.parent, state: ActivityState::default(), })); } - _ => { - panic!( - "Unhandled start activity: {}", - serde_json::to_string(&message)? - ); - } }; self.print_current_status(); } - NixAction::Stop { id } => { - let entry = self.activity_tree.entry(*id); + NixAction::Stop(stop_message) => { + let entry = self.activity_tree.entry(stop_message.id); match entry { std::collections::btree_map::Entry::Vacant(_vacant_entry) => { - panic!("Stopped an activity that is not in the tree: {id}"); + panic!( + "Stopped an activity that is not in the tree: {}", + stop_message.id + ); } std::collections::btree_map::Entry::Occupied(mut occupied_entry) => { occupied_entry.get_mut().stop(); @@ -324,56 +236,52 @@ impl<'db> RunningBuild<'db> { self.print_current_status(); // println!("{}", serde_json::to_string(&message)?); } - NixAction::Result { id, fields, r#type } => { - match r#type { - 100 => { + NixAction::Result(activity_result_message) => { + match activity_result_message { + ActivityResultMessage::FileLinked(activity_result_file_linked) => { // FileLinked // TODO: Haven't seen any of these. - warn!("Found FileLinked: {}", serde_json::to_string(&message)?); + // warn!("Found FileLinked: {}", serde_json::to_string(&message)?); } - 101 => { + ActivityResultMessage::BuildLogLine(activity_result_build_log_line) => { // BuildLogLine // The first field is a string containing the log line } - 102 => { + ActivityResultMessage::UntrustedPath(activity_result_untrusted_path) => { // UntrustedPath // TODO: Haven't seen any of these. - warn!("Found UntrustedPath: {}", serde_json::to_string(&message)?); + // warn!("Found UntrustedPath: {}", serde_json::to_string(&message)?); } - 103 => { + ActivityResultMessage::CorruptedPath(activity_result_corrupted_path) => { // CorruptedPath // TODO: Haven't seen any of these. - warn!("Found CorruptedPath: {}", serde_json::to_string(&message)?); + // warn!("Found CorruptedPath: {}", serde_json::to_string(&message)?); } - 104 => { - // SetPhase + ActivityResultMessage::SetPhase(activity_result_set_phase) => { // SetPhase // The first field is the phase name } - 105 => { - // Progress + ActivityResultMessage::Progress(activity_result_progress) => { // Progress // Fields numerator, denominator, running?, failed? } - 106 => { - // SetExpected + ActivityResultMessage::SetExpected(activity_result_set_expected) => { // SetExpected // Fields activity type?, expected? } - 107 => { + ActivityResultMessage::PostBuildLogLine( + activity_result_post_build_log_line, + ) => { // PostBuildLogLine // TODO: Haven't seen any of these. - warn!( - "Found PostBuildLogLine: {}", - serde_json::to_string(&message)? - ); + // warn!( + // "Found PostBuildLogLine: {}", + // serde_json::to_string(&message)? + // ); } - 108 => { + ActivityResultMessage::FetchStatus(activity_result_fetch_status) => { // FetchStatus // TODO: Haven't seen any of these. - warn!("Found FetchStatus: {}", serde_json::to_string(&message)?); + // warn!("Found FetchStatus: {}", serde_json::to_string(&message)?); // println!("{}", serde_json::to_string(&message)?); } - _ => { - panic!("Unhandled result: {}", serde_json::to_string(&message)?); - } }; } }; @@ -381,42 +289,15 @@ impl<'db> RunningBuild<'db> { } fn print_current_status(&self) -> () { - // let in_progress = self - // .activity_tree - // .iter() - // .filter(|activity: &(&u64, &ActivityTreeEntry)| activity.1.is_active()); - // let names: Vec<&str> = in_progress - // .map(|activity| match activity.1 { - // ActivityTreeEntry::Unknown { - // id, - // parent, - // r#type, - // state, - // } => "unknown", - // ActivityTreeEntry::System { - // id, - // parent, - // r#type, - // state, - // } => "system", - // ActivityTreeEntry::Download { - // id, - // parent, - // r#type, - // state, - // url, - // } => url, - // ActivityTreeEntry::Build { - // id, - // parent, - // r#type, - // state, - // path, - // } => path, - // }) - // .collect(); - // let name_list = names.join(", "); - // println!("In progress: {name_list}"); + let in_progress = self + .activity_tree + .iter() + .filter(|activity: &(&u64, &Activity)| activity.1.is_active()); + let names: Vec> = in_progress + .filter_map(|(id, activity)| activity.display_name()) + .collect(); + let name_list = names.join(", "); + println!("In progress: {name_list}"); } } @@ -460,25 +341,6 @@ enum Activity { } impl Activity { - fn get_type(&self) -> u8 { - match self { - Activity::Unknown(_) => 0, - Activity::CopyPath(_) => 100, - Activity::FileTransfer(_) => 101, - Activity::Realize(_) => 102, - Activity::CopyPaths(_) => 103, - Activity::Builds(_) => 104, - Activity::Build(_) => 105, - Activity::OptimizeStore(_) => 106, - Activity::VerifyPaths(_) => 107, - Activity::Substitute(_) => 108, - Activity::QueryPathInfo(_) => 109, - Activity::PostBuildHook(_) => 110, - Activity::BuildWaiting(_) => 111, - Activity::FetchTree(_) => 112, - } - } - fn stop(&mut self) -> () { match self { Activity::Unknown(activity_unknown) => { @@ -525,6 +387,120 @@ impl Activity { } } } + + fn is_active(&self) -> bool { + match self { + Activity::Unknown(activity_unknown) => { + matches!(activity_unknown.state, ActivityState::Started { .. }) + } + Activity::CopyPath(activity_copy_path) => { + matches!(activity_copy_path.state, ActivityState::Started { .. }) + } + Activity::FileTransfer(activity_file_transfer) => { + matches!(activity_file_transfer.state, ActivityState::Started { .. }) + } + Activity::Realize(activity_realize) => { + matches!(activity_realize.state, ActivityState::Started { .. }) + } + Activity::CopyPaths(activity_copy_paths) => { + matches!(activity_copy_paths.state, ActivityState::Started { .. }) + } + Activity::Builds(activity_builds) => { + matches!(activity_builds.state, ActivityState::Started { .. }) + } + Activity::Build(activity_build) => { + matches!(activity_build.state, ActivityState::Started { .. }) + } + Activity::OptimizeStore(activity_optimize_store) => { + matches!(activity_optimize_store.state, ActivityState::Started { .. }) + } + Activity::VerifyPaths(activity_verify_paths) => { + matches!(activity_verify_paths.state, ActivityState::Started { .. }) + } + Activity::Substitute(activity_substitute) => { + matches!(activity_substitute.state, ActivityState::Started { .. }) + } + Activity::QueryPathInfo(activity_query_path_info) => { + matches!( + activity_query_path_info.state, + ActivityState::Started { .. } + ) + } + Activity::PostBuildHook(activity_post_build_hook) => { + matches!( + activity_post_build_hook.state, + ActivityState::Started { .. } + ) + } + Activity::BuildWaiting(activity_build_waiting) => { + matches!(activity_build_waiting.state, ActivityState::Started { .. }) + } + Activity::FetchTree(activity_fetch_tree) => { + matches!(activity_fetch_tree.state, ActivityState::Started { .. }) + } + } + } + + fn display_name(&self) -> Option> { + match self { + Activity::Unknown(activity_unknown) => { + // TODO + Some(Cow::Borrowed("Unknown")) + } + Activity::CopyPath(activity_copy_path) => { + // TODO + Some(Cow::Borrowed("CopyPath")) + } + Activity::FileTransfer(activity_file_transfer) => { + // TODO + Some(Cow::Borrowed("FileTransfer")) + } + Activity::Realize(activity_realize) => { + // TODO + Some(Cow::Borrowed("Realize")) + } + Activity::CopyPaths(activity_copy_paths) => { + // TODO + Some(Cow::Borrowed("CopyPaths")) + } + Activity::Builds(activity_builds) => { + // TODO + Some(Cow::Borrowed("Builds")) + } + Activity::Build(activity_build) => { + // TODO + Some(Cow::Borrowed("Build")) + } + Activity::OptimizeStore(activity_optimize_store) => { + // TODO + Some(Cow::Borrowed("OptimizeStore")) + } + Activity::VerifyPaths(activity_verify_paths) => { + // TODO + Some(Cow::Borrowed("VerifyPaths")) + } + Activity::Substitute(activity_substitute) => { + // TODO + Some(Cow::Borrowed("Substitute")) + } + Activity::QueryPathInfo(activity_query_path_info) => { + // TODO + Some(Cow::Borrowed("QueryPathInfo")) + } + Activity::PostBuildHook(activity_post_build_hook) => { + // TODO + Some(Cow::Borrowed("PostBuildHook")) + } + Activity::BuildWaiting(activity_build_waiting) => { + // TODO + Some(Cow::Borrowed("BuildWaiting")) + } + Activity::FetchTree(activity_fetch_tree) => { + // TODO + Some(Cow::Borrowed("FetchTree")) + } + } + } } struct ActivityUnknown { @@ -611,51 +587,3 @@ struct ActivityFetchTree { parent: u64, state: ActivityState, } - -enum ActivityResult { - FileLinked(ResultFileLinked), - BuildLogLine(ResultBuildLogLine), - UntrustedPath(ResultUntrustedPath), - CorruptedPath(ResultCorruptedPath), - SetPhase(ResultSetPhase), - Progress(ResultProgress), - SetExpected(ResultSetExpected), - PostBuildLogLine(ResultPostBuildLogLine), - FetchStatus(ResultFetchStatus), -} - -impl ActivityResult { - fn get_type(&self) -> u8 { - match self { - ActivityResult::FileLinked(_) => 100, - ActivityResult::BuildLogLine(_) => 101, - ActivityResult::UntrustedPath(_) => 102, - ActivityResult::CorruptedPath(_) => 103, - ActivityResult::SetPhase(_) => 104, - ActivityResult::Progress(_) => 105, - ActivityResult::SetExpected(_) => 106, - ActivityResult::PostBuildLogLine(_) => 107, - ActivityResult::FetchStatus(_) => 108, - } - } -} - -struct ResultFileLinked {} -struct ResultBuildLogLine {} -struct ResultUntrustedPath {} -struct ResultCorruptedPath {} -struct ResultSetPhase {} -struct ResultProgress {} -struct ResultSetExpected {} -struct ResultPostBuildLogLine {} -struct ResultFetchStatus {} - -fn string_field(fields: &Option>, ind: usize) -> &String { - match fields { - Some(fields) => match &fields[ind] { - Field::Number(_n) => panic!("Expected field {ind} to be text, but it is a number."), - Field::Text(t) => t, - }, - None => panic!("Expected fields but no fields present."), - } -}