Move the running build into its own struct.

This commit is contained in:
Tom Alexander
2026-02-16 20:06:46 -05:00
parent dd99900b90
commit ab1f86384b
3 changed files with 133 additions and 109 deletions

View File

@@ -3,17 +3,13 @@ use std::ffi::OsString;
use std::path::Path; use std::path::Path;
use std::process::Stdio; use std::process::Stdio;
use tokio::io::AsyncBufReadExt;
use tokio::io::BufReader;
use tokio::io::Lines;
use tokio::process::Child;
use tokio::process::ChildStderr;
use tokio::process::ChildStdout;
use tokio::process::Command; use tokio::process::Command;
use crate::Result; use crate::Result;
use crate::database::db_handle::DbHandle; use crate::database::db_handle::DbHandle;
use super::running_build::RunningBuild;
pub(crate) async fn nixos_build_target<B, F, A>( pub(crate) async fn nixos_build_target<B, F, A>(
db_handle: &DbHandle, db_handle: &DbHandle,
build_path: B, build_path: B,
@@ -56,110 +52,10 @@ where
command.arg(reference); command.arg(reference);
command.kill_on_drop(true); command.kill_on_drop(true);
let mut child = command.spawn()?; let child = command.spawn()?;
let mut output_stream = OutputSream::from_child(&mut child)?; let mut running_build = RunningBuild::new(db_handle)?;
running_build.run_to_completion(child).await?;
let exit_status_handle = tokio::spawn(async move {
let status = child
.wait()
.await
.expect("nixos-rebuild encountered an error");
status
});
loop {
let next_line = output_stream.next_line().await?;
match next_line {
OutputLine::Stdout(line) => {
println!("STDOUT: {line}");
}
OutputLine::Stderr(line) => {
println!("STDERR: {line}");
}
OutputLine::Done => break,
};
}
let exit_status = exit_status_handle.await?;
println!("nixos-rebuild status was: {}", exit_status);
Ok(()) Ok(())
} }
struct OutputSream {
stdout: Option<Lines<BufReader<ChildStdout>>>,
stderr: Option<Lines<BufReader<ChildStderr>>>,
}
impl OutputSream {
pub(crate) fn from_child(child: &mut Child) -> Result<Self> {
let stdout = child
.stdout
.take()
.expect("child did not have a handle to stdout");
let stderr = child
.stderr
.take()
.expect("child did not have a handle to stderr");
let stdout = BufReader::new(stdout).lines();
let stderr = BufReader::new(stderr).lines();
Ok(OutputSream {
stdout: Some(stdout),
stderr: Some(stderr),
})
}
pub(crate) async fn next_line(&mut self) -> Result<OutputLine> {
loop {
match (&mut self.stdout, &mut self.stderr) {
(None, None) => {
return Ok(OutputLine::Done);
}
(None, Some(err)) => {
if let Some(line) = err.next_line().await? {
return Ok(OutputLine::Stderr(line));
} else {
return Ok(OutputLine::Done);
}
}
(Some(out), None) => {
if let Some(line) = out.next_line().await? {
return Ok(OutputLine::Stdout(line));
} else {
return Ok(OutputLine::Done);
}
}
(Some(out), Some(err)) => {
tokio::select! {
Ok(line) = out.next_line() => match line {
Some(line) => {
return Ok(OutputLine::Stdout(line));
},
None => {
self.stdout.take();
},
},
Ok(line) = err.next_line() => match line {
Some(line) => {
return Ok(OutputLine::Stderr(line));
},
None => {
self.stderr.take();
},
},
else => {
return Ok(OutputLine::Done);
},
};
}
};
}
}
}
enum OutputLine {
Stdout(String),
Stderr(String),
Done,
}

View File

@@ -1,2 +1,3 @@
mod high_level; mod high_level;
mod running_build;
pub(crate) use high_level::*; pub(crate) use high_level::*;

View File

@@ -0,0 +1,127 @@
use tokio::io::AsyncBufReadExt;
use tokio::io::BufReader;
use tokio::io::Lines;
use tokio::process::Child;
use tokio::process::ChildStderr;
use tokio::process::ChildStdout;
use crate::Result;
use crate::database::db_handle::DbHandle;
pub(crate) struct RunningBuild<'db> {
db_handle: &'db DbHandle,
}
impl<'db> RunningBuild<'db> {
pub(crate) fn new(db_handle: &'db DbHandle) -> Result<Self> {
Ok(RunningBuild { db_handle })
}
pub(crate) async fn run_to_completion(&mut self, mut child: Child) -> Result<()> {
let mut output_stream = OutputStream::from_child(&mut child)?;
let exit_status_handle = tokio::spawn(async move {
let status = child
.wait()
.await
.expect("nixos-rebuild encountered an error");
status
});
loop {
let next_line = output_stream.next_line().await?;
match next_line {
OutputLine::Stdout(line) | OutputLine::Stderr(line) => {
self.handle_line(line)?;
}
OutputLine::Done => break,
};
}
let exit_status = exit_status_handle.await?;
println!("nixos-rebuild status was: {}", exit_status);
Ok(())
}
fn handle_line(&mut self, line: String) -> Result<()> {
Ok(())
}
}
struct OutputStream {
stdout: Option<Lines<BufReader<ChildStdout>>>,
stderr: Option<Lines<BufReader<ChildStderr>>>,
}
impl OutputStream {
pub(crate) fn from_child(child: &mut Child) -> Result<Self> {
let stdout = child
.stdout
.take()
.expect("child did not have a handle to stdout");
let stderr = child
.stderr
.take()
.expect("child did not have a handle to stderr");
let stdout = BufReader::new(stdout).lines();
let stderr = BufReader::new(stderr).lines();
Ok(OutputStream {
stdout: Some(stdout),
stderr: Some(stderr),
})
}
pub(crate) async fn next_line(&mut self) -> Result<OutputLine> {
loop {
match (&mut self.stdout, &mut self.stderr) {
(None, None) => {
return Ok(OutputLine::Done);
}
(None, Some(err)) => {
if let Some(line) = err.next_line().await? {
return Ok(OutputLine::Stderr(line));
} else {
return Ok(OutputLine::Done);
}
}
(Some(out), None) => {
if let Some(line) = out.next_line().await? {
return Ok(OutputLine::Stdout(line));
} else {
return Ok(OutputLine::Done);
}
}
(Some(out), Some(err)) => {
tokio::select! {
Ok(line) = out.next_line() => match line {
Some(line) => {
return Ok(OutputLine::Stdout(line));
},
None => {
self.stdout.take();
},
},
Ok(line) = err.next_line() => match line {
Some(line) => {
return Ok(OutputLine::Stderr(line));
},
None => {
self.stderr.take();
},
},
else => {
return Ok(OutputLine::Done);
},
};
}
};
}
}
}
enum OutputLine {
Stdout(String),
Stderr(String),
Done,
}