Parse to a meaningful format in the nix output stream before hitting the running build.

This commit is contained in:
Tom Alexander
2026-02-19 18:39:01 -05:00
parent f797a19175
commit 8d2dd015c4
8 changed files with 1028 additions and 309 deletions

1
.gitignore vendored
View File

@@ -1,2 +1,3 @@
/target /target
/work /work
/example_logs

View File

@@ -28,7 +28,3 @@ url = { version = "2.5.8", default-features = false, features = ["std"] }
inherits = "release" inherits = "release"
lto = true lto = true
strip = "symbols" strip = "symbols"
[profile.dev.package.sqlx-macros]
# Faster compile-time verified macros
opt-level = 3

View File

@@ -5,13 +5,13 @@
repo = "https://code.fizz.buzz/talexander/machine_setup.git" repo = "https://code.fizz.buzz/talexander/machine_setup.git"
branch = "nix" branch = "nix"
path = "nix/configuration" path = "nix/configuration"
attr = "odo" attr = "nixosConfigurations.odo.config.system.build.toplevel"
[[targets]] [[targets]]
name = "odo_update" name = "odo_update"
repo = "https://code.fizz.buzz/talexander/machine_setup.git" repo = "https://code.fizz.buzz/talexander/machine_setup.git"
branch = "nix" branch = "nix"
path = "nix/configuration" path = "nix/configuration"
attr = "odo" attr = "nixosConfigurations.odo.config.system.build.toplevel"
update = true update = true
update_branch = "nix_update" update_branch = "nix_update"

View File

@@ -62,7 +62,7 @@ impl DbHandle {
options.connect(&full_url).await? options.connect(&full_url).await?
} }
None => { 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. // We force it to a single connection that never dies or else the data and schema in the in-memory DB is lost.
options options
.min_connections(1) .min_connections(1)

View File

@@ -1,3 +1,5 @@
use std::fmt::Display;
use std::num::TryFromIntError;
use std::str::Utf8Error; use std::str::Utf8Error;
use std::string::FromUtf8Error; use std::string::FromUtf8Error;
@@ -16,6 +18,13 @@ pub(crate) enum CustomError {
UrlParseError(#[allow(dead_code)] url::ParseError), UrlParseError(#[allow(dead_code)] url::ParseError),
Migrate(#[allow(dead_code)] sqlx::migrate::MigrateError), Migrate(#[allow(dead_code)] sqlx::migrate::MigrateError),
Sql(#[allow(dead_code)] sqlx::Error), 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<std::io::Error> for CustomError { impl From<std::io::Error> for CustomError {
@@ -95,3 +104,9 @@ impl From<sqlx::Error> for CustomError {
CustomError::Sql(value) CustomError::Sql(value)
} }
} }
impl From<TryFromIntError> for CustomError {
fn from(value: TryFromIntError) -> Self {
CustomError::TryFromIntError(value)
}
}

View File

@@ -33,7 +33,7 @@ where
reference reference
}; };
let mut command = Command::new("nixos-rebuild"); let mut command = Command::new("nix");
command.current_dir(build_path); command.current_dir(build_path);
command.stdout(Stdio::piped()); command.stdout(Stdio::piped());
command.stderr(Stdio::piped()); command.stderr(Stdio::piped());
@@ -41,7 +41,6 @@ where
command.args([ command.args([
"build", "build",
"--show-trace", "--show-trace",
"--sudo",
"--max-jobs", "--max-jobs",
"1", "1",
"--log-format", "--log-format",
@@ -49,7 +48,6 @@ where
"-vvvvvvvvvvv", "-vvvvvvvvvvv",
"--keep-going", "--keep-going",
]); ]);
command.arg("--flake");
command.arg(reference); command.arg(reference);
command.kill_on_drop(true); command.kill_on_drop(true);

View File

