diff --git a/src/lib.rs b/src/lib.rs index 6e4ba7aa85..b1ba1b1b88 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1580,18 +1580,15 @@ impl Bindings { /// Write these bindings as source text to a file. pub fn write_to_file>(&self, path: P) -> io::Result<()> { - { - let file = try!( - OpenOptions::new() - .write(true) - .truncate(true) - .create(true) - .open(path.as_ref()) - ); - self.write(Box::new(file))?; - } - - self.rustfmt_generated_file(path.as_ref()) + let file = try!( + OpenOptions::new() + .write(true) + .truncate(true) + .create(true) + .open(path.as_ref()) + ); + self.write(Box::new(file))?; + Ok(()) } /// Write these bindings as source text to the given `Write`able. @@ -1604,11 +1601,19 @@ impl Bindings { writer.write(line.as_bytes())?; writer.write("\n".as_bytes())?; } + if !self.options.raw_lines.is_empty() { writer.write("\n".as_bytes())?; } - writer.write(self.module.as_str().as_bytes())?; + let bindings = self.module.as_str().to_string(); + + match self.rustfmt_generated_string(bindings) { + Ok(rustfmt_bindings) => { + writer.write(rustfmt_bindings.as_str().as_bytes())?; + }, + Err(err) => eprintln!("{:?}", err), + } Ok(()) } @@ -1668,6 +1673,79 @@ impl Bindings { )) } } + + /// Checks if rustfmt_bindings is set and runs rustfmt on the string + //fn rustfmt_generated_string(&self, source: String) -> io::Result> { + fn rustfmt_generated_string(&self, source: String) -> io::Result { + let _t = self.context.timer("rustfmt_generated_string"); + + if !self.context.options().rustfmt_bindings { + return Ok(source); + } + + let rustfmt = if let Ok(rustfmt) = which::which("rustfmt") { + rustfmt + } else { + eprintln!("warning: could not find usable rustfmt to pretty print bindings"); + return Ok(source); + }; + + let mut cmd = if let Ok(rustup) = which::which("rustup") { + let mut cmd = Command::new(rustup); + cmd.args(&["run", "nightly", "rustfmt", "--"]); + cmd + } else { + Command::new(rustfmt) + }; + + cmd + .args(&["--write-mode=display"]) + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()); + + if let Some(path) = self.context + .options() + .rustfmt_configuration_file + .as_ref() + .and_then(|f| f.to_str()) + { + cmd.args(&["--config-path", path]); + } + + let mut child = cmd.spawn().expect("Failed to spawn rustfmt."); + let mut child_stdin = child.stdin.take().unwrap(); + let mut child_stdout = child.stdout.take().unwrap(); + let mut child_stderr = child.stderr.take().unwrap(); + + // Write to stdin in a new thread, so that we can read from stdout on this + // thread. This keeps the child from blocking on writing to its stdout which + // might block us from writing to its stdin. + let stdin_handle = ::std::thread::spawn(move || { + child_stdin.write_all(source.as_bytes()) + }); + + // Read stderr on a new thread for similar reasons. + let stderr_handle = ::std::thread::spawn(move || { + let mut output = vec![]; + io::copy(&mut child_stderr, &mut output) + .map(|_| String::from_utf8_lossy(&output).to_string()) + }); + + let mut output = vec![]; + io::copy(&mut child_stdout, &mut output) + .expect("Should copy stdout into vec OK"); + + // Ignore actual rustfmt status because it is often non-zero for trivial + // things. + let _ = child.wait().expect("should wait on rustfmt child OK"); + + stdin_handle.join(); + let bindings = String::from_utf8(output).unwrap(); + let child_stderr = stderr_handle.join(); + + Ok(bindings) + } } /// Determines whether the given cursor is in any of the files matched by the