pub mod schema_capnp;
pub mod codegen;
pub mod codegen_types;
mod pointer_constants;
use std::path::{Path, PathBuf};
pub(crate) fn convert_io_err(err: std::io::Error) -> capnp::Error {
use std::io;
let kind = match err.kind() {
io::ErrorKind::TimedOut => capnp::ErrorKind::Overloaded,
io::ErrorKind::BrokenPipe |
io::ErrorKind::ConnectionRefused |
io::ErrorKind::ConnectionReset |
io::ErrorKind::ConnectionAborted |
io::ErrorKind::NotConnected => capnp::ErrorKind::Disconnected,
_ => capnp::ErrorKind::Failed,
};
capnp::Error { description: format!("{}", err), kind: kind }
}
fn run_command(mut command: ::std::process::Command, mut code_generation_command: codegen::CodeGenerationCommand) -> ::capnp::Result<()> {
let mut p = command.spawn().map_err(convert_io_err)?;
code_generation_command.run(p.stdout.take().unwrap())?;
let exit_status = p.wait().map_err(convert_io_err)?;
if !exit_status.success() {
Err(::capnp::Error::failed(format!(
"Non-success exit status: {}",
exit_status
)))
} else {
Ok(())
}
}
pub struct CompilerCommand {
files: Vec<PathBuf>,
src_prefixes: Vec<PathBuf>,
import_paths: Vec<PathBuf>,
no_standard_import: bool,
executable_path: Option<PathBuf>,
output_path: Option<PathBuf>,
default_parent_module: Vec<String>,
raw_code_generator_request_path: Option<PathBuf>,
}
impl CompilerCommand {
pub fn new() -> CompilerCommand {
CompilerCommand {
files: Vec::new(),
src_prefixes: Vec::new(),
import_paths: Vec::new(),
no_standard_import: false,
executable_path: None,
output_path: None,
default_parent_module: Vec::new(),
raw_code_generator_request_path: None,
}
}
pub fn file<P>(&mut self, path: P) -> &mut CompilerCommand
where
P: AsRef<Path>,
{
self.files.push(path.as_ref().to_path_buf());
self
}
pub fn src_prefix<P>(&mut self, prefix: P) -> &mut CompilerCommand
where
P: AsRef<Path>,
{
self.src_prefixes.push(prefix.as_ref().to_path_buf());
self
}
pub fn import_path<P>(&mut self, dir: P) -> &mut CompilerCommand
where
P: AsRef<Path>,
{
self.import_paths.push(dir.as_ref().to_path_buf());
self
}
pub fn no_standard_import(&mut self) -> &mut CompilerCommand {
self.no_standard_import = true;
self
}
pub fn output_path<P>(&mut self, path: P) -> &mut CompilerCommand
where
P: AsRef<Path>,
{
self.output_path = Some(path.as_ref().to_path_buf());
self
}
pub fn capnp_executable<P>(&mut self, path: P) -> &mut CompilerCommand
where
P: AsRef<Path>
{
self.executable_path = Some(path.as_ref().to_path_buf());
self
}
pub fn default_parent_module(&mut self, default_parent_module: Vec<String>) -> &mut CompilerCommand
{
self.default_parent_module = default_parent_module;
self
}
pub fn raw_code_generator_request_path<P>(&mut self, path: P) -> &mut CompilerCommand
where
P: AsRef<Path>,
{
self.raw_code_generator_request_path = Some(path.as_ref().to_path_buf());
self
}
pub fn run(&mut self) -> ::capnp::Result<()> {
let mut command = if let Some(executable) = &self.executable_path {
::std::process::Command::new(executable)
} else {
::std::process::Command::new("capnp")
};
command.arg("compile").arg("-o").arg("-");
if self.no_standard_import {
command.arg("--no-standard-import");
}
for import_path in &self.import_paths {
command.arg(&format!("--import-path={}", import_path.display()));
}
for src_prefix in &self.src_prefixes {
command.arg(&format!("--src-prefix={}", src_prefix.display()));
}
for file in &self.files {
std::fs::metadata(file).map_err(|error| {
let current_dir = match std::env::current_dir() {
Ok(current_dir) => format!("`{}`", current_dir.display()),
Err(..) => "<unknown working directory>".to_string(),
};
::capnp::Error::failed(format!(
"Unable to stat capnp input file `{}` in working directory {}: {}. \
Please check that the file exists and is accessible for read.",
file.display(),
current_dir,
error
))
})?;
command.arg(file);
}
let output_path = if let Some(output_path) = &self.output_path {
output_path.clone()
} else {
PathBuf::from(::std::env::var("OUT_DIR").map_err(|error| {
::capnp::Error::failed(format!(
"Could not access `OUT_DIR` environment variable: {}. \
You might need to set it up or instead create you own output \
structure using `CompilerCommand::output_path`",
error
))
})?)
};
command.stdout(::std::process::Stdio::piped());
command.stderr(::std::process::Stdio::inherit());
let mut code_generation_command = crate::codegen::CodeGenerationCommand::new();
code_generation_command
.output_directory(output_path)
.default_parent_module(self.default_parent_module.clone());
if let Some(raw_code_generator_request_path) = &self.raw_code_generator_request_path {
code_generation_command.raw_code_generator_request_path(raw_code_generator_request_path.clone());
}
run_command(command, code_generation_command).map_err(|error| {
::capnp::Error::failed(format!(
"Error while trying to execute `capnp compile`: {}. \
Please verify that version 0.5.2 or higher of the capnp executable \
is installed on your system. See https://capnproto.org/install.html",
error
))
})
}
}
#[test]
fn compiler_command_new_no_out_dir() {
let error = CompilerCommand::new().run().unwrap_err().description;
assert!(error.starts_with("Could not access `OUT_DIR` environment variable"));
}
#[test]
fn compiler_command_with_output_path_no_out_dir() {
let error = CompilerCommand::new().output_path("foo").run().unwrap_err().description;
assert!(error.starts_with("Error while trying to execute `capnp compile`"));
}