diff --git a/.gitignore b/.gitignore index defb674..135676c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ # Rust/Cargo /target +**/target # Direnv /.direnv diff --git a/Cargo.lock b/Cargo.lock index 2768fe6..bf6aae2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,554 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "bitflags" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" + +[[package]] +name = "bytes" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "fixedbitset" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "futures" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-executor" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash", +] + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" + +[[package]] +name = "indexmap" +version = "2.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2" +dependencies = [ + "equivalent", + "hashbrown 0.16.1", +] + +[[package]] +name = "itoa" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" + +[[package]] +name = "libc" +version = "0.2.178" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091" + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "memchr" +version = "2.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" + +[[package]] +name = "mio" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.61.2", +] + +[[package]] +name = "nix-json" +version = "0.1.0" +dependencies = [ + "serde", + "serde_json", +] + [[package]] name = "nixie-build" version = "0.1.0" +dependencies = [ + "displaydoc", + "futures", + "nix-json", + "petgraph", + "serde", + "serde_json", + "thiserror", + "tokio", +] + +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-link", +] + +[[package]] +name = "petgraph" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8701b58ea97060d5e5b155d383a69952a60943f0e6dfe30b04c287beb0b27455" +dependencies = [ + "fixedbitset", + "hashbrown 0.15.5", + "indexmap", + "serde", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "proc-macro2" +version = "1.0.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9695f8df41bb4f3d222c95a67532365f569318332d03d5f3f67f37b20e6ebdf0" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.148" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3084b546a1dd6289475996f182a22aba973866ea8e8b02c51d9f46b1336a22da" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" +dependencies = [ + "errno", + "libc", +] + +[[package]] +name = "slab" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "socket2" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881" +dependencies = [ + "libc", + "windows-sys 0.60.2", +] + +[[package]] +name = "syn" +version = "2.0.111" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "2.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio" +version = "1.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408" +dependencies = [ + "bytes", + "libc", + "mio", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.61.2", +] + +[[package]] +name = "tokio-macros" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "unicode-ident" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-targets" +version = "0.53.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" + +[[package]] +name = "windows_i686_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" + +[[package]] +name = "windows_i686_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" + +[[package]] +name = "zmij" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f4a4e8e9dc5c62d159f04fcdbe07f4c3fb710415aab4754bf11505501e3251d" diff --git a/Cargo.toml b/Cargo.toml index 50caee9..249cd27 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,12 +1,23 @@ [workspace] -members = ["nixie-build"] +members = ["nix-json", "nixie-build"] resolver = "3" [workspace.package] edition = "2024" version = "0.1.0" -license = "EUPL-1.2" +license = "EUPLs-1.2" authors = ["Marcel Müller "] readme = "./README.md" + +[workspace.dependencies] +thiserror = "2" +displaydoc = "0.2" +serde_json = "1" +serde = { features = ["derive"], version = "1" } +petgraph = "0.8" +tokio = { features = ["full"], version = "1.48" } +futures = "0.3" + +nix-json = { version = "0.1.0", path = "./nix-json" } diff --git a/nix-json/Cargo.toml b/nix-json/Cargo.toml new file mode 100644 index 0000000..531f5ba --- /dev/null +++ b/nix-json/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "nix-json" +edition.workspace = true +version = "0.1.0" +license.workspace = true +authors.workspace = true +readme.workspace = true + +[dependencies] +serde.workspace = true +serde_json.workspace = true diff --git a/nix-json/src/lib.rs b/nix-json/src/lib.rs new file mode 100644 index 0000000..6fcb446 --- /dev/null +++ b/nix-json/src/lib.rs @@ -0,0 +1,96 @@ +use std::collections::HashMap; + +use serde::Deserialize; + +#[derive(Debug, Deserialize)] +#[serde(tag = "action", rename_all = "lowercase")] +pub enum NixBuildLogLine { + Start(NixLogStartAction), + Stop(NixLogStartAction), + Result(NixLogResultAction), + Msg(NixLogMsgAction), +} + +#[derive(Debug, Deserialize)] +pub struct NixLogResultAction { + pub id: i64, + #[serde(rename = "type")] + pub kind: i64, + #[serde(default)] + pub fields: Vec, +} + +#[derive(Debug, Deserialize)] +pub struct NixLogStopAction { + pub id: i64, +} + +#[derive(Debug, Deserialize)] +pub struct NixLogMsgAction { + pub level: Option, + pub msg: String, +} + +#[derive(Debug, Deserialize)] +pub struct NixLogStartAction { + pub id: i64, + pub level: Option, + pub parent: Option, + pub text: Option, + #[serde(rename = "type")] + pub kind: Option, + #[serde(default)] + pub fields: Vec, +} + +#[derive(Debug, Deserialize)] +pub struct RawNixDerivationInfoOutput(HashMap); + +impl RawNixDerivationInfoOutput { + pub fn info(&self) -> &HashMap { + &self.0 + } +} + +#[derive(Debug, Deserialize)] +pub struct RawNixDerivationInfo { + args: Vec, + builder: String, + #[serde(rename = "inputDrvs")] + input_derivations: HashMap, + #[serde(rename = "inputSrcs")] + input_sources: Vec, + name: String, + outputs: HashMap>, + system: String, +} + +impl RawNixDerivationInfo { + pub fn args(&self) -> &[String] { + &self.args + } + + pub fn builder(&self) -> &str { + &self.builder + } + + pub fn input_derivations(&self) -> &HashMap { + &self.input_derivations + } + + pub fn input_sources(&self) -> &[String] { + &self.input_sources + } + + pub fn name(&self) -> &str { + &self.name + } + + pub fn outputs(&self) -> &HashMap> { + &self.outputs + } + + pub fn system(&self) -> &str { + &self.system + } +} diff --git a/nixie-build/Cargo.toml b/nixie-build/Cargo.toml index bc344a5..c35c767 100644 --- a/nixie-build/Cargo.toml +++ b/nixie-build/Cargo.toml @@ -7,3 +7,11 @@ authors.workspace = true readme.workspace = true [dependencies] +thiserror.workspace = true +displaydoc.workspace = true +serde.workspace = true +serde_json.workspace = true +petgraph.workspace = true +tokio.workspace = true +futures.workspace = true +nix-json.workspace = true diff --git a/nixie-build/src/lib.rs b/nixie-build/src/lib.rs index b93cf3f..0ed4fa0 100644 --- a/nixie-build/src/lib.rs +++ b/nixie-build/src/lib.rs @@ -1,14 +1,265 @@ -pub fn add(left: u64, right: u64) -> u64 { - left + right +use std::collections::HashMap; +use std::fmt::Write; +use std::process::Stdio; + +use futures::FutureExt; +use futures::StreamExt; +use futures::stream::BoxStream; +use nix_json::NixBuildLogLine; +use nix_json::RawNixDerivationInfoOutput; +use petgraph::Directed; +use petgraph::Graph; +use petgraph::prelude::NodeIndex; +use tokio::io::AsyncBufReadExt; +use tokio::process::Command; + +#[derive(Debug)] +pub struct NixBuildResult { + derivation: String, + log: String, + success: bool, +} + +impl NixBuildResult { + pub fn derivation(&self) -> &str { + &self.derivation + } + + pub fn log(&self) -> &str { + &self.log + } + + pub fn success(&self) -> bool { + self.success + } +} + +#[derive(Debug)] +pub enum NixBuildError {} + +pub trait NixInProgressBuild { + fn next_log_line( + &mut self, + ) -> impl Future>>; +} + +pub trait NixBackend { + type InProgressBuild<'b>: NixInProgressBuild + where + Self: 'b; + + fn start_build( + &self, + derivation: &str, + ) -> impl Future, NixBuildError>>; + + fn get_needed_builds( + &self, + derivation: &str, + ) -> impl Future>; +} + +pub struct NixCliBackend { + command_path: String, +} + +pub struct NixCliBackendBuild { + child: BoxStream<'static, String>, +} + +impl NixInProgressBuild for NixCliBackendBuild { + fn next_log_line( + &mut self, + ) -> impl Future>> { + self.child.next().map(|out| { + out.map(|line| Ok(serde_json::from_str(line.trim_start_matches("@nix ")).unwrap())) + }) + } +} + +impl NixBackend for NixCliBackend { + type InProgressBuild<'b> = NixCliBackendBuild; + + fn start_build( + &self, + derivation: &str, + ) -> impl Future, NixBuildError>> { + async move { + let mut cmd = Command::new(&self.command_path) + .args(["build", "--log-format", "internal-json"]) + .arg(derivation) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn() + .unwrap(); + + let init = cmd.stderr.take().unwrap(); + Ok(NixCliBackendBuild { + child: futures::stream::unfold( + tokio::io::BufReader::new(init), + move |mut state| async move { + let mut buffer = String::new(); + let num_bytes = state.read_line(&mut buffer).await.unwrap(); + + if num_bytes == 0 { + None + } else { + Some((std::mem::take(&mut buffer), state)) + } + }, + ) + .boxed(), + }) + } + } + + fn get_needed_builds( + &self, + derivation: &str, + ) -> impl Future> { + async move { + let cmd = Command::new(&self.command_path) + .args(["derivation", "show", "--recursive"]) + .arg(derivation) + .output() + .await + .unwrap(); + + let output: RawNixDerivationInfoOutput = serde_json::from_slice(&cmd.stdout).unwrap(); + + let mut build_graph = NixBuildGraph::default(); + + for (path, _info) in output.info() { + let internal_id = build_graph.dependencies.add_node(path.to_string()); + build_graph.build_infos.insert( + path.to_string(), + NixBuildInfo { + internal_id, + present_in_binary_cache: false, + }, + ); + } + + for (path, info) in output.info() { + let build_info = &build_graph.build_infos[path]; + let cur_node = build_info.internal_id; + + for (dep_path, _dep_info) in info.input_derivations() { + let other_node = build_graph.build_infos[dep_path].internal_id; + build_graph.dependencies.add_edge( + cur_node, + other_node, + NixBuildOutput { + output_name: String::new(), + }, + ); + } + } + + Ok(build_graph) + } + } +} + +pub struct NixBuilder { + backend: B, +} + +#[derive(Debug, Clone)] +pub struct NixBuildInfo { + internal_id: NodeIndex, + present_in_binary_cache: bool, +} + +#[derive(Debug)] +pub struct NixBuildOutput { + output_name: String, +} + +#[derive(Debug, Default)] +pub struct NixBuildGraph { + build_infos: HashMap, + dependencies: Graph, +} + +impl NixBuildGraph { + fn get_non_binary_builds(&self) -> impl Iterator { + self.build_infos + .iter() + .filter(|(_, value)| !value.present_in_binary_cache) + .map(|(key, value)| (key.clone(), value.clone())) + } +} + +impl NixBuilder +where + B: NixBackend, +{ + pub async fn build( + &self, + derivation: String, + ) -> Result, NixBuildError> { + let needed_builds = self.backend.get_needed_builds(&derivation).await?; + let mut started_build = self.backend.start_build(&derivation).await?; + + let mut actually_built = + HashMap::from_iter(needed_builds.get_non_binary_builds().map(|(k, _v)| { + ( + k.clone(), + NixBuildResult { + derivation: k, + log: String::new(), + success: false, + }, + ) + })); + + let mut id_to_derivation = HashMap::new(); + + while let Some(next_log_line) = started_build.next_log_line().await { + let next_log_line = next_log_line?; + + if let NixBuildLogLine::Start(start_log) = next_log_line { + if start_log.kind == Some(105) { + id_to_derivation.insert( + start_log.id, + start_log.fields[0].as_str().unwrap().to_string(), + ); + } + } else if let NixBuildLogLine::Result(result) = next_log_line { + if result.kind == 101 { + if let Some(build) = actually_built.get_mut(&id_to_derivation[&result.id]) { + writeln!(&mut build.log, "{}", result.fields[0].as_str().unwrap()).unwrap() + } else { + panic!("Could not find correct id"); + } + } + } + } + + actually_built.retain(|_k, v| !v.log.is_empty()); + + Ok(actually_built) + } } #[cfg(test)] mod tests { use super::*; - #[test] - fn it_works() { - let result = add(2, 2); - assert_eq!(result, 4); + #[tokio::test] + async fn check_unpure() { + let builder = NixBuilder { + backend: NixCliBackend { + command_path: String::from("nix"), + }, + }; + + let infos = builder + .build(".#checks.x86_64-linux.crate-fmt".to_string()) + .await + .unwrap(); + + println!("Got: {infos:#?}"); } }