use std::{fmt, io, path::Path, path::PathBuf};
pub use ast::Spec;
pub mod ast;
pub mod backend;
pub mod parser;
use thiserror::Error;
#[derive(Error, Debug)]
pub enum LibError {
#[error("backend '{backend}' does not support artifact '{artifact}'")]
UnsupportedArtifact {
backend: &'static str,
artifact: Artifact,
},
#[error("backend '{backend}' requires output to be a folder")]
OutputMustBeFolder { backend: &'static str },
#[error("backend '{backend}' expects output folder to be empty")]
OutputFolderNotEmpty { backend: &'static str },
#[error(transparent)]
IoError(#[from] io::Error),
#[error(transparent)]
ParseError(#[from] pest::error::Error<parser::Rule>),
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum Artifact {
TypesOnly,
ClientEndpoints,
ServerEndpoints,
}
impl Default for Artifact {
fn default() -> Self {
Artifact::TypesOnly
}
}
impl fmt::Display for Artifact {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let printable = match *self {
Artifact::TypesOnly => "TypesOnly",
Artifact::ClientEndpoints => "ClientEndpoints",
Artifact::ServerEndpoints => "ServerEndpoints",
};
write!(f, "{}", printable)
}
}
pub trait CodeGenerator {
fn generate(&self, spec: &Spec, output: &Path) -> Result<(), LibError>;
}
pub fn parse<I: io::Read>(mut src: I) -> Result<ast::Spec, LibError> {
let mut input = String::new();
src.read_to_string(&mut input).map_err(LibError::IoError)?;
Ok(parser::parse(&input).map_err(LibError::ParseError)?)
}
pub fn build<P: AsRef<Path>>(src: P) -> Result<(), LibError> {
println!("cargo:rerun-if-changed={}", src.as_ref().display());
let out_dir: PathBuf = std::env::var("OUT_DIR")
.expect("read OUT_DIR envvar")
.into();
let out_path = out_dir.join("protocol.rs");
let infile = std::fs::File::open(src)?;
let spec = parse(infile)?;
let generator = backend::rust::Generator::new(Artifact::ServerEndpoints)?;
generator.generate(&spec, &out_path)?;
Ok(())
}