@@ -1,10 +1,83 @@
use serde::Deserialize; use serde::Deserialize;
use serde::Serialize; use serde::Serialize;
use serde_json::Value; use serde_json::Value;
use tracing::warn;
use super::output_stream::OutputLineStream; use super::output_stream::OutputLineStream;
use crate::Result; 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<S> { pub(crate) struct NixOutputStream<S> {
inner: S, inner: S,
@@ -30,7 +103,8 @@ impl<S: OutputLineStream> NixOutputStream<S> {
let payload = &line[4..]; let payload = &line[4..];
if let Ok(action) = serde_json::from_str(&payload) { 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) { if let Ok(parsed) = serde_json::from_str(&payload) {
return Ok(Some(NixMessage::Generic(parsed, line))); return Ok(Some(NixMessage::Generic(parsed, line)));
@@ -39,6 +113,297 @@ impl<S: OutputLineStream> NixOutputStream<S> {
} }
} }
fn parse_action(raw: RawNixAction) -> Result<NixAction> {
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)] #[derive(Debug, Clone)]
pub(crate) enum NixMessage { pub(crate) enum NixMessage {
ParseFailure(String), ParseFailure(String),
@@ -46,9 +411,107 @@ pub(crate) enum NixMessage {
Action(NixAction), 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<u64> for ActivityType {
type Error = CustomError;
fn try_from(value: u64) -> std::result::Result<Self, Self::Error> {
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<u8> 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<u64> for ResultType {
type Error = CustomError;
fn try_from(value: u64) -> std::result::Result<Self, Self::Error> {
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<u8> for ResultType {
fn into(self) -> u8 {
self as u8
}
}
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "action", rename_all = "lowercase", deny_unknown_fields)] #[serde(tag = "action", rename_all = "lowercase", deny_unknown_fields)]
pub(crate) enum NixAction { pub(crate) enum RawNixAction {
Msg { Msg {
level: u8, level: u8,
msg: String, msg: String,
@@ -64,6 +527,9 @@ pub(crate) enum NixAction {
#[serde(default, skip_serializing_if = "Option::is_none")] #[serde(default, skip_serializing_if = "Option::is_none")]
column: Option<u64>, column: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
trace: Option<Vec<TraceEntry>>,
}, },
Start { Start {
id: u64, id: u64,
@@ -74,7 +540,7 @@ pub(crate) enum NixAction {
level: u8, level: u8,
parent: u64, parent: u64,
text: String, text: String,
r#type: u8, r#type: ActivityType,
}, },
Stop { Stop {
id: u64, id: u64,
@@ -82,7 +548,7 @@ pub(crate) enum NixAction {
Result { Result {
id: u64, id: u64,
fields: Vec<Field>, fields: Vec<Field>,
r#type: u8, r#type: ResultType,
}, },
} }
@@ -92,3 +558,331 @@ pub(crate) enum Field {
Number(u64), Number(u64),
Text(String), 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<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
file: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
line: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
column: Option<u64>,
}
#[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<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
file: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
line: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
column: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
trace: Option<Vec<TraceEntry>>,
}
#[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<String>,
pub(crate) path_resolved: Option<String>,
}
#[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<Vec<Field>>, 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<Vec<Field>>, 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<Vec<Field>>, 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."),
}
}

View File

@@ -1,21 +1,20 @@
use std::borrow::Cow;
use std::collections::BTreeMap; use std::collections::BTreeMap;
use sqlx::Row; use sqlx::Row;
use tokio::process::Child; use tokio::process::Child;
use tracing::error; use tracing::error;
use tracing::warn;
use crate::Result; use crate::Result;
use crate::database::db_handle::DbHandle; 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::NixAction;
use crate::nix_util::nix_output_stream::NixOutputStream; use crate::nix_util::nix_output_stream::NixOutputStream;
use crate::nix_util::output_stream::OutputStream; 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; use super::nix_output_stream::NixMessage;
const ACTIVITY_TYPE_: i32 = 10;
pub(crate) struct RunningBuild<'db> { pub(crate) struct RunningBuild<'db> {
db_handle: &'db DbHandle, db_handle: &'db DbHandle,
activity_tree: BTreeMap<u64, Activity>, activity_tree: BTreeMap<u64, Activity>,
@@ -62,7 +61,7 @@ impl<'db> RunningBuild<'db> {
} }
let exit_status = exit_status_handle.await?; let exit_status = exit_status_handle.await?;
println!("nixos-rebuild status was: {}", exit_status); println!("nix build status was: {}", exit_status);
let update: u64 = let update: u64 =
sqlx::query(r#"UPDATE build SET end_time=unixepoch('now'), status=? WHERE id=?"#) 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, NixMessage::Action(nix_action) => nix_action,
}; };
match &message { match message {
NixAction::Msg { NixAction::Msg(msg_message) => {
level,
msg,
raw_msg,
file,
line,
column,
} => {
// For now we can ignore the messages. // For now we can ignore the messages.
} }
NixAction::Start { NixAction::Start(activity_start_message) => {
id, let id = activity_start_message.get_id();
fields, let entry = self.activity_tree.entry(id);
level,
parent,
text,
r#type,
} => {
let entry = self.activity_tree.entry(*id);
let entry = match entry { let entry = match entry {
std::collections::btree_map::Entry::Vacant(vacant_entry) => vacant_entry, std::collections::btree_map::Entry::Vacant(vacant_entry) => vacant_entry,
std::collections::btree_map::Entry::Occupied(_occupied_entry) => { std::collections::btree_map::Entry::Occupied(_occupied_entry) => {
panic!("Started an already started activity: {id}.") panic!("Started an already started activity: {id}.")
} }
}; };
match r#type { match activity_start_message {
0 => { ActivityStartMessage::Unknown(activity_start_unknown) => {
entry.insert(Activity::Unknown(ActivityUnknown { entry.insert(Activity::Unknown(ActivityUnknown {
id: *id, id: activity_start_unknown.id,
parent: *parent, parent: activity_start_unknown.parent,
state: ActivityState::default(), state: ActivityState::default(),
level: *level, level: activity_start_unknown.level,
text: text.to_owned(), text: activity_start_unknown.text,
})); }));
} }
100 => { ActivityStartMessage::CopyPath(activity_start_copy_path) => {
// TODO: Haven't seen any of these.
warn!("Found CopyPath: {}", serde_json::to_string(&message)?);
entry.insert(Activity::CopyPath(ActivityCopyPath { entry.insert(Activity::CopyPath(ActivityCopyPath {
id: *id, id: activity_start_copy_path.id,
parent: *parent, parent: activity_start_copy_path.parent,
state: ActivityState::default(), state: ActivityState::default(),
})); }));
} }
101 => { ActivityStartMessage::FileTransfer(activity_start_file_transfer) => {
match fields {
Some(f) if f.len() > 1 => {
warn!(
"Found more than one field in ActivityFileTransfer: {}",
serde_json::to_string(&message)?
);
}
_ => {}
};
entry.insert(Activity::FileTransfer(ActivityFileTransfer { entry.insert(Activity::FileTransfer(ActivityFileTransfer {
id: *id, id: activity_start_file_transfer.id,
parent: *parent, parent: activity_start_file_transfer.parent,
state: ActivityState::default(), state: ActivityState::default(),
level: *level, text: activity_start_file_transfer.text,
text: text.to_owned(), url: activity_start_file_transfer.url,
url: string_field(fields, 0).to_owned(),
})); }));
} }
102 => { ActivityStartMessage::Realize(activity_start_realize) => {
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)?
);
}
entry.insert(Activity::Realize(ActivityRealize { entry.insert(Activity::Realize(ActivityRealize {
id: *id, id: activity_start_realize.id,
parent: *parent, parent: activity_start_realize.parent,
state: ActivityState::default(), state: ActivityState::default(),
level: *level,
text: text.to_owned(),
})); }));
} }
103 => { ActivityStartMessage::CopyPaths(activity_start_copy_paths) => {
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)?
);
}
entry.insert(Activity::CopyPaths(ActivityCopyPaths { entry.insert(Activity::CopyPaths(ActivityCopyPaths {
id: *id, id: activity_start_copy_paths.id,
parent: *parent, parent: activity_start_copy_paths.parent,
state: ActivityState::default(), state: ActivityState::default(),
level: *level, level: activity_start_copy_paths.level,
text: text.to_owned(), text: activity_start_copy_paths.text,
})); }));
} }
104 => { ActivityStartMessage::Builds(activity_start_builds) => {
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)?
);
}
entry.insert(Activity::Builds(ActivityBuilds { entry.insert(Activity::Builds(ActivityBuilds {
id: *id, id: activity_start_builds.id,
parent: *parent, parent: activity_start_builds.parent,
state: ActivityState::default(), state: ActivityState::default(),
level: *level,
text: text.to_owned(),
})); }));
} }
105 => { ActivityStartMessage::Build(activity_start_build) => {
// TODO: What are the other fields ["/nix/store/j54kvd8mlj8cl9ycvlkh5987fqvzl4p5-m4-1.4.20.tar.bz2.drv","",1,1]
entry.insert(Activity::Build(ActivityBuild { entry.insert(Activity::Build(ActivityBuild {
id: *id, id: activity_start_build.id,
parent: *parent, parent: activity_start_build.parent,
state: ActivityState::default(), state: ActivityState::default(),
level: *level, text: activity_start_build.text,
text: text.to_owned(), drv_path: activity_start_build.drv_path,
path: string_field(fields, 0).to_owned(),
})); }));
} }
106 => { ActivityStartMessage::OptimizeStore(activity_start_optimize_store) => {
// TODO: Haven't seen any of these.
warn!("Found OptimizeStore: {}", serde_json::to_string(&message)?);
entry.insert(Activity::OptimizeStore(ActivityOptimizeStore { entry.insert(Activity::OptimizeStore(ActivityOptimizeStore {
id: *id, id: activity_start_optimize_store.id,
parent: *parent, parent: activity_start_optimize_store.parent,
state: ActivityState::default(), state: ActivityState::default(),
})); }));
} }
107 => { ActivityStartMessage::VerifyPaths(activity_start_verify_paths) => {
// TODO: Haven't seen any of these.
warn!("Found VerifyPath: {}", serde_json::to_string(&message)?);
entry.insert(Activity::VerifyPaths(ActivityVerifyPaths { entry.insert(Activity::VerifyPaths(ActivityVerifyPaths {
id: *id, id: activity_start_verify_paths.id,
parent: *parent, parent: activity_start_verify_paths.parent,
state: ActivityState::default(), state: ActivityState::default(),
})); }));
} }
108 => { ActivityStartMessage::Substitute(activity_start_substitute) => {
// TODO: Haven't seen any of these.
warn!("Found Subtitute: {}", serde_json::to_string(&message)?);
entry.insert(Activity::Substitute(ActivitySubstitute { entry.insert(Activity::Substitute(ActivitySubstitute {
id: *id, id: activity_start_substitute.id,
parent: *parent, parent: activity_start_substitute.parent,
state: ActivityState::default(), state: ActivityState::default(),
})); }));
} }
109 => { ActivityStartMessage::QueryPathInfo(activity_start_query_path_info) => {
// TODO: Haven't seen any of these.
warn!("Found QueryPathInfo: {}", serde_json::to_string(&message)?);
entry.insert(Activity::QueryPathInfo(ActivityQueryPathInfo { entry.insert(Activity::QueryPathInfo(ActivityQueryPathInfo {
id: *id, id: activity_start_query_path_info.id,
parent: *parent, parent: activity_start_query_path_info.parent,
state: ActivityState::default(), state: ActivityState::default(),
})); }));
} }
110 => { ActivityStartMessage::PostBuildHook(activity_start_post_build_hook) => {
// TODO: Haven't seen any of these.
warn!("Found PostBuildHook: {}", serde_json::to_string(&message)?);
entry.insert(Activity::PostBuildHook(ActivityPostBuildHook { entry.insert(Activity::PostBuildHook(ActivityPostBuildHook {
id: *id, id: activity_start_post_build_hook.id,
parent: *parent, parent: activity_start_post_build_hook.parent,
state: ActivityState::default(), state: ActivityState::default(),
})); }));
} }
111 => { ActivityStartMessage::BuildWaiting(activity_start_build_waiting) => {
// TODO: Haven't seen any of these.
warn!("Found BuildWaiting: {}", serde_json::to_string(&message)?);
entry.insert(Activity::BuildWaiting(ActivityBuildWaiting { entry.insert(Activity::BuildWaiting(ActivityBuildWaiting {
id: *id, id: activity_start_build_waiting.id,
parent: *parent,
state: ActivityState::default(), state: ActivityState::default(),
})); }));
} }
112 => { ActivityStartMessage::FetchTree(activity_start_fetch_tree) => {
// TODO: Haven't seen any of these.
warn!("Found FetchTree: {}", serde_json::to_string(&message)?);
entry.insert(Activity::FetchTree(ActivityFetchTree { entry.insert(Activity::FetchTree(ActivityFetchTree {
id: *id, id: activity_start_fetch_tree.id,
parent: *parent, parent: activity_start_fetch_tree.parent,
state: ActivityState::default(), state: ActivityState::default(),
})); }));
} }
_ => {
panic!(
"Unhandled start activity: {}",
serde_json::to_string(&message)?
);
}
}; };
self.print_current_status(); self.print_current_status();
} }
NixAction::Stop { id } => { NixAction::Stop(stop_message) => {
let entry = self.activity_tree.entry(*id); let entry = self.activity_tree.entry(stop_message.id);
match entry { match entry {
std::collections::btree_map::Entry::Vacant(_vacant_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) => { std::collections::btree_map::Entry::Occupied(mut occupied_entry) => {
occupied_entry.get_mut().stop(); occupied_entry.get_mut().stop();
@@ -324,56 +229,52 @@ impl<'db> RunningBuild<'db> {
self.print_current_status(); self.print_current_status();
// println!("{}", serde_json::to_string(&message)?); // println!("{}", serde_json::to_string(&message)?);
} }
NixAction::Result { id, fields, r#type } => { NixAction::Result(activity_result_message) => {
match r#type { match activity_result_message {
100 => { ActivityResultMessage::FileLinked(activity_result_file_linked) => {
// FileLinked // FileLinked
// TODO: Haven't seen any of these. // 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 // BuildLogLine
// The first field is a string containing the log line // The first field is a string containing the log line
} }
102 => { ActivityResultMessage::UntrustedPath(activity_result_untrusted_path) => {
// UntrustedPath // UntrustedPath
// TODO: Haven't seen any of these. // 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 // CorruptedPath
// TODO: Haven't seen any of these. // TODO: Haven't seen any of these.
warn!("Found CorruptedPath: {}", serde_json::to_string(&message)?); // warn!("Found CorruptedPath: {}", serde_json::to_string(&message)?);
} }
104 => { ActivityResultMessage::SetPhase(activity_result_set_phase) => { // SetPhase
// SetPhase
// The first field is the phase name // The first field is the phase name
} }
105 => { ActivityResultMessage::Progress(activity_result_progress) => { // Progress
// Progress
// Fields numerator, denominator, running?, failed? // Fields numerator, denominator, running?, failed?
} }
106 => { ActivityResultMessage::SetExpected(activity_result_set_expected) => { // SetExpected
// SetExpected
// Fields activity type?, expected? // Fields activity type?, expected?
} }
107 => { ActivityResultMessage::PostBuildLogLine(
activity_result_post_build_log_line,
) => {
// PostBuildLogLine // PostBuildLogLine
// TODO: Haven't seen any of these. // TODO: Haven't seen any of these.
warn!( // warn!(
"Found PostBuildLogLine: {}", // "Found PostBuildLogLine: {}",
serde_json::to_string(&message)? // serde_json::to_string(&message)?
); // );
} }
108 => { ActivityResultMessage::FetchStatus(activity_result_fetch_status) => {
// FetchStatus // FetchStatus
// TODO: Haven't seen any of these. // 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)?); // 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) -> () { fn print_current_status(&self) -> () {
// let in_progress = self let in_progress = self
// .activity_tree .activity_tree
// .iter() .iter()
// .filter(|activity: &(&u64, &ActivityTreeEntry)| activity.1.is_active()); .filter(|activity: &(&u64, &Activity)| activity.1.is_active());
// let names: Vec<&str> = in_progress let names: Vec<Cow<'_, str>> = in_progress
// .map(|activity| match activity.1 { .filter_map(|(id, activity)| activity.display_name())
// ActivityTreeEntry::Unknown { .collect();
// id, let name_list = names.join(", ");
// parent, // TODO: Make a meaningful current status.
// 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}"); // println!("In progress: {name_list}");
} }
} }
@@ -460,25 +335,6 @@ enum Activity {
} }
impl 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) -> () { fn stop(&mut self) -> () {
match self { match self {
Activity::Unknown(activity_unknown) => { 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<Cow<'_, str>> {
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 { struct ActivityUnknown {
@@ -543,7 +513,6 @@ struct ActivityFileTransfer {
id: u64, id: u64,
parent: u64, parent: u64,
state: ActivityState, state: ActivityState,
level: u8,
text: String, text: String,
url: String, url: String,
} }
@@ -551,8 +520,6 @@ struct ActivityRealize {
id: u64, id: u64,
parent: u64, parent: u64,
state: ActivityState, state: ActivityState,
level: u8,
text: String,
} }
struct ActivityCopyPaths { struct ActivityCopyPaths {
id: u64, id: u64,
@@ -565,16 +532,13 @@ struct ActivityBuilds {
id: u64, id: u64,
parent: u64, parent: u64,
state: ActivityState, state: ActivityState,
level: u8,
text: String,
} }
struct ActivityBuild { struct ActivityBuild {
id: u64, id: u64,
parent: u64, parent: u64,
state: ActivityState, state: ActivityState,
level: u8,
text: String, text: String,
path: String, drv_path: String,
} }
struct ActivityOptimizeStore { struct ActivityOptimizeStore {
id: u64, id: u64,
@@ -603,7 +567,6 @@ struct ActivityPostBuildHook {
} }
struct ActivityBuildWaiting { struct ActivityBuildWaiting {
id: u64, id: u64,
parent: u64,
state: ActivityState, state: ActivityState,
} }
struct ActivityFetchTree { struct ActivityFetchTree {
@@ -611,51 +574,3 @@ struct ActivityFetchTree {
parent: u64, parent: u64,
state: ActivityState, 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<Vec<Field>>, 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."),
}
}