diff options
Diffstat (limited to 'nix/nint/nint.rs')
-rw-r--r-- | nix/nint/nint.rs | 149 |
1 files changed, 149 insertions, 0 deletions
diff --git a/nix/nint/nint.rs b/nix/nint/nint.rs new file mode 100644 index 000000000000..abb0153c3ad2 --- /dev/null +++ b/nix/nint/nint.rs @@ -0,0 +1,149 @@ +extern crate serde_json; + +use serde_json::Value; +use std::convert::TryFrom; +use std::ffi::OsString; +use std::io::{stderr, stdout, Error, ErrorKind, Write}; +use std::os::unix::ffi::{OsStrExt, OsStringExt}; +use std::process::Command; + +fn render_nix_string(s: &OsString) -> OsString { + let mut rendered = Vec::new(); + + rendered.extend(b"\""); + + for b in s.as_os_str().as_bytes() { + match char::from(*b) { + '\"' => rendered.extend(b"\\\""), + '\\' => rendered.extend(b"\\\\"), + '$' => rendered.extend(b"\\$"), + _ => rendered.push(*b), + } + } + + rendered.extend(b"\""); + + OsString::from_vec(rendered) +} + +fn render_nix_list(arr: &[OsString]) -> OsString { + let mut rendered = Vec::new(); + + rendered.extend(b"[ "); + + for el in arr { + rendered.extend(render_nix_string(el).as_os_str().as_bytes()); + rendered.extend(b" "); + } + + rendered.extend(b"]"); + + OsString::from_vec(rendered) +} + +/// Slightly overkill helper macro which takes a `Map<String, Value>` obtained +/// from `Value::Object` and an output name (`stderr` or `stdout`) as an +/// identifier. If a value exists for the given output in the object it gets +/// written to the appropriate output. +macro_rules! handle_set_output { + ($map_name:ident, $output_name:ident) => { + match $map_name.get(stringify!($output_name)) { + Some(Value::String(s)) => $output_name().write_all(s.as_bytes()), + Some(_) => Err(Error::new( + ErrorKind::Other, + format!("Attribute {} must be a string!", stringify!($output_name)), + )), + None => Ok(()), + } + }; +} + +fn main() -> std::io::Result<()> { + let mut nix_args = Vec::new(); + + let mut args = std::env::args_os().into_iter(); + let mut in_args = true; + + let mut argv: Vec<OsString> = Vec::new(); + + // skip argv[0] + args.next(); + + loop { + let arg = match args.next() { + Some(a) => a, + None => break, + }; + + if !arg.to_str().map(|s| s.starts_with("-")).unwrap_or(false) { + in_args = false; + } + + if in_args { + match (arg.to_str()) { + Some("--arg") | Some("--argstr") => { + nix_args.push(arg); + nix_args.push(args.next().unwrap()); + nix_args.push(args.next().unwrap()); + Ok(()) + } + _ => Err(Error::new(ErrorKind::Other, "unknown argument")), + }? + } else { + argv.push(arg); + } + } + + if argv.len() < 1 { + Err(Error::new(ErrorKind::Other, "missing argv")) + } else { + let cd = std::env::current_dir()?.into_os_string(); + + nix_args.push(OsString::from("--arg")); + nix_args.push(OsString::from("currentDir")); + nix_args.push(cd); + + nix_args.push(OsString::from("--arg")); + nix_args.push(OsString::from("argv")); + nix_args.push(render_nix_list(&argv[..])); + + nix_args.push(OsString::from("--eval")); + nix_args.push(OsString::from("--strict")); + nix_args.push(OsString::from("--json")); + + nix_args.push(argv[0].clone()); + + let run = Command::new("nix-instantiate").args(nix_args).output()?; + + match serde_json::from_slice(&run.stdout[..]) { + Ok(Value::String(s)) => stdout().write_all(s.as_bytes()), + Ok(Value::Object(m)) => { + handle_set_output!(m, stdout)?; + handle_set_output!(m, stderr)?; + + match m.get("exit") { + Some(Value::Number(n)) => { + let code = n.as_i64().and_then(|v| i32::try_from(v).ok()); + + match code { + Some(i) => std::process::exit(i), + None => { + Err(Error::new(ErrorKind::Other, "Attribute exit is not an i32")) + } + } + } + Some(_) => Err(Error::new(ErrorKind::Other, "exit must be a number")), + None => Ok(()), + } + } + Ok(_) => Err(Error::new( + ErrorKind::Other, + "output must be a string or an object", + )), + _ => { + stderr().write_all(&run.stderr[..]); + Err(Error::new(ErrorKind::Other, "internal nix error")) + } + } + } +} |