Change-Id: Iab7e00cc26a4f9727d3ab98691ef379921a33052 Reviewed-on: https://cl.tvl.fyi/c/depot/+/5240 Tested-by: BuildkiteCI Reviewed-by: kanepyork <rikingcoding@gmail.com> Reviewed-by: Profpatsch <mail@profpatsch.de> Reviewed-by: grfn <grfn@gws.fyi> Reviewed-by: tazjin <tazjin@tvl.su>
		
			
				
	
	
		
			149 lines
		
	
	
	
		
			4.4 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			149 lines
		
	
	
	
		
			4.4 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
| 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"))
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| }
 |