Compare commits
5 Commits
d0fc2331e7
...
7b19e43fce
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7b19e43fce | ||
|
|
4fdff95039 | ||
|
|
2b7349a7ae | ||
|
|
87fbaf4aeb | ||
|
|
8d2dd015c4 |
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,2 +1,3 @@
|
||||
/target
|
||||
/work
|
||||
/example_logs
|
||||
|
||||
@ -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
|
||||
|
||||
106
draw_tree.py
Normal file
106
draw_tree.py
Normal file
@ -0,0 +1,106 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
|
||||
|
||||
def main():
|
||||
activity_tree = ActivityTree()
|
||||
activity_tree.activities[1] = Activity(id=1, parent=0, text="foo")
|
||||
activity_tree.activities[2] = Activity(id=2, parent=0, text="bar")
|
||||
activity_tree.activities[3] = Activity(id=3, parent=0, text="baz")
|
||||
activity_tree.activities[4] = Activity(id=4, parent=2, text="lorem")
|
||||
# activity_tree.activities[5] = Activity(id=5, parent=2, text="ipsum")
|
||||
# activity_tree.activities[6] = Activity(id=6, parent=2, text="dolar")
|
||||
activity_tree.activities[7] = Activity(id=7, parent=4, text="north")
|
||||
activity_tree.activities[8] = Activity(id=8, parent=4, text="east")
|
||||
activity_tree.activities[9] = Activity(id=9, parent=4, text="south")
|
||||
activity_tree.activities[10] = Activity(id=10, parent=4, text="west")
|
||||
|
||||
# For each line we need to know:
|
||||
# - the depth
|
||||
# - whether there are later siblings
|
||||
# - whether there are earlier siblings
|
||||
#
|
||||
# Draw order:
|
||||
# - Find all leaves
|
||||
# - Start with last child of root
|
||||
# - If it has children, keep going down until you hit leaves
|
||||
|
||||
for entry in activity_tree.get_draw_order():
|
||||
text = activity_tree.activities[entry.id].text
|
||||
leading_bars = "".join(" " if depth else " " for depth in entry.depth)
|
||||
branch = "" if entry.has_later_siblings else ""
|
||||
print(f"{leading_bars}{branch} {text}")
|
||||
|
||||
|
||||
class ActivityTree:
|
||||
activities: dict[int, Activity]
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.activities = {}
|
||||
|
||||
def get_draw_order(self) -> list[StackEntry]:
|
||||
draw_order = []
|
||||
stack = [
|
||||
StackEntry(
|
||||
id=child_id,
|
||||
depth=[],
|
||||
has_later_siblings=True,
|
||||
have_pulled_children=False,
|
||||
)
|
||||
for child_id in self.get_children_in_order(0)
|
||||
]
|
||||
stack[-1].has_later_siblings = False
|
||||
while len(stack) > 0:
|
||||
current_entry = stack.pop()
|
||||
if current_entry.have_pulled_children:
|
||||
if current_entry.id != 0:
|
||||
draw_order.append(current_entry)
|
||||
continue
|
||||
|
||||
current_entry.have_pulled_children = True
|
||||
stack.append(current_entry)
|
||||
all_children = self.get_children_in_order(current_entry.id)
|
||||
for child_id in all_children:
|
||||
stack.append(
|
||||
StackEntry(
|
||||
id=child_id,
|
||||
depth=current_entry.depth + [current_entry.has_later_siblings],
|
||||
has_later_siblings=True,
|
||||
have_pulled_children=False,
|
||||
)
|
||||
)
|
||||
if len(all_children) > 0:
|
||||
stack[-1].has_later_siblings = False
|
||||
return draw_order
|
||||
|
||||
def get_children_in_order(self, parent_id: int) -> list[int]:
|
||||
return sorted(
|
||||
child_id
|
||||
for child_id, child in self.activities.items()
|
||||
if child.parent == parent_id
|
||||
)
|
||||
|
||||
def has_children(self, parent_id: int) -> bool:
|
||||
return any(
|
||||
child.parent == parent_id for _child_id, child in self.activities.items()
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class StackEntry:
|
||||
id: int
|
||||
depth: list[bool]
|
||||
has_later_siblings: bool
|
||||
have_pulled_children: bool # Only used when building the draw order
|
||||
|
||||
|
||||
@dataclass
|
||||
class Activity:
|
||||
id: int
|
||||
parent: int
|
||||
text: str
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@ -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"
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -1,6 +1,11 @@
|
||||
use std::fmt::Display;
|
||||
use std::num::TryFromIntError;
|
||||
use std::str::Utf8Error;
|
||||
use std::string::FromUtf8Error;
|
||||
|
||||
use crate::nix_util::ActivityIdAlreadyInTreeError;
|
||||
use crate::nix_util::ActivityIdNotInTreeError;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum CustomError {
|
||||
Static(#[allow(dead_code)] &'static str),
|
||||
@ -16,6 +21,15 @@ 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),
|
||||
ActivityIdNotInTreeError(#[allow(dead_code)] ActivityIdNotInTreeError),
|
||||
ActivityIdAlreadyInTreeError(#[allow(dead_code)] ActivityIdAlreadyInTreeError),
|
||||
}
|
||||
|
||||
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 {
|
||||
@ -95,3 +109,21 @@ impl From<sqlx::Error> for CustomError {
|
||||
CustomError::Sql(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TryFromIntError> for CustomError {
|
||||
fn from(value: TryFromIntError) -> Self {
|
||||
CustomError::TryFromIntError(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ActivityIdNotInTreeError> for CustomError {
|
||||
fn from(value: ActivityIdNotInTreeError) -> Self {
|
||||
CustomError::ActivityIdNotInTreeError(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ActivityIdAlreadyInTreeError> for CustomError {
|
||||
fn from(value: ActivityIdAlreadyInTreeError) -> Self {
|
||||
CustomError::ActivityIdAlreadyInTreeError(value)
|
||||
}
|
||||
}
|
||||
|
||||
471
src/nix_util/activity.rs
Normal file
471
src/nix_util/activity.rs
Normal file
@ -0,0 +1,471 @@
|
||||
use std::borrow::Cow;
|
||||
|
||||
use tracing::warn;
|
||||
|
||||
#[repr(u8)]
|
||||
pub(crate) enum Activity {
|
||||
Root(ActivityRoot),
|
||||
Unknown(ActivityUnknown),
|
||||
CopyPath(ActivityCopyPath),
|
||||
FileTransfer(ActivityFileTransfer),
|
||||
Realize(ActivityRealize),
|
||||
CopyPaths(ActivityCopyPaths),
|
||||
Builds(ActivityBuilds),
|
||||
Build(ActivityBuild),
|
||||
OptimizeStore(ActivityOptimizeStore),
|
||||
VerifyPaths(ActivityVerifyPaths),
|
||||
Substitute(ActivitySubstitute),
|
||||
QueryPathInfo(ActivityQueryPathInfo),
|
||||
PostBuildHook(ActivityPostBuildHook),
|
||||
BuildWaiting(ActivityBuildWaiting),
|
||||
FetchTree(ActivityFetchTree),
|
||||
}
|
||||
|
||||
impl Activity {
|
||||
pub(crate) fn stop(&mut self) -> () {
|
||||
match self {
|
||||
Activity::Root(_activity_root) => {
|
||||
panic!("Attempted to start root activity.");
|
||||
}
|
||||
Activity::Unknown(activity_unknown) => {
|
||||
activity_unknown.state = ActivityState::Stopped;
|
||||
}
|
||||
Activity::CopyPath(activity_copy_path) => {
|
||||
activity_copy_path.state = ActivityState::Stopped;
|
||||
}
|
||||
Activity::FileTransfer(activity_file_transfer) => {
|
||||
activity_file_transfer.state = ActivityState::Stopped;
|
||||
}
|
||||
Activity::Realize(activity_realize) => {
|
||||
activity_realize.state = ActivityState::Stopped;
|
||||
}
|
||||
Activity::CopyPaths(activity_copy_paths) => {
|
||||
activity_copy_paths.state = ActivityState::Stopped;
|
||||
}
|
||||
Activity::Builds(activity_builds) => {
|
||||
activity_builds.state = ActivityState::Stopped;
|
||||
}
|
||||
Activity::Build(activity_build) => {
|
||||
activity_build.state = ActivityState::Stopped;
|
||||
}
|
||||
Activity::OptimizeStore(activity_optimize_store) => {
|
||||
activity_optimize_store.state = ActivityState::Stopped;
|
||||
}
|
||||
Activity::VerifyPaths(activity_verify_paths) => {
|
||||
activity_verify_paths.state = ActivityState::Stopped;
|
||||
}
|
||||
Activity::Substitute(activity_substitute) => {
|
||||
activity_substitute.state = ActivityState::Stopped;
|
||||
}
|
||||
Activity::QueryPathInfo(activity_query_path_info) => {
|
||||
activity_query_path_info.state = ActivityState::Stopped;
|
||||
}
|
||||
Activity::PostBuildHook(activity_post_build_hook) => {
|
||||
activity_post_build_hook.state = ActivityState::Stopped;
|
||||
}
|
||||
Activity::BuildWaiting(activity_build_waiting) => {
|
||||
activity_build_waiting.state = ActivityState::Stopped;
|
||||
}
|
||||
Activity::FetchTree(activity_fetch_tree) => {
|
||||
activity_fetch_tree.state = ActivityState::Stopped;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn is_active(&self) -> bool {
|
||||
match self {
|
||||
Activity::Root(_activity_root) => true,
|
||||
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 { .. })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn is_transparent(&self) -> bool {
|
||||
match self {
|
||||
Activity::Root(_activity_root) => true,
|
||||
Activity::Unknown(_activity_unknown) => false,
|
||||
Activity::CopyPath(_activity_copy_path) => false,
|
||||
Activity::FileTransfer(_activity_file_transfer) => false,
|
||||
Activity::Realize(_activity_realize) => true,
|
||||
Activity::CopyPaths(_activity_copy_paths) => true,
|
||||
Activity::Builds(_activity_builds) => true,
|
||||
Activity::Build(_activity_build) => false,
|
||||
Activity::OptimizeStore(_activity_optimize_store) => false,
|
||||
Activity::VerifyPaths(_activity_verify_paths) => false,
|
||||
Activity::Substitute(_activity_substitute) => false,
|
||||
Activity::QueryPathInfo(_activity_query_path_info) => false,
|
||||
Activity::PostBuildHook(_activity_post_build_hook) => false,
|
||||
Activity::BuildWaiting(_activity_build_waiting) => true,
|
||||
Activity::FetchTree(_activity_fetch_tree) => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn display_name(&self) -> Option<Cow<'_, str>> {
|
||||
match self {
|
||||
Activity::Root(_activity_root) => {
|
||||
warn!("Unexpected display of root activity.");
|
||||
Some(Cow::Borrowed("Root"))
|
||||
}
|
||||
Activity::Unknown(activity_unknown) => {
|
||||
Some(Cow::Owned(format!("Unknown({})", activity_unknown.text)))
|
||||
}
|
||||
Activity::CopyPath(activity_copy_path) => Some(Cow::Owned(format!(
|
||||
"CopyPath({} | {} -> {})",
|
||||
activity_copy_path.missing_path,
|
||||
activity_copy_path.source,
|
||||
activity_copy_path.destination
|
||||
))),
|
||||
Activity::FileTransfer(activity_file_transfer) => Some(Cow::Owned(format!(
|
||||
"FileTransfer({})",
|
||||
activity_file_transfer.url
|
||||
))),
|
||||
Activity::Realize(_activity_realize) => Some(Cow::Borrowed("Realize")),
|
||||
Activity::CopyPaths(activity_copy_paths) => Some(Cow::Owned(format!(
|
||||
"CopyPaths({})",
|
||||
activity_copy_paths.text
|
||||
))),
|
||||
Activity::Builds(_activity_builds) => Some(Cow::Borrowed("Builds")),
|
||||
Activity::Build(activity_build) => {
|
||||
if let Some(machine_name) = &activity_build.machine_name {
|
||||
Some(Cow::Owned(format!(
|
||||
"Build({}@{machine_name})",
|
||||
activity_build.drv_path
|
||||
)))
|
||||
} else {
|
||||
Some(Cow::Owned(format!("Build({})", activity_build.drv_path)))
|
||||
}
|
||||
}
|
||||
Activity::OptimizeStore(_activity_optimize_store) => {
|
||||
Some(Cow::Borrowed("OptimizeStore"))
|
||||
}
|
||||
Activity::VerifyPaths(_activity_verify_paths) => Some(Cow::Borrowed("VerifyPaths")),
|
||||
Activity::Substitute(_activity_substitute) => Some(Cow::Borrowed("Substitute")),
|
||||
Activity::QueryPathInfo(_activity_query_path_info) => {
|
||||
Some(Cow::Borrowed("QueryPathInfo"))
|
||||
}
|
||||
Activity::PostBuildHook(_activity_post_build_hook) => {
|
||||
Some(Cow::Borrowed("PostBuildHook"))
|
||||
}
|
||||
Activity::BuildWaiting(activity_build_waiting) => Some(Cow::Owned(format!(
|
||||
"BuildWaiting({:?} | {:?} | {})",
|
||||
activity_build_waiting.drv_path,
|
||||
activity_build_waiting.path_resolved,
|
||||
activity_build_waiting.text
|
||||
))),
|
||||
Activity::FetchTree(_activity_fetch_tree) => Some(Cow::Borrowed("FetchTree")),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn set_phase(&mut self, phase: Option<String>) -> () {
|
||||
match self {
|
||||
Activity::Root(_activity_root) => {
|
||||
panic!("Attempted to set the phase of a root activity.");
|
||||
}
|
||||
Activity::Unknown(_activity_unknown) => {
|
||||
panic!("Attempted to set the phase of an unknown activity.");
|
||||
}
|
||||
Activity::CopyPath(_activity_copy_path) => {
|
||||
panic!("Attempted to set the phase of a copy path activity.");
|
||||
}
|
||||
Activity::FileTransfer(_activity_file_transfer) => {
|
||||
panic!("Attempted to set the phase of a file transfer activity.");
|
||||
}
|
||||
Activity::Realize(_activity_realize) => {
|
||||
panic!("Attempted to set the phase of a realize activity.");
|
||||
}
|
||||
Activity::CopyPaths(_activity_copy_paths) => {
|
||||
panic!("Attempted to set the phase of a copy paths activity.");
|
||||
}
|
||||
Activity::Builds(_activity_builds) => {
|
||||
panic!("Attempted to set the phase of a builds activity.");
|
||||
}
|
||||
Activity::Build(activity_build) => {
|
||||
activity_build.phase = phase;
|
||||
}
|
||||
Activity::OptimizeStore(_activity_optimize_store) => {
|
||||
panic!("Attempted to set the phase of an optimize store activity.");
|
||||
}
|
||||
Activity::VerifyPaths(_activity_verify_paths) => {
|
||||
panic!("Attempted to set the phase of a verify paths activity.");
|
||||
}
|
||||
Activity::Substitute(_activity_substitute) => {
|
||||
panic!("Attempted to set the phase of a substitute activity.");
|
||||
}
|
||||
Activity::QueryPathInfo(_activity_query_path_info) => {
|
||||
panic!("Attempted to set the phase of a query path info activity.");
|
||||
}
|
||||
Activity::PostBuildHook(_activity_post_build_hook) => {
|
||||
panic!("Attempted to set the phase of a post build hook activity.");
|
||||
}
|
||||
Activity::BuildWaiting(_activity_build_waiting) => {
|
||||
panic!("Attempted to set the phase of a build waiting activity.");
|
||||
}
|
||||
Activity::FetchTree(_activity_fetch_tree) => {
|
||||
panic!("Attempted to set the phase of a fetch tree activity.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn set_progress(
|
||||
&mut self,
|
||||
done: u64,
|
||||
expected: u64,
|
||||
running: u64,
|
||||
failed: u64,
|
||||
) -> () {
|
||||
match self {
|
||||
Activity::Root(_activity_root) => {
|
||||
panic!("Attempted to set the progress of a root activity.");
|
||||
}
|
||||
Activity::Unknown(_activity_unknown) => {
|
||||
panic!("Attempted to set the progress of an unknown activity.");
|
||||
}
|
||||
Activity::CopyPath(activity_copy_path) => {
|
||||
if running != 0 || failed != 0 {
|
||||
panic!("Attempted to set the progress of a copy path activity.");
|
||||
}
|
||||
activity_copy_path.done = done;
|
||||
activity_copy_path.expected = expected;
|
||||
}
|
||||
Activity::FileTransfer(activity_file_transfer) => {
|
||||
if running != 0 || failed != 0 {
|
||||
panic!("Attempted to set the progress of a file transfer activity.");
|
||||
}
|
||||
activity_file_transfer.done = done;
|
||||
activity_file_transfer.expected = expected;
|
||||
}
|
||||
Activity::Realize(_activity_realize) => {
|
||||
panic!("Attempted to set the progress of a realize activity.");
|
||||
}
|
||||
Activity::CopyPaths(activity_copy_paths) => {
|
||||
activity_copy_paths.done = done;
|
||||
activity_copy_paths.expected = expected;
|
||||
activity_copy_paths.running = running;
|
||||
activity_copy_paths.failed = failed;
|
||||
}
|
||||
Activity::Builds(activity_builds) => {
|
||||
activity_builds.done = done;
|
||||
activity_builds.expected = expected;
|
||||
activity_builds.running = running;
|
||||
activity_builds.failed = failed;
|
||||
}
|
||||
Activity::Build(_activity_build) => {
|
||||
panic!("Attempted to set the progress of a build activity.");
|
||||
}
|
||||
Activity::OptimizeStore(_activity_optimize_store) => {
|
||||
panic!("Attempted to set the progress of an optimize store activity.");
|
||||
}
|
||||
Activity::VerifyPaths(_activity_verify_paths) => {
|
||||
panic!("Attempted to set the progress of a verify paths activity.");
|
||||
}
|
||||
Activity::Substitute(_activity_substitute) => {
|
||||
panic!("Attempted to set the progress of a substitute activity.");
|
||||
}
|
||||
Activity::QueryPathInfo(_activity_query_path_info) => {
|
||||
panic!("Attempted to set the progress of a query path info activity.");
|
||||
}
|
||||
Activity::PostBuildHook(_activity_post_build_hook) => {
|
||||
panic!("Attempted to set the progress of a post build hook activity.");
|
||||
}
|
||||
Activity::BuildWaiting(_activity_build_waiting) => {
|
||||
panic!("Attempted to set the progress of a build waiting activity.");
|
||||
}
|
||||
Activity::FetchTree(_activity_fetch_tree) => {
|
||||
panic!("Attempted to set the progress of a fetch tree activity.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn set_expected(&mut self, expected: u64) -> () {
|
||||
match self {
|
||||
Activity::Root(_activity_root) => {
|
||||
panic!("Attempted to set the expected of a root activity.");
|
||||
}
|
||||
Activity::Unknown(_activity_unknown) => {
|
||||
panic!("Attempted to set the expected of an unknown activity.");
|
||||
}
|
||||
Activity::CopyPath(_activity_copy_path) => {
|
||||
panic!("Attempted to set the expected of a copy path activity.");
|
||||
}
|
||||
Activity::FileTransfer(_activity_file_transfer) => {
|
||||
panic!("Attempted to set the expected of a file transfer activity.");
|
||||
}
|
||||
Activity::Realize(_activity_realize) => {
|
||||
// Seems to always be zero?
|
||||
if expected != 0 {
|
||||
panic!("Attempted to set the expected of a realize activity.");
|
||||
}
|
||||
}
|
||||
Activity::CopyPaths(activity_copy_paths) => {
|
||||
activity_copy_paths.expected = expected;
|
||||
}
|
||||
Activity::Builds(_activity_builds) => {
|
||||
panic!("Attempted to set the expected of a builds activity.");
|
||||
}
|
||||
Activity::Build(_activity_build) => {
|
||||
panic!("Attempted to set the expected of a build activity.");
|
||||
}
|
||||
Activity::OptimizeStore(_activity_optimize_store) => {
|
||||
panic!("Attempted to set the expected of an optimize store activity.");
|
||||
}
|
||||
Activity::VerifyPaths(_activity_verify_paths) => {
|
||||
panic!("Attempted to set the expected of a verify paths activity.");
|
||||
}
|
||||
Activity::Substitute(_activity_substitute) => {
|
||||
panic!("Attempted to set the expected of a substitute activity.");
|
||||
}
|
||||
Activity::QueryPathInfo(_activity_query_path_info) => {
|
||||
panic!("Attempted to set the expected of a query path info activity.");
|
||||
}
|
||||
Activity::PostBuildHook(_activity_post_build_hook) => {
|
||||
panic!("Attempted to set the expected of a post build hook activity.");
|
||||
}
|
||||
Activity::BuildWaiting(_activity_build_waiting) => {
|
||||
panic!("Attempted to set the expected of a build waiting activity.");
|
||||
}
|
||||
Activity::FetchTree(_activity_fetch_tree) => {
|
||||
panic!("Attempted to set the expected of a fetch tree activity.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct ActivityRoot {}
|
||||
pub(crate) struct ActivityUnknown {
|
||||
pub(crate) state: ActivityState,
|
||||
pub(crate) text: String,
|
||||
}
|
||||
pub(crate) struct ActivityCopyPath {
|
||||
pub(crate) state: ActivityState,
|
||||
pub(crate) missing_path: String,
|
||||
|
||||
/// The machine with the file(s)
|
||||
pub(crate) source: String,
|
||||
|
||||
/// The machine that is receiving the file(s)
|
||||
pub(crate) destination: String,
|
||||
|
||||
pub(crate) done: u64,
|
||||
pub(crate) expected: u64,
|
||||
}
|
||||
pub(crate) struct ActivityFileTransfer {
|
||||
pub(crate) state: ActivityState,
|
||||
pub(crate) url: String,
|
||||
pub(crate) done: u64,
|
||||
pub(crate) expected: u64,
|
||||
}
|
||||
pub(crate) struct ActivityRealize {
|
||||
pub(crate) state: ActivityState,
|
||||
}
|
||||
pub(crate) struct ActivityCopyPaths {
|
||||
pub(crate) state: ActivityState,
|
||||
pub(crate) text: String,
|
||||
pub(crate) done: u64,
|
||||
pub(crate) expected: u64,
|
||||
pub(crate) running: u64,
|
||||
pub(crate) failed: u64,
|
||||
}
|
||||
pub(crate) struct ActivityBuilds {
|
||||
pub(crate) state: ActivityState,
|
||||
pub(crate) done: u64,
|
||||
pub(crate) expected: u64,
|
||||
pub(crate) running: u64,
|
||||
pub(crate) failed: u64,
|
||||
}
|
||||
pub(crate) struct ActivityBuild {
|
||||
pub(crate) state: ActivityState,
|
||||
pub(crate) drv_path: String,
|
||||
pub(crate) machine_name: Option<String>,
|
||||
pub(crate) phase: Option<String>,
|
||||
}
|
||||
pub(crate) struct ActivityOptimizeStore {
|
||||
pub(crate) state: ActivityState,
|
||||
}
|
||||
pub(crate) struct ActivityVerifyPaths {
|
||||
pub(crate) state: ActivityState,
|
||||
}
|
||||
pub(crate) struct ActivitySubstitute {
|
||||
pub(crate) state: ActivityState,
|
||||
}
|
||||
pub(crate) struct ActivityQueryPathInfo {
|
||||
pub(crate) state: ActivityState,
|
||||
}
|
||||
pub(crate) struct ActivityPostBuildHook {
|
||||
pub(crate) state: ActivityState,
|
||||
}
|
||||
pub(crate) struct ActivityBuildWaiting {
|
||||
pub(crate) state: ActivityState,
|
||||
pub(crate) text: String,
|
||||
pub(crate) drv_path: Option<String>,
|
||||
pub(crate) path_resolved: Option<String>,
|
||||
}
|
||||
pub(crate) struct ActivityFetchTree {
|
||||
pub(crate) state: ActivityState,
|
||||
}
|
||||
|
||||
pub(crate) enum ActivityState {
|
||||
Started {
|
||||
progress_numerator: u64,
|
||||
progress_denominator: u64,
|
||||
progress_running: u64,
|
||||
progress_failed: u64,
|
||||
},
|
||||
Stopped,
|
||||
}
|
||||
|
||||
impl Default for ActivityState {
|
||||
fn default() -> Self {
|
||||
ActivityState::Started {
|
||||
progress_numerator: 0,
|
||||
progress_denominator: 0,
|
||||
progress_running: 0,
|
||||
progress_failed: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
180
src/nix_util/activity_tree.rs
Normal file
180
src/nix_util/activity_tree.rs
Normal file
@ -0,0 +1,180 @@
|
||||
use crate::Result;
|
||||
use std::collections::BTreeMap;
|
||||
use std::fmt::Display;
|
||||
use std::ops::Deref;
|
||||
|
||||
use super::activity::Activity;
|
||||
use super::activity::ActivityRoot;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct ActivityId {
|
||||
id: u64,
|
||||
}
|
||||
|
||||
impl ActivityId {
|
||||
fn new(id: u64) -> ActivityId {
|
||||
ActivityId { id }
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for ActivityId {
|
||||
type Target = u64;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.id
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for ActivityId {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
Display::fmt(&**self, f)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct ActivityTree {
|
||||
activities: BTreeMap<u64, ActivityTreeEntry>,
|
||||
}
|
||||
|
||||
impl ActivityTree {
|
||||
pub(crate) fn new() -> ActivityTree {
|
||||
let mut activities = BTreeMap::new();
|
||||
activities.insert(
|
||||
0,
|
||||
ActivityTreeEntry {
|
||||
id: ActivityId::new(0),
|
||||
parent_id: ActivityId::new(0),
|
||||
child_ids: Vec::new(),
|
||||
activity: Activity::Root(ActivityRoot {}),
|
||||
},
|
||||
);
|
||||
ActivityTree { activities }
|
||||
}
|
||||
|
||||
pub(crate) fn add_activity(
|
||||
&mut self,
|
||||
activity_id: u64,
|
||||
parent_id: u64,
|
||||
activity: Activity,
|
||||
) -> Result<&ActivityTreeEntry> {
|
||||
// The activity_id is not yet in the tree, so we can't use get_activity_id.
|
||||
let activity_id = ActivityId::new(activity_id);
|
||||
let parent_id = self.get_activity_id(parent_id)?;
|
||||
match self.activities.entry(activity_id.id) {
|
||||
std::collections::btree_map::Entry::Vacant(vacant_entry) => {
|
||||
vacant_entry.insert(ActivityTreeEntry {
|
||||
id: activity_id.clone(),
|
||||
parent_id: parent_id.clone(),
|
||||
child_ids: Vec::new(),
|
||||
activity,
|
||||
});
|
||||
}
|
||||
std::collections::btree_map::Entry::Occupied(_occupied_entry) => {
|
||||
return Err(ActivityIdAlreadyInTreeError::new(*activity_id).into());
|
||||
}
|
||||
};
|
||||
self.get_mut(&parent_id).child_ids.push(activity_id.clone());
|
||||
let tree_entry = self
|
||||
.activities
|
||||
.get(&*activity_id)
|
||||
.expect("We just created this entry so it must exist.");
|
||||
|
||||
Ok(tree_entry)
|
||||
}
|
||||
|
||||
pub(crate) fn get_activity_id(&self, activity_id: u64) -> Result<ActivityId> {
|
||||
if activity_id == 0 || self.activities.contains_key(&activity_id) {
|
||||
Ok(ActivityId::new(activity_id))
|
||||
} else {
|
||||
Err(ActivityIdNotInTreeError::new(activity_id).into())
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn get_root_id(&self) -> ActivityId {
|
||||
// The root always exists, so there is no need to check.
|
||||
ActivityId::new(0)
|
||||
}
|
||||
|
||||
pub(crate) fn get(&self, activity_id: &ActivityId) -> &ActivityTreeEntry {
|
||||
self.activities
|
||||
.get(activity_id)
|
||||
.expect("You cannot create an ActivityId if the activity is not in the tree.")
|
||||
}
|
||||
|
||||
pub(crate) fn get_mut(&mut self, activity_id: &ActivityId) -> &mut ActivityTreeEntry {
|
||||
self.activities
|
||||
.get_mut(activity_id)
|
||||
.expect("You cannot create an ActivityId if the activity is not in the tree.")
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct ActivityTreeEntry {
|
||||
id: ActivityId,
|
||||
parent_id: ActivityId,
|
||||
child_ids: Vec<ActivityId>,
|
||||
activity: Activity,
|
||||
}
|
||||
|
||||
impl ActivityTreeEntry {
|
||||
pub(crate) fn get_activity_id(&self) -> &ActivityId {
|
||||
&self.id
|
||||
}
|
||||
|
||||
pub(crate) fn get_parent_id(&self) -> &ActivityId {
|
||||
&self.parent_id
|
||||
}
|
||||
|
||||
pub(crate) fn get_activity(&self) -> &Activity {
|
||||
&self.activity
|
||||
}
|
||||
|
||||
pub(crate) fn get_mut_activity(&mut self) -> &mut Activity {
|
||||
&mut self.activity
|
||||
}
|
||||
|
||||
pub(crate) fn get_child_ids(&self) -> &Vec<ActivityId> {
|
||||
&self.child_ids
|
||||
}
|
||||
|
||||
pub(crate) fn iter_children<'tree>(
|
||||
&'_ self,
|
||||
activity_tree: &'tree ActivityTree,
|
||||
) -> impl Iterator<Item = &'tree ActivityTreeEntry> {
|
||||
self.child_ids
|
||||
.iter()
|
||||
.map(|child_id| activity_tree.get(child_id))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct ActivityIdNotInTreeError {
|
||||
id: u64,
|
||||
}
|
||||
|
||||
impl ActivityIdNotInTreeError {
|
||||
fn new(id: u64) -> ActivityIdNotInTreeError {
|
||||
ActivityIdNotInTreeError { id }
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for ActivityIdNotInTreeError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
write!(f, "Activity id {} not in the tree.", self.id)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct ActivityIdAlreadyInTreeError {
|
||||
id: u64,
|
||||
}
|
||||
|
||||
impl ActivityIdAlreadyInTreeError {
|
||||
fn new(id: u64) -> ActivityIdAlreadyInTreeError {
|
||||
ActivityIdAlreadyInTreeError { id }
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for ActivityIdAlreadyInTreeError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
write!(f, "Activity id {} already in the tree.", self.id)
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -1,7 +1,12 @@
|
||||
mod activity;
|
||||
mod activity_tree;
|
||||
mod high_level;
|
||||
mod nix_output_stream;
|
||||
mod output_stream;
|
||||
mod running_build;
|
||||
mod transparent_iter;
|
||||
pub(crate) use activity_tree::ActivityIdAlreadyInTreeError;
|
||||
pub(crate) use activity_tree::ActivityIdNotInTreeError;
|
||||
pub(crate) use high_level::*;
|
||||
pub(crate) use nix_output_stream::NixOutputStream;
|
||||
pub(crate) use output_stream::OutputLine;
|
||||
|
||||
@ -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<S> {
|
||||
inner: S,
|
||||
@ -30,7 +103,8 @@ impl<S: OutputLineStream> NixOutputStream<S> {
|
||||
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,295 @@ 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);
|
||||
|
||||
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();
|
||||
let failed = number_field(&fields, 3).to_owned();
|
||||
|
||||
Ok(NixAction::Result(ActivityResultMessage::Progress(
|
||||
ActivityResultProgress {
|
||||
id,
|
||||
done,
|
||||
expected,
|
||||
running,
|
||||
failed,
|
||||
},
|
||||
)))
|
||||
}
|
||||
ResultType::SetExpected => {
|
||||
warn_if_len!(ActivityResultSetExpected, original_json, &fields, != 2);
|
||||
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 +409,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<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)]
|
||||
#[serde(tag = "action", rename_all = "lowercase", deny_unknown_fields)]
|
||||
pub(crate) enum NixAction {
|
||||
pub(crate) enum RawNixAction {
|
||||
Msg {
|
||||
level: u8,
|
||||
msg: String,
|
||||
@ -64,6 +525,9 @@ pub(crate) enum NixAction {
|
||||
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
column: Option<u64>,
|
||||
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
trace: Option<Vec<TraceEntry>>,
|
||||
},
|
||||
Start {
|
||||
id: u64,
|
||||
@ -74,7 +538,7 @@ pub(crate) enum NixAction {
|
||||
level: u8,
|
||||
parent: u64,
|
||||
text: String,
|
||||
r#type: u8,
|
||||
r#type: ActivityType,
|
||||
},
|
||||
Stop {
|
||||
id: u64,
|
||||
@ -82,7 +546,7 @@ pub(crate) enum NixAction {
|
||||
Result {
|
||||
id: u64,
|
||||
fields: Vec<Field>,
|
||||
r#type: u8,
|
||||
r#type: ResultType,
|
||||
},
|
||||
}
|
||||
|
||||
@ -92,3 +556,333 @@ 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<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,
|
||||
|
||||
/// Seems to either be blank or "copying 9 paths"
|
||||
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: u64,
|
||||
pub(crate) failed: u64,
|
||||
}
|
||||
|
||||
#[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."),
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,31 +1,47 @@
|
||||
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 crate::nix_util::transparent_iter::TransparentIter;
|
||||
|
||||
use super::nix_output_stream::Field;
|
||||
use super::activity::Activity;
|
||||
use super::activity::ActivityBuild;
|
||||
use super::activity::ActivityBuildWaiting;
|
||||
use super::activity::ActivityBuilds;
|
||||
use super::activity::ActivityCopyPath;
|
||||
use super::activity::ActivityCopyPaths;
|
||||
use super::activity::ActivityFetchTree;
|
||||
use super::activity::ActivityFileTransfer;
|
||||
use super::activity::ActivityOptimizeStore;
|
||||
use super::activity::ActivityPostBuildHook;
|
||||
use super::activity::ActivityQueryPathInfo;
|
||||
use super::activity::ActivityRealize;
|
||||
use super::activity::ActivityState;
|
||||
use super::activity::ActivitySubstitute;
|
||||
use super::activity::ActivityUnknown;
|
||||
use super::activity::ActivityVerifyPaths;
|
||||
use super::activity_tree::ActivityId;
|
||||
use super::activity_tree::ActivityTree;
|
||||
use super::activity_tree::ActivityTreeEntry;
|
||||
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<u64, Activity>,
|
||||
activity_tree: ActivityTree,
|
||||
}
|
||||
|
||||
impl<'db> RunningBuild<'db> {
|
||||
pub(crate) fn new(db_handle: &'db DbHandle) -> Result<Self> {
|
||||
Ok(RunningBuild {
|
||||
db_handle,
|
||||
activity_tree: BTreeMap::new(),
|
||||
activity_tree: ActivityTree::new(),
|
||||
})
|
||||
}
|
||||
|
||||
@ -62,7 +78,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,288 +108,220 @@ 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);
|
||||
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}.")
|
||||
NixAction::Start(activity_start_message) => {
|
||||
match activity_start_message {
|
||||
ActivityStartMessage::Unknown(activity_start_unknown) => {
|
||||
self.activity_tree.add_activity(
|
||||
activity_start_unknown.id,
|
||||
activity_start_unknown.parent,
|
||||
Activity::Unknown(ActivityUnknown {
|
||||
state: ActivityState::default(),
|
||||
text: activity_start_unknown.text,
|
||||
}),
|
||||
)?;
|
||||
}
|
||||
ActivityStartMessage::CopyPath(activity_start_copy_path) => {
|
||||
self.activity_tree.add_activity(
|
||||
activity_start_copy_path.id,
|
||||
activity_start_copy_path.parent,
|
||||
Activity::CopyPath(ActivityCopyPath {
|
||||
state: ActivityState::default(),
|
||||
missing_path: activity_start_copy_path.missing_path,
|
||||
source: activity_start_copy_path.source,
|
||||
destination: activity_start_copy_path.destination,
|
||||
done: 0,
|
||||
expected: 0,
|
||||
}),
|
||||
)?;
|
||||
}
|
||||
ActivityStartMessage::FileTransfer(activity_start_file_transfer) => {
|
||||
self.activity_tree.add_activity(
|
||||
activity_start_file_transfer.id,
|
||||
activity_start_file_transfer.parent,
|
||||
Activity::FileTransfer(ActivityFileTransfer {
|
||||
state: ActivityState::default(),
|
||||
url: activity_start_file_transfer.url,
|
||||
done: 0,
|
||||
expected: 0,
|
||||
}),
|
||||
)?;
|
||||
}
|
||||
ActivityStartMessage::Realize(activity_start_realize) => {
|
||||
self.activity_tree.add_activity(
|
||||
activity_start_realize.id,
|
||||
activity_start_realize.parent,
|
||||
Activity::Realize(ActivityRealize {
|
||||
state: ActivityState::default(),
|
||||
}),
|
||||
)?;
|
||||
}
|
||||
ActivityStartMessage::CopyPaths(activity_start_copy_paths) => {
|
||||
self.activity_tree.add_activity(
|
||||
activity_start_copy_paths.id,
|
||||
activity_start_copy_paths.parent,
|
||||
Activity::CopyPaths(ActivityCopyPaths {
|
||||
state: ActivityState::default(),
|
||||
text: activity_start_copy_paths.text,
|
||||
done: 0,
|
||||
expected: 0,
|
||||
running: 0,
|
||||
failed: 0,
|
||||
}),
|
||||
)?;
|
||||
}
|
||||
ActivityStartMessage::Builds(activity_start_builds) => {
|
||||
self.activity_tree.add_activity(
|
||||
activity_start_builds.id,
|
||||
activity_start_builds.parent,
|
||||
Activity::Builds(ActivityBuilds {
|
||||
state: ActivityState::default(),
|
||||
done: 0,
|
||||
expected: 0,
|
||||
running: 0,
|
||||
failed: 0,
|
||||
}),
|
||||
)?;
|
||||
}
|
||||
ActivityStartMessage::Build(activity_start_build) => {
|
||||
self.activity_tree.add_activity(
|
||||
activity_start_build.id,
|
||||
activity_start_build.parent,
|
||||
Activity::Build(ActivityBuild {
|
||||
state: ActivityState::default(),
|
||||
drv_path: activity_start_build.drv_path,
|
||||
machine_name: if activity_start_build.machine_name.len() > 0 {
|
||||
Some(activity_start_build.machine_name)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
phase: None,
|
||||
}),
|
||||
)?;
|
||||
}
|
||||
ActivityStartMessage::OptimizeStore(activity_start_optimize_store) => {
|
||||
self.activity_tree.add_activity(
|
||||
activity_start_optimize_store.id,
|
||||
activity_start_optimize_store.parent,
|
||||
Activity::OptimizeStore(ActivityOptimizeStore {
|
||||
state: ActivityState::default(),
|
||||
}),
|
||||
)?;
|
||||
}
|
||||
ActivityStartMessage::VerifyPaths(activity_start_verify_paths) => {
|
||||
self.activity_tree.add_activity(
|
||||
activity_start_verify_paths.id,
|
||||
activity_start_verify_paths.parent,
|
||||
Activity::VerifyPaths(ActivityVerifyPaths {
|
||||
state: ActivityState::default(),
|
||||
}),
|
||||
)?;
|
||||
}
|
||||
ActivityStartMessage::Substitute(activity_start_substitute) => {
|
||||
self.activity_tree.add_activity(
|
||||
activity_start_substitute.id,
|
||||
activity_start_substitute.parent,
|
||||
Activity::Substitute(ActivitySubstitute {
|
||||
state: ActivityState::default(),
|
||||
}),
|
||||
)?;
|
||||
}
|
||||
ActivityStartMessage::QueryPathInfo(activity_start_query_path_info) => {
|
||||
self.activity_tree.add_activity(
|
||||
activity_start_query_path_info.id,
|
||||
activity_start_query_path_info.parent,
|
||||
Activity::QueryPathInfo(ActivityQueryPathInfo {
|
||||
state: ActivityState::default(),
|
||||
}),
|
||||
)?;
|
||||
}
|
||||
ActivityStartMessage::PostBuildHook(activity_start_post_build_hook) => {
|
||||
self.activity_tree.add_activity(
|
||||
activity_start_post_build_hook.id,
|
||||
activity_start_post_build_hook.parent,
|
||||
Activity::PostBuildHook(ActivityPostBuildHook {
|
||||
state: ActivityState::default(),
|
||||
}),
|
||||
)?;
|
||||
}
|
||||
ActivityStartMessage::BuildWaiting(activity_start_build_waiting) => {
|
||||
self.activity_tree.add_activity(
|
||||
activity_start_build_waiting.id,
|
||||
0,
|
||||
Activity::BuildWaiting(ActivityBuildWaiting {
|
||||
state: ActivityState::default(),
|
||||
text: activity_start_build_waiting.text,
|
||||
drv_path: activity_start_build_waiting.drv_path,
|
||||
path_resolved: activity_start_build_waiting.path_resolved,
|
||||
}),
|
||||
)?;
|
||||
}
|
||||
ActivityStartMessage::FetchTree(activity_start_fetch_tree) => {
|
||||
self.activity_tree.add_activity(
|
||||
activity_start_fetch_tree.id,
|
||||
activity_start_fetch_tree.parent,
|
||||
Activity::FetchTree(ActivityFetchTree {
|
||||
state: ActivityState::default(),
|
||||
}),
|
||||
)?;
|
||||
}
|
||||
};
|
||||
match r#type {
|
||||
0 => {
|
||||
entry.insert(Activity::Unknown(ActivityUnknown {
|
||||
id: *id,
|
||||
parent: *parent,
|
||||
state: ActivityState::default(),
|
||||
level: *level,
|
||||
text: text.to_owned(),
|
||||
}));
|
||||
}
|
||||
100 => {
|
||||
// TODO: Haven't seen any of these.
|
||||
warn!("Found CopyPath: {}", serde_json::to_string(&message)?);
|
||||
entry.insert(Activity::CopyPath(ActivityCopyPath {
|
||||
id: *id,
|
||||
parent: *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)?
|
||||
);
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
entry.insert(Activity::FileTransfer(ActivityFileTransfer {
|
||||
id: *id,
|
||||
parent: *parent,
|
||||
state: ActivityState::default(),
|
||||
level: *level,
|
||||
text: text.to_owned(),
|
||||
url: string_field(fields, 0).to_owned(),
|
||||
}));
|
||||
}
|
||||
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)?
|
||||
);
|
||||
}
|
||||
entry.insert(Activity::Realize(ActivityRealize {
|
||||
id: *id,
|
||||
parent: *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)?
|
||||
);
|
||||
}
|
||||
entry.insert(Activity::CopyPaths(ActivityCopyPaths {
|
||||
id: *id,
|
||||
parent: *parent,
|
||||
state: ActivityState::default(),
|
||||
level: *level,
|
||||
text: text.to_owned(),
|
||||
}));
|
||||
}
|
||||
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)?
|
||||
);
|
||||
}
|
||||
entry.insert(Activity::Builds(ActivityBuilds {
|
||||
id: *id,
|
||||
parent: *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]
|
||||
entry.insert(Activity::Build(ActivityBuild {
|
||||
id: *id,
|
||||
parent: *parent,
|
||||
state: ActivityState::default(),
|
||||
level: *level,
|
||||
text: text.to_owned(),
|
||||
path: string_field(fields, 0).to_owned(),
|
||||
}));
|
||||
}
|
||||
106 => {
|
||||
// TODO: Haven't seen any of these.
|
||||
warn!("Found OptimizeStore: {}", serde_json::to_string(&message)?);
|
||||
entry.insert(Activity::OptimizeStore(ActivityOptimizeStore {
|
||||
id: *id,
|
||||
parent: *parent,
|
||||
state: ActivityState::default(),
|
||||
}));
|
||||
}
|
||||
107 => {
|
||||
// TODO: Haven't seen any of these.
|
||||
warn!("Found VerifyPath: {}", serde_json::to_string(&message)?);
|
||||
entry.insert(Activity::VerifyPaths(ActivityVerifyPaths {
|
||||
id: *id,
|
||||
parent: *parent,
|
||||
state: ActivityState::default(),
|
||||
}));
|
||||
}
|
||||
108 => {
|
||||
// TODO: Haven't seen any of these.
|
||||
warn!("Found Subtitute: {}", serde_json::to_string(&message)?);
|
||||
entry.insert(Activity::Substitute(ActivitySubstitute {
|
||||
id: *id,
|
||||
parent: *parent,
|
||||
state: ActivityState::default(),
|
||||
}));
|
||||
}
|
||||
109 => {
|
||||
// TODO: Haven't seen any of these.
|
||||
warn!("Found QueryPathInfo: {}", serde_json::to_string(&message)?);
|
||||
entry.insert(Activity::QueryPathInfo(ActivityQueryPathInfo {
|
||||
id: *id,
|
||||
parent: *parent,
|
||||
state: ActivityState::default(),
|
||||
}));
|
||||
}
|
||||
110 => {
|
||||
// TODO: Haven't seen any of these.
|
||||
warn!("Found PostBuildHook: {}", serde_json::to_string(&message)?);
|
||||
entry.insert(Activity::PostBuildHook(ActivityPostBuildHook {
|
||||
id: *id,
|
||||
parent: *parent,
|
||||
state: ActivityState::default(),
|
||||
}));
|
||||
}
|
||||
111 => {
|
||||
// TODO: Haven't seen any of these.
|
||||
warn!("Found BuildWaiting: {}", serde_json::to_string(&message)?);
|
||||
entry.insert(Activity::BuildWaiting(ActivityBuildWaiting {
|
||||
id: *id,
|
||||
parent: *parent,
|
||||
state: ActivityState::default(),
|
||||
}));
|
||||
}
|
||||
112 => {
|
||||
// TODO: Haven't seen any of these.
|
||||
warn!("Found FetchTree: {}", serde_json::to_string(&message)?);
|
||||
entry.insert(Activity::FetchTree(ActivityFetchTree {
|
||||
id: *id,
|
||||
parent: *parent,
|
||||
state: ActivityState::default(),
|
||||
}));
|
||||
}
|
||||
_ => {
|
||||
panic!(
|
||||
"Unhandled start activity: {}",
|
||||
serde_json::to_string(&message)?
|
||||
);
|
||||
}
|
||||
};
|
||||
self.print_current_status();
|
||||
// self.print_current_status();
|
||||
}
|
||||
NixAction::Stop { id } => {
|
||||
let entry = self.activity_tree.entry(*id);
|
||||
match entry {
|
||||
std::collections::btree_map::Entry::Vacant(_vacant_entry) => {
|
||||
panic!("Stopped an activity that is not in the tree: {id}");
|
||||
}
|
||||
std::collections::btree_map::Entry::Occupied(mut occupied_entry) => {
|
||||
occupied_entry.get_mut().stop();
|
||||
}
|
||||
};
|
||||
self.print_current_status();
|
||||
NixAction::Stop(stop_message) => {
|
||||
let activity = self
|
||||
.activity_tree
|
||||
.get_activity_id(stop_message.id)
|
||||
.map(|activity_id| self.activity_tree.get_mut(&activity_id))?;
|
||||
activity.get_mut_activity().stop();
|
||||
// self.print_current_status();
|
||||
// println!("{}", serde_json::to_string(&message)?);
|
||||
}
|
||||
NixAction::Result { id, fields, r#type } => {
|
||||
match r#type {
|
||||
100 => {
|
||||
// FileLinked
|
||||
// TODO: Haven't seen any of these.
|
||||
warn!("Found FileLinked: {}", serde_json::to_string(&message)?);
|
||||
NixAction::Result(activity_result_message) => {
|
||||
match activity_result_message {
|
||||
ActivityResultMessage::FileLinked(_activity_result_file_linked) => {}
|
||||
ActivityResultMessage::BuildLogLine(_activity_result_build_log_line) => {
|
||||
// These are the output from the actual build (as opposed to the output from nix).
|
||||
}
|
||||
101 => {
|
||||
// BuildLogLine
|
||||
// The first field is a string containing the log line
|
||||
ActivityResultMessage::UntrustedPath(_activity_result_untrusted_path) => {}
|
||||
ActivityResultMessage::CorruptedPath(_activity_result_corrupted_path) => {}
|
||||
ActivityResultMessage::SetPhase(activity_result_set_phase) => {
|
||||
let activity_id = self
|
||||
.activity_tree
|
||||
.get_activity_id(activity_result_set_phase.id)?;
|
||||
let activity = self.activity_tree.get_mut(&activity_id);
|
||||
activity
|
||||
.get_mut_activity()
|
||||
.set_phase(Some(activity_result_set_phase.phase));
|
||||
}
|
||||
102 => {
|
||||
// UntrustedPath
|
||||
// TODO: Haven't seen any of these.
|
||||
warn!("Found UntrustedPath: {}", serde_json::to_string(&message)?);
|
||||
}
|
||||
103 => {
|
||||
// CorruptedPath
|
||||
// TODO: Haven't seen any of these.
|
||||
warn!("Found CorruptedPath: {}", serde_json::to_string(&message)?);
|
||||
}
|
||||
104 => {
|
||||
// SetPhase
|
||||
// The first field is the phase name
|
||||
}
|
||||
105 => {
|
||||
// Progress
|
||||
// Fields numerator, denominator, running?, failed?
|
||||
}
|
||||
106 => {
|
||||
// SetExpected
|
||||
// Fields activity type?, expected?
|
||||
}
|
||||
107 => {
|
||||
// PostBuildLogLine
|
||||
// TODO: Haven't seen any of these.
|
||||
warn!(
|
||||
"Found PostBuildLogLine: {}",
|
||||
serde_json::to_string(&message)?
|
||||
ActivityResultMessage::Progress(activity_result_progress) => {
|
||||
let activity_id = self
|
||||
.activity_tree
|
||||
.get_activity_id(activity_result_progress.id)?;
|
||||
let activity = self.activity_tree.get_mut(&activity_id);
|
||||
activity.get_mut_activity().set_progress(
|
||||
activity_result_progress.done,
|
||||
activity_result_progress.expected,
|
||||
activity_result_progress.running,
|
||||
activity_result_progress.failed,
|
||||
);
|
||||
}
|
||||
108 => {
|
||||
// FetchStatus
|
||||
// TODO: Haven't seen any of these.
|
||||
warn!("Found FetchStatus: {}", serde_json::to_string(&message)?);
|
||||
// println!("{}", serde_json::to_string(&message)?);
|
||||
}
|
||||
_ => {
|
||||
panic!("Unhandled result: {}", serde_json::to_string(&message)?);
|
||||
ActivityResultMessage::SetExpected(activity_result_set_expected) => {
|
||||
let activity_id = self
|
||||
.activity_tree
|
||||
.get_activity_id(activity_result_set_expected.id)?;
|
||||
let activity = self.activity_tree.get_mut(&activity_id);
|
||||
activity
|
||||
.get_mut_activity()
|
||||
.set_expected(activity_result_set_expected.expected);
|
||||
}
|
||||
ActivityResultMessage::PostBuildLogLine(
|
||||
_activity_result_post_build_log_line,
|
||||
) => {}
|
||||
ActivityResultMessage::FetchStatus(_activity_result_fetch_status) => {}
|
||||
};
|
||||
}
|
||||
};
|
||||
@ -381,281 +329,139 @@ impl<'db> RunningBuild<'db> {
|
||||
}
|
||||
|
||||
fn print_current_status(&self) -> () {
|
||||
// let in_progress = self
|
||||
// .activity_tree
|
||||
// .iter()
|
||||
// .filter(|activity: &(&u64, &ActivityTreeEntry)| activity.1.is_active());
|
||||
// let names: Vec<&str> = in_progress
|
||||
// .map(|activity| match activity.1 {
|
||||
// ActivityTreeEntry::Unknown {
|
||||
// id,
|
||||
// parent,
|
||||
// r#type,
|
||||
// state,
|
||||
// } => "unknown",
|
||||
// ActivityTreeEntry::System {
|
||||
// id,
|
||||
// parent,
|
||||
// r#type,
|
||||
// state,
|
||||
// } => "system",
|
||||
// ActivityTreeEntry::Download {
|
||||
// id,
|
||||
// parent,
|
||||
// r#type,
|
||||
// state,
|
||||
// url,
|
||||
// } => url,
|
||||
// ActivityTreeEntry::Build {
|
||||
// id,
|
||||
// parent,
|
||||
// r#type,
|
||||
// state,
|
||||
// path,
|
||||
// } => path,
|
||||
// })
|
||||
// .collect();
|
||||
// let name_list = names.join(", ");
|
||||
// println!("In progress: {name_list}");
|
||||
}
|
||||
}
|
||||
|
||||
enum ActivityState {
|
||||
Started {
|
||||
progress_numerator: u64,
|
||||
progress_denominator: u64,
|
||||
progress_running: u64,
|
||||
progress_failed: u64,
|
||||
},
|
||||
Stopped,
|
||||
}
|
||||
|
||||
impl Default for ActivityState {
|
||||
fn default() -> Self {
|
||||
ActivityState::Started {
|
||||
progress_numerator: 0,
|
||||
progress_denominator: 0,
|
||||
progress_running: 0,
|
||||
progress_failed: 0,
|
||||
let mut tree = String::new();
|
||||
let draw_order = self.get_draw_order();
|
||||
for dag_entry in draw_order {
|
||||
let leading_bars = {
|
||||
let mut leading_bars = String::with_capacity(3 * dag_entry.depth.len());
|
||||
for leading_bar in dag_entry
|
||||
.depth
|
||||
.iter()
|
||||
.map(|depth| if *depth { " " } else { " " })
|
||||
{
|
||||
leading_bars.push_str(leading_bar);
|
||||
}
|
||||
leading_bars
|
||||
};
|
||||
let branch = if dag_entry.has_later_siblings {
|
||||
""
|
||||
} else {
|
||||
""
|
||||
};
|
||||
let activity = self.activity_tree.get(&dag_entry.activity_id);
|
||||
let display_name = activity
|
||||
.get_activity()
|
||||
.display_name()
|
||||
.expect("Currently we always return a display name.");
|
||||
let activity_id = activity.get_activity_id();
|
||||
let parent_id = activity.get_parent_id();
|
||||
tree += &format!("{leading_bars}{branch} {display_name} {parent_id} {activity_id}\n");
|
||||
}
|
||||
if tree.is_empty() {
|
||||
println!("No active activities.");
|
||||
} else {
|
||||
print!("\n{}\n", tree);
|
||||
}
|
||||
}
|
||||
|
||||
fn get_draw_order(&self) -> Vec<DrawDagEntry> {
|
||||
let mut draw_order: Vec<DrawDagEntry> = Vec::new();
|
||||
let mut stack: Vec<DrawStackEntry> = self
|
||||
.get_children_in_order(self.activity_tree.get_root_id())
|
||||
.filter_map(|child| {
|
||||
if child.get_activity().is_active() {
|
||||
Some(DrawStackEntry::HasNotVisitedChildren(DrawDagEntry {
|
||||
activity_id: child.get_activity_id().clone(),
|
||||
depth: Vec::new(),
|
||||
has_later_siblings: true,
|
||||
}))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
if stack.len() == 0 {
|
||||
return draw_order;
|
||||
}
|
||||
stack
|
||||
.last_mut()
|
||||
.expect("If-statement ensured this is Some()")
|
||||
.set_no_later_siblings();
|
||||
|
||||
while !stack.is_empty() {
|
||||
let current_entry = stack.pop().expect("While-loop ensured this is Some.");
|
||||
match current_entry {
|
||||
DrawStackEntry::HasNotVisitedChildren(draw_dag_entry) => {
|
||||
let current_id = draw_dag_entry.activity_id.clone();
|
||||
let mut current_depth = draw_dag_entry.depth.clone();
|
||||
current_depth.push(draw_dag_entry.has_later_siblings);
|
||||
stack.push(DrawStackEntry::VisitedChildren(draw_dag_entry));
|
||||
let children: Vec<ActivityId> = self
|
||||
.get_children_in_order(current_id)
|
||||
.filter_map(|child| {
|
||||
if child.get_activity().is_active() {
|
||||
Some(child.get_activity_id().clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
let has_children = !children.is_empty();
|
||||
stack.extend(children.into_iter().map(|child_id| {
|
||||
DrawStackEntry::HasNotVisitedChildren(DrawDagEntry {
|
||||
activity_id: child_id,
|
||||
depth: current_depth.clone(),
|
||||
has_later_siblings: true,
|
||||
})
|
||||
}));
|
||||
if has_children {
|
||||
stack
|
||||
.last_mut()
|
||||
.expect("If-statement ensured this is Some()")
|
||||
.set_no_later_siblings();
|
||||
}
|
||||
}
|
||||
DrawStackEntry::VisitedChildren(draw_dag_entry) => {
|
||||
draw_order.push(draw_dag_entry);
|
||||
}
|
||||
};
|
||||
}
|
||||
draw_order
|
||||
}
|
||||
|
||||
fn get_children_in_order(
|
||||
&self,
|
||||
parent_id: ActivityId,
|
||||
) -> impl Iterator<Item = &ActivityTreeEntry> {
|
||||
let parent = self.activity_tree.get(&parent_id);
|
||||
parent
|
||||
.get_child_ids()
|
||||
.iter()
|
||||
.map(|child_id| self.activity_tree.get(child_id))
|
||||
.flat_map(|child| TransparentIter::new(&self.activity_tree, child))
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(u8)]
|
||||
enum Activity {
|
||||
Unknown(ActivityUnknown),
|
||||
CopyPath(ActivityCopyPath),
|
||||
FileTransfer(ActivityFileTransfer),
|
||||
Realize(ActivityRealize),
|
||||
CopyPaths(ActivityCopyPaths),
|
||||
Builds(ActivityBuilds),
|
||||
Build(ActivityBuild),
|
||||
OptimizeStore(ActivityOptimizeStore),
|
||||
VerifyPaths(ActivityVerifyPaths),
|
||||
Substitute(ActivitySubstitute),
|
||||
QueryPathInfo(ActivityQueryPathInfo),
|
||||
PostBuildHook(ActivityPostBuildHook),
|
||||
BuildWaiting(ActivityBuildWaiting),
|
||||
FetchTree(ActivityFetchTree),
|
||||
enum DrawStackEntry {
|
||||
HasNotVisitedChildren(DrawDagEntry),
|
||||
VisitedChildren(DrawDagEntry),
|
||||
}
|
||||
|
||||
impl Activity {
|
||||
fn get_type(&self) -> u8 {
|
||||
struct DrawDagEntry {
|
||||
activity_id: ActivityId,
|
||||
depth: Vec<bool>,
|
||||
has_later_siblings: bool,
|
||||
}
|
||||
|
||||
impl DrawStackEntry {
|
||||
fn set_no_later_siblings(&mut self) -> () {
|
||||
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) => {
|
||||
activity_unknown.state = ActivityState::Stopped;
|
||||
DrawStackEntry::HasNotVisitedChildren(draw_dag_entry) => {
|
||||
draw_dag_entry.has_later_siblings = false
|
||||
}
|
||||
Activity::CopyPath(activity_copy_path) => {
|
||||
activity_copy_path.state = ActivityState::Stopped;
|
||||
DrawStackEntry::VisitedChildren(draw_dag_entry) => {
|
||||
draw_dag_entry.has_later_siblings = false
|
||||
}
|
||||
Activity::FileTransfer(activity_file_transfer) => {
|
||||
activity_file_transfer.state = ActivityState::Stopped;
|
||||
}
|
||||
Activity::Realize(activity_realize) => {
|
||||
activity_realize.state = ActivityState::Stopped;
|
||||
}
|
||||
Activity::CopyPaths(activity_copy_paths) => {
|
||||
activity_copy_paths.state = ActivityState::Stopped;
|
||||
}
|
||||
Activity::Builds(activity_builds) => {
|
||||
activity_builds.state = ActivityState::Stopped;
|
||||
}
|
||||
Activity::Build(activity_build) => {
|
||||
activity_build.state = ActivityState::Stopped;
|
||||
}
|
||||
Activity::OptimizeStore(activity_optimize_store) => {
|
||||
activity_optimize_store.state = ActivityState::Stopped;
|
||||
}
|
||||
Activity::VerifyPaths(activity_verify_paths) => {
|
||||
activity_verify_paths.state = ActivityState::Stopped;
|
||||
}
|
||||
Activity::Substitute(activity_substitute) => {
|
||||
activity_substitute.state = ActivityState::Stopped;
|
||||
}
|
||||
Activity::QueryPathInfo(activity_query_path_info) => {
|
||||
activity_query_path_info.state = ActivityState::Stopped;
|
||||
}
|
||||
Activity::PostBuildHook(activity_post_build_hook) => {
|
||||
activity_post_build_hook.state = ActivityState::Stopped;
|
||||
}
|
||||
Activity::BuildWaiting(activity_build_waiting) => {
|
||||
activity_build_waiting.state = ActivityState::Stopped;
|
||||
}
|
||||
Activity::FetchTree(activity_fetch_tree) => {
|
||||
activity_fetch_tree.state = ActivityState::Stopped;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ActivityUnknown {
|
||||
id: u64,
|
||||
parent: u64,
|
||||
state: ActivityState,
|
||||
level: u8,
|
||||
text: String,
|
||||
}
|
||||
struct ActivityCopyPath {
|
||||
id: u64,
|
||||
parent: u64,
|
||||
state: ActivityState,
|
||||
}
|
||||
struct ActivityFileTransfer {
|
||||
id: u64,
|
||||
parent: u64,
|
||||
state: ActivityState,
|
||||
level: u8,
|
||||
text: String,
|
||||
url: String,
|
||||
}
|
||||
struct ActivityRealize {
|
||||
id: u64,
|
||||
parent: u64,
|
||||
state: ActivityState,
|
||||
level: u8,
|
||||
text: String,
|
||||
}
|
||||
struct ActivityCopyPaths {
|
||||
id: u64,
|
||||
parent: u64,
|
||||
state: ActivityState,
|
||||
level: u8,
|
||||
text: String,
|
||||
}
|
||||
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,
|
||||
}
|
||||
struct ActivityOptimizeStore {
|
||||
id: u64,
|
||||
parent: u64,
|
||||
state: ActivityState,
|
||||
}
|
||||
struct ActivityVerifyPaths {
|
||||
id: u64,
|
||||
parent: u64,
|
||||
state: ActivityState,
|
||||
}
|
||||
struct ActivitySubstitute {
|
||||
id: u64,
|
||||
parent: u64,
|
||||
state: ActivityState,
|
||||
}
|
||||
struct ActivityQueryPathInfo {
|
||||
id: u64,
|
||||
parent: u64,
|
||||
state: ActivityState,
|
||||
}
|
||||
struct ActivityPostBuildHook {
|
||||
id: u64,
|
||||
parent: u64,
|
||||
state: ActivityState,
|
||||
}
|
||||
struct ActivityBuildWaiting {
|
||||
id: u64,
|
||||
parent: u64,
|
||||
state: ActivityState,
|
||||
}
|
||||
struct ActivityFetchTree {
|
||||
id: u64,
|
||||
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<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."),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
90
src/nix_util/transparent_iter.rs
Normal file
90
src/nix_util/transparent_iter.rs
Normal file
@ -0,0 +1,90 @@
|
||||
use super::activity_tree::ActivityTree;
|
||||
use super::activity_tree::ActivityTreeEntry;
|
||||
|
||||
/// An iterator that returns either the original activity if it is not transparent, or if it is transparent, the earliest non-transparent children.
|
||||
pub(crate) struct TransparentIter<'tree> {
|
||||
activity_tree: &'tree ActivityTree,
|
||||
origin: Option<&'tree ActivityTreeEntry>,
|
||||
child_index: Vec<usize>,
|
||||
}
|
||||
|
||||
impl<'tree> Iterator for TransparentIter<'tree> {
|
||||
type Item = &'tree ActivityTreeEntry;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let origin = match self.origin {
|
||||
Some(o) => o,
|
||||
None => {
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
if !origin.get_activity().is_transparent() {
|
||||
let origin = self
|
||||
.origin
|
||||
.take()
|
||||
.expect("We would have returned early if origin was None.");
|
||||
return Some(origin);
|
||||
}
|
||||
|
||||
if self.child_index.is_empty() {
|
||||
self.child_index.push(0);
|
||||
}
|
||||
|
||||
loop {
|
||||
let current_entry = self.get_current_entry();
|
||||
if let Some(child) = current_entry {
|
||||
if child.get_activity().is_transparent() {
|
||||
self.child_index.push(0);
|
||||
} else {
|
||||
let last_index = self.child_index.last_mut().expect("Stack cannot be empty.");
|
||||
*last_index += 1;
|
||||
return Some(child);
|
||||
}
|
||||
} else {
|
||||
self.child_index.pop();
|
||||
if self.child_index.is_empty() {
|
||||
self.origin.take();
|
||||
return None;
|
||||
}
|
||||
let last_index = self.child_index.last_mut().expect("Stack cannot be empty.");
|
||||
*last_index += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tree> TransparentIter<'tree> {
|
||||
pub(crate) fn new(
|
||||
activity_tree: &'tree ActivityTree,
|
||||
origin: &'tree ActivityTreeEntry,
|
||||
) -> TransparentIter<'tree> {
|
||||
TransparentIter {
|
||||
activity_tree,
|
||||
origin: Some(origin),
|
||||
child_index: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_current_entry(&mut self) -> Option<&'tree ActivityTreeEntry> {
|
||||
let mut current_entry = match self.origin {
|
||||
Some(o) => o,
|
||||
None => {
|
||||
return None;
|
||||
}
|
||||
};
|
||||
for ind in self.child_index.iter() {
|
||||
if let Some(child) = current_entry
|
||||
.get_child_ids()
|
||||
.get(*ind)
|
||||
.map(|child_index| self.activity_tree.get(child_index))
|
||||
{
|
||||
current_entry = child;
|
||||
continue;
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
Some(current_entry)
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user