From 8d2dd015c4c3671f5234c50defa9d399eaac0652 Mon Sep 17 00:00:00 2001 From: Tom Alexander Date: Thu, 19 Feb 2026 18:39:01 -0500 Subject: [PATCH] Parse to a meaningful format in the nix output stream before hitting the running build. --- .gitignore | 1 + Cargo.toml | 4 - example_config.toml | 4 +- src/database/db_handle.rs | 2 +- src/error/error.rs | 15 + src/nix_util/high_level.rs | 4 +- src/nix_util/nix_output_stream.rs | 802 +++++++++++++++++++++++++++++- src/nix_util/running_build.rs | 505 ++++++++----------- 8 files changed, 1028 insertions(+), 309 deletions(-) diff --git a/.gitignore b/.gitignore index c35a4ed..1b43f17 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /target /work +/example_logs diff --git a/Cargo.toml b/Cargo.toml index b4fc51d..552eed7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,7 +28,3 @@ url = { version = "2.5.8", default-features = false, features = ["std"] } inherits = "release" lto = true strip = "symbols" - -[profile.dev.package.sqlx-macros] -# Faster compile-time verified macros -opt-level = 3 diff --git a/example_config.toml b/example_config.toml index 88ca3bf..e979de7 100644 --- a/example_config.toml +++ b/example_config.toml @@ -5,13 +5,13 @@ repo = "https://code.fizz.buzz/talexander/machine_setup.git" branch = "nix" path = "nix/configuration" - attr = "odo" + attr = "nixosConfigurations.odo.config.system.build.toplevel" [[targets]] name = "odo_update" repo = "https://code.fizz.buzz/talexander/machine_setup.git" branch = "nix" path = "nix/configuration" - attr = "odo" + attr = "nixosConfigurations.odo.config.system.build.toplevel" update = true update_branch = "nix_update" diff --git a/src/database/db_handle.rs b/src/database/db_handle.rs index 7a88c40..1a45206 100644 --- a/src/database/db_handle.rs +++ b/src/database/db_handle.rs @@ -62,7 +62,7 @@ impl DbHandle { options.connect(&full_url).await? } None => { - warn!("No sqlite_path set in config. Using an in-memory database."); + warn!("Using an in-memory database."); // We force it to a single connection that never dies or else the data and schema in the in-memory DB is lost. options .min_connections(1) diff --git a/src/error/error.rs b/src/error/error.rs index 795d8b9..41a30cf 100644 --- a/src/error/error.rs +++ b/src/error/error.rs @@ -1,3 +1,5 @@ +use std::fmt::Display; +use std::num::TryFromIntError; use std::str::Utf8Error; use std::string::FromUtf8Error; @@ -16,6 +18,13 @@ pub(crate) enum CustomError { UrlParseError(#[allow(dead_code)] url::ParseError), Migrate(#[allow(dead_code)] sqlx::migrate::MigrateError), Sql(#[allow(dead_code)] sqlx::Error), + TryFromIntError(#[allow(dead_code)] TryFromIntError), +} + +impl Display for CustomError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{:?}", self) + } } impl From for CustomError { @@ -95,3 +104,9 @@ impl From for CustomError { CustomError::Sql(value) } } + +impl From for CustomError { + fn from(value: TryFromIntError) -> Self { + CustomError::TryFromIntError(value) + } +} diff --git a/src/nix_util/high_level.rs b/src/nix_util/high_level.rs index 5de730d..53a6321 100644 --- a/src/nix_util/high_level.rs +++ b/src/nix_util/high_level.rs @@ -33,7 +33,7 @@ where reference }; - let mut command = Command::new("nixos-rebuild"); + let mut command = Command::new("nix"); command.current_dir(build_path); command.stdout(Stdio::piped()); command.stderr(Stdio::piped()); @@ -41,7 +41,6 @@ where command.args([ "build", "--show-trace", - "--sudo", "--max-jobs", "1", "--log-format", @@ -49,7 +48,6 @@ where "-vvvvvvvvvvv", "--keep-going", ]); - command.arg("--flake"); command.arg(reference); command.kill_on_drop(true); diff --git a/src/nix_util/nix_output_stream.rs b/src/nix_util/nix_output_stream.rs index 76b6c65..633e044 100644 --- a/src/nix_util/nix_output_stream.rs +++ b/src/nix_util/nix_output_stream.rs @@ -1,10 +1,83 @@ use serde::Deserialize; use serde::Serialize; use serde_json::Value; +use tracing::warn; use super::output_stream::OutputLineStream; use crate::Result; +use crate::error::CustomError; + +macro_rules! warn_if_len { + ($name:expr, $json:ident, $collection:expr, $($comparison:tt)*) => {{ + match $collection { + Some(f) if f.len() $($comparison)* => { + warn!( + "Found {}.len() {} in {}: {}", stringify!($collection), stringify!($($comparison)*), stringify!($name), $json + ); + } + Some(_) => {}, + _ => { + warn!( + "Found {} None in {}: {}", stringify!($collection), stringify!($name), $json + ); + } + }; + }}; +} + +macro_rules! warn_if_optional_len { + ($name:expr, $json:ident, $collection:expr, $($comparison:tt)*) => {{ + match $collection { + Some(f) if f.len() $($comparison)* => { + warn!( + "Found {}.len() {} in {}: {}", stringify!($collection), stringify!($($comparison)*), stringify!($name), $json + ); + } + Some(_) => {}, + _ => {} + }; + }}; +} + +macro_rules! warn_if_non_null { + ($name:expr, $json:ident, $collection:expr) => {{ + match $collection { + Some(_) => { + warn!( + "Found non-null {} in {}: {}", + stringify!($collection), + stringify!($name), + $json + ); + } + None => {} + }; + }}; +} + +macro_rules! warn_if_non_empty_string { + ($name:expr, $json:ident, $str:expr) => {{ + if $str.len() != 0 { + warn!( + "Found non-empty string {} in {}: {}", + stringify!($str), + stringify!($name), + $json + ); + } + }}; +} + +macro_rules! warn_if { + ($name:expr, $json:ident, $val:expr, $($comparison:tt)*) => {{ + if $val $($comparison)* { + warn!( + "Found {} {} in {}: {}", stringify!($val), stringify!($($comparison)*), stringify!($name), $json + ); + } + }}; +} pub(crate) struct NixOutputStream { inner: S, @@ -30,7 +103,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 +113,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 { + ActivityType::Unknown => { + warn_if_non_null!(ActivityStartUnknown, original_json, &fields); + + Ok(NixAction::Start(ActivityStartMessage::Unknown( + ActivityStartUnknown { + id, + parent, + level, + text, + }, + ))) + } + ActivityType::CopyPath => { + warn_if_len!(ActivityStartCopyPath, original_json, &fields, != 3); + warn_if!(ActivityStartCopyPath, original_json, level, != 3); + + let missing_path = string_field(&fields, 0).to_owned(); + let source = string_field(&fields, 1).to_owned(); + let destination = string_field(&fields, 2).to_owned(); + Ok(NixAction::Start(ActivityStartMessage::CopyPath( + ActivityStartCopyPath { + id, + parent, + text, + missing_path, + source, + destination, + }, + ))) + } + ActivityType::FileTransfer => { + warn_if_len!(ActivityStartFileTransfer, original_json, &fields, != 1); + warn_if!(ActivityStartFileTransfer, original_json, level, != 4); + let url = string_field(&fields, 0).to_owned(); + Ok(NixAction::Start(ActivityStartMessage::FileTransfer( + ActivityStartFileTransfer { + id, + parent, + text, + url, + }, + ))) + } + ActivityType::Realize => { + warn_if_non_empty_string!(ActivityStartRealize, original_json, text); + warn_if_non_null!(ActivityStartRealize, original_json, &fields); + warn_if!(ActivityStartRealize, original_json, level, != 0); + Ok(NixAction::Start(ActivityStartMessage::Realize( + ActivityStartRealize { id, parent }, + ))) + } + ActivityType::CopyPaths => { + warn_if_non_null!(ActivityStartCopyPaths, original_json, &fields); + // println!("{}", original_json); + Ok(NixAction::Start(ActivityStartMessage::CopyPaths( + ActivityStartCopyPaths { + id, + parent, + level, + text, + }, + ))) + } + ActivityType::Builds => { + // println!("{}", original_json); + warn_if_non_empty_string!(ActivityStartBuilds, original_json, text); + warn_if_non_null!(ActivityStartBuilds, original_json, &fields); + warn_if!(ActivityStartBuilds, original_json, level, != 0); + Ok(NixAction::Start(ActivityStartMessage::Builds( + ActivityStartBuilds { id, parent }, + ))) + } + ActivityType::Build => { + // println!("{}", original_json); + let drv_path = string_field(&fields, 0).to_owned(); + let machine_name = string_field(&fields, 1).to_owned(); + let unknown_constant_1 = number_field(&fields, 2).to_owned(); + let unknown_constant_2 = number_field(&fields, 3).to_owned(); + warn_if!(ActivityStartBuild, original_json, unknown_constant_1, != 1); + warn_if!(ActivityStartBuild, original_json, unknown_constant_2, != 1); + warn_if!(ActivityStartBuild, original_json, level, != 3); + + Ok(NixAction::Start(ActivityStartMessage::Build( + ActivityStartBuild { + id, + parent, + text, + drv_path, + machine_name, + }, + ))) + } + ActivityType::OptimizeStore => { + // println!("{}", original_json); + warn_if_non_empty_string!(ActivityStartOptimizeStore, original_json, text); + warn_if_non_null!(ActivityStartOptimizeStore, original_json, &fields); + warn_if!(ActivityStartOptimizeStore, original_json, level, != 0); + + Ok(NixAction::Start(ActivityStartMessage::OptimizeStore( + ActivityStartOptimizeStore { id, parent }, + ))) + } + ActivityType::VerifyPaths => { + // println!("{}", original_json); + warn_if_non_empty_string!(ActivityStartVerifyPaths, original_json, text); + warn_if_non_null!(ActivityStartVerifyPaths, original_json, &fields); + warn_if!(ActivityStartVerifyPaths, original_json, level, != 0); + Ok(NixAction::Start(ActivityStartMessage::VerifyPaths( + ActivityStartVerifyPaths { id, parent }, + ))) + } + ActivityType::Substitute => { + warn_if_non_empty_string!(ActivityStartSubstitute, original_json, text); + warn_if_non_null!(ActivityStartSubstitute, original_json, &fields); + warn_if!(ActivityStartSubstitute, original_json, level, != 0); + Ok(NixAction::Start(ActivityStartMessage::Substitute( + ActivityStartSubstitute { id, parent }, + ))) + } + ActivityType::QueryPathInfo => { + warn_if_non_empty_string!(ActivityStartQueryPathInfo, original_json, text); + warn_if_non_null!(ActivityStartQueryPathInfo, original_json, &fields); + warn_if!(ActivityStartQueryPathInfo, original_json, level, != 0); + Ok(NixAction::Start(ActivityStartMessage::QueryPathInfo( + ActivityStartQueryPathInfo { id, parent }, + ))) + } + ActivityType::PostBuildHook => { + warn_if_non_empty_string!(ActivityStartPostBuildHook, original_json, text); + warn_if_non_null!(ActivityStartPostBuildHook, original_json, &fields); + warn_if!(ActivityStartPostBuildHook, original_json, level, != 0); + Ok(NixAction::Start(ActivityStartMessage::PostBuildHook( + ActivityStartPostBuildHook { id, parent }, + ))) + } + ActivityType::BuildWaiting => { + warn_if_optional_len!(ActivityStartBuildWaiting, original_json, &fields, != 2); + warn_if!(ActivityStartBuildWaiting, original_json, parent, != 0); + let drv_path = optional_string_field(&fields, 0) + .to_owned() + .map(String::to_owned); + let path_resolved = optional_string_field(&fields, 1) + .to_owned() + .map(String::to_owned); + Ok(NixAction::Start(ActivityStartMessage::BuildWaiting( + ActivityStartBuildWaiting { + id, + level, + text, + drv_path, + path_resolved, + }, + ))) + } + ActivityType::FetchTree => { + warn_if_non_empty_string!(ActivityStartFetchTree, original_json, text); + warn_if_non_null!(ActivityStartFetchTree, original_json, &fields); + warn_if!(ActivityStartFetchTree, original_json, level, != 0); + Ok(NixAction::Start(ActivityStartMessage::FetchTree( + ActivityStartFetchTree { id, parent }, + ))) + } + } + } + RawNixAction::Stop { id } => Ok(NixAction::Stop(StopMessage { id })), + RawNixAction::Result { id, fields, r#type } => { + // Wrap fields in an option to be able able to use the same utility functions we use for the fields from RawNixAction::Start + let fields = Some(fields); + match r#type { + ResultType::FileLinked => { + warn_if_len!(ActivityResultFileLinked, original_json, &fields, != 0); + warn_if!(ActivityResultFileLinked, original_json, id, != 0); + + println!("{}", original_json); + Ok(NixAction::Result(ActivityResultMessage::FileLinked( + ActivityResultFileLinked {}, + ))) + } + ResultType::BuildLogLine => { + warn_if_len!(ActivityResultBuildLogLine, original_json, &fields, != 1); + let log_line = string_field(&fields, 0).to_owned(); + + Ok(NixAction::Result(ActivityResultMessage::BuildLogLine( + ActivityResultBuildLogLine { id, log_line }, + ))) + } + ResultType::UntrustedPath => { + warn_if_len!(ActivityResultUntrustedPath, original_json, &fields, != 0); + warn_if!(ActivityResultUntrustedPath, original_json, id, != 0); + + Ok(NixAction::Result(ActivityResultMessage::UntrustedPath( + ActivityResultUntrustedPath {}, + ))) + } + ResultType::CorruptedPath => { + warn_if_len!(ActivityResultCorruptedPath, original_json, &fields, != 0); + warn_if!(ActivityResultCorruptedPath, original_json, id, != 0); + + Ok(NixAction::Result(ActivityResultMessage::CorruptedPath( + ActivityResultCorruptedPath {}, + ))) + } + ResultType::SetPhase => { + warn_if_len!(ActivityResultSetPhase, original_json, &fields, != 1); + let phase = string_field(&fields, 0).to_owned(); + + Ok(NixAction::Result(ActivityResultMessage::SetPhase( + ActivityResultSetPhase { id, phase }, + ))) + } + ResultType::Progress => { + warn_if_len!(ActivityResultProgress, original_json, &fields, != 4); + let done = number_field(&fields, 0).to_owned(); + let expected = number_field(&fields, 1).to_owned(); + let running = number_field(&fields, 2).to_owned().try_into()?; + let failed = number_field(&fields, 3).to_owned().try_into()?; + + Ok(NixAction::Result(ActivityResultMessage::Progress( + ActivityResultProgress { + id, + done, + expected, + running, + failed, + }, + ))) + } + ResultType::SetExpected => { + warn_if_len!(ActivityResultSetExpected, original_json, &fields, != 2); + // TODO: Maybe map activity_type to an enum? + let activity_type = number_field(&fields, 0).to_owned().try_into()?; + let expected = number_field(&fields, 1).to_owned(); + + Ok(NixAction::Result(ActivityResultMessage::SetExpected( + ActivityResultSetExpected { + id, + activity_type, + expected, + }, + ))) + } + ResultType::PostBuildLogLine => { + warn_if_len!(ActivityResultPostBuildLogLine, original_json, &fields, != 0); + warn_if!(ActivityResultPostBuildLogLine, original_json, id, != 0); + + Ok(NixAction::Result(ActivityResultMessage::PostBuildLogLine( + ActivityResultPostBuildLogLine {}, + ))) + } + ResultType::FetchStatus => { + warn_if_len!(ActivityResultFetchStatus, original_json, &fields, != 0); + warn_if!(ActivityResultFetchStatus, original_json, id, != 0); + + Ok(NixAction::Result(ActivityResultMessage::FetchStatus( + ActivityResultFetchStatus {}, + ))) + } + } + } + } +} + #[derive(Debug, Clone)] pub(crate) enum NixMessage { ParseFailure(String), @@ -46,9 +411,107 @@ pub(crate) enum NixMessage { Action(NixAction), } +#[derive(Debug, Clone, Serialize, Deserialize)] +#[repr(u8)] +#[serde(try_from = "u64", into = "u8")] +pub(crate) enum ActivityType { + Unknown = 0, + CopyPath = 100, + FileTransfer = 101, + Realize = 102, + CopyPaths = 103, + Builds = 104, + Build = 105, + OptimizeStore = 106, + VerifyPaths = 107, + Substitute = 108, + QueryPathInfo = 109, + PostBuildHook = 110, + BuildWaiting = 111, + FetchTree = 112, +} + +impl TryFrom for ActivityType { + type Error = CustomError; + + fn try_from(value: u64) -> std::result::Result { + Ok(match value { + 0 => ActivityType::Unknown, + 100 => ActivityType::CopyPath, + 101 => ActivityType::FileTransfer, + 102 => ActivityType::Realize, + 103 => ActivityType::CopyPaths, + 104 => ActivityType::Builds, + 105 => ActivityType::Build, + 106 => ActivityType::OptimizeStore, + 107 => ActivityType::VerifyPaths, + 108 => ActivityType::Substitute, + 109 => ActivityType::QueryPathInfo, + 110 => ActivityType::PostBuildHook, + 111 => ActivityType::BuildWaiting, + 112 => ActivityType::FetchTree, + _ => { + return Err(CustomError::String(format!( + "Unexpected ActivityType: {value}" + ))); + } + }) + } +} + +impl Into for ActivityType { + fn into(self) -> u8 { + self as u8 + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[repr(u8)] +#[serde(try_from = "u64", into = "u8")] +pub(crate) enum ResultType { + FileLinked = 100, + BuildLogLine = 101, + UntrustedPath = 102, + CorruptedPath = 103, + SetPhase = 104, + Progress = 105, + SetExpected = 106, + PostBuildLogLine = 107, + FetchStatus = 108, +} + +impl TryFrom for ResultType { + type Error = CustomError; + + fn try_from(value: u64) -> std::result::Result { + Ok(match value { + 100 => ResultType::FileLinked, + 101 => ResultType::BuildLogLine, + 102 => ResultType::UntrustedPath, + 103 => ResultType::CorruptedPath, + 104 => ResultType::SetPhase, + 105 => ResultType::Progress, + 106 => ResultType::SetExpected, + 107 => ResultType::PostBuildLogLine, + 108 => ResultType::FetchStatus, + _ => { + return Err(CustomError::String(format!( + "Unexpected ResultType: {value}" + ))); + } + }) + } +} + +impl Into for ResultType { + fn into(self) -> u8 { + self as u8 + } +} + #[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 +527,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, @@ -74,7 +540,7 @@ pub(crate) enum NixAction { level: u8, parent: u64, text: String, - r#type: u8, + r#type: ActivityType, }, Stop { id: u64, @@ -82,7 +548,7 @@ pub(crate) enum NixAction { Result { id: u64, fields: Vec, - r#type: u8, + r#type: ResultType, }, } @@ -92,3 +558,331 @@ 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) -> ActivityType { + match self { + ActivityStartMessage::Unknown(_) => ActivityType::Unknown, + ActivityStartMessage::CopyPath(_) => ActivityType::CopyPath, + ActivityStartMessage::FileTransfer(_) => ActivityType::FileTransfer, + ActivityStartMessage::Realize(_) => ActivityType::Realize, + ActivityStartMessage::CopyPaths(_) => ActivityType::CopyPaths, + ActivityStartMessage::Builds(_) => ActivityType::Builds, + ActivityStartMessage::Build(_) => ActivityType::Build, + ActivityStartMessage::OptimizeStore(_) => ActivityType::OptimizeStore, + ActivityStartMessage::VerifyPaths(_) => ActivityType::VerifyPaths, + ActivityStartMessage::Substitute(_) => ActivityType::Substitute, + ActivityStartMessage::QueryPathInfo(_) => ActivityType::QueryPathInfo, + ActivityStartMessage::PostBuildHook(_) => ActivityType::PostBuildHook, + ActivityStartMessage::BuildWaiting(_) => ActivityType::BuildWaiting, + ActivityStartMessage::FetchTree(_) => ActivityType::FetchTree, + } + } + + 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, + pub(crate) text: String, + pub(crate) missing_path: String, + pub(crate) source: String, + pub(crate) destination: String, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub(crate) struct ActivityStartFileTransfer { + pub(crate) id: u64, + pub(crate) parent: u64, + 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, +} + +#[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, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub(crate) struct ActivityStartBuild { + pub(crate) id: u64, + pub(crate) parent: u64, + pub(crate) text: String, + pub(crate) drv_path: String, + pub(crate) machine_name: 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) level: u8, + pub(crate) text: String, + pub(crate) drv_path: Option, + pub(crate) path_resolved: Option, +} + +#[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) -> ResultType { + match self { + ActivityResultMessage::FileLinked(_) => ResultType::FileLinked, + ActivityResultMessage::BuildLogLine(_) => ResultType::BuildLogLine, + ActivityResultMessage::UntrustedPath(_) => ResultType::UntrustedPath, + ActivityResultMessage::CorruptedPath(_) => ResultType::CorruptedPath, + ActivityResultMessage::SetPhase(_) => ResultType::SetPhase, + ActivityResultMessage::Progress(_) => ResultType::Progress, + ActivityResultMessage::SetExpected(_) => ResultType::SetExpected, + ActivityResultMessage::PostBuildLogLine(_) => ResultType::PostBuildLogLine, + ActivityResultMessage::FetchStatus(_) => ResultType::FetchStatus, + } + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub(crate) struct ActivityResultFileLinked {} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub(crate) struct ActivityResultBuildLogLine { + pub(crate) id: u64, + pub(crate) log_line: String, +} + +#[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 { + pub(crate) id: u64, + pub(crate) phase: String, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub(crate) struct ActivityResultProgress { + pub(crate) id: u64, + pub(crate) done: u64, + pub(crate) expected: u64, + pub(crate) running: u8, + pub(crate) failed: u8, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub(crate) struct ActivityResultSetExpected { + pub(crate) id: u64, + pub(crate) activity_type: ActivityType, + pub(crate) expected: u64, +} + +#[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."), + } +} + +fn optional_string_field(fields: &Option>, ind: usize) -> Option<&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) => Some(t), + }, + None => None, + } +} + +fn number_field(fields: &Option>, ind: usize) -> &u64 { + match fields { + Some(fields) => match &fields[ind] { + Field::Number(n) => n, + Field::Text(t) => panic!("Expected field {ind} to be a number, but it is text."), + }, + 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..65a7865 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, @@ -62,7 +61,7 @@ impl<'db> RunningBuild<'db> { } let exit_status = exit_status_handle.await?; - println!("nixos-rebuild status was: {}", exit_status); + println!("nix build status was: {}", exit_status); let update: u64 = sqlx::query(r#"UPDATE build SET end_time=unixepoch('now'), status=? WHERE id=?"#) @@ -92,230 +91,136 @@ 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(), + 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(), })); } - 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(), })); } - 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(), + text: activity_start_build.text, + drv_path: activity_start_build.drv_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, 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 +229,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,41 +282,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(", "); + 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(", "); + // TODO: Make a meaningful current status. // println!("In progress: {name_list}"); } } @@ -460,25 +335,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 +381,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 { @@ -543,7 +513,6 @@ struct ActivityFileTransfer { id: u64, parent: u64, state: ActivityState, - level: u8, text: String, url: String, } @@ -551,8 +520,6 @@ struct ActivityRealize { id: u64, parent: u64, state: ActivityState, - level: u8, - text: String, } struct ActivityCopyPaths { id: u64, @@ -565,16 +532,13 @@ struct ActivityBuilds { id: u64, parent: u64, state: ActivityState, - level: u8, - text: String, } struct ActivityBuild { id: u64, parent: u64, state: ActivityState, - level: u8, text: String, - path: String, + drv_path: String, } struct ActivityOptimizeStore { id: u64, @@ -603,7 +567,6 @@ struct ActivityPostBuildHook { } struct ActivityBuildWaiting { id: u64, - parent: u64, state: ActivityState, } struct ActivityFetchTree { @@ -611,51 +574,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."), - } -}