1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
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(())
}