Skip to content

Commit 63dc876

Browse files
committed
codegen: Introduce a new codegen using prost
This commit refactors the ttrpc-codegen and the compiler, and merges the two crates into a single crate, named "codegen". The codegen uses prost crate, a protobuf compiler for Rust. Signed-off-by: Xuewei Niu <niuxuewei.nxw@antgroup.com>
1 parent f115450 commit 63dc876

File tree

31 files changed

+3638
-0
lines changed

31 files changed

+3638
-0
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,6 @@ Cargo.lock
77
*.o
88
example/protocols/**/*.rs
99
!example/protocols/**/mod.rs
10+
example2/protocols/**/*.rs
11+
!example2/protocols/**/mod.rs
1012
src/ttrpc.rs

codegen/Cargo.toml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
[package]
2+
name = "ttrpc-codegen"
3+
version = "1.0.0"
4+
edition = "2021"
5+
6+
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
7+
8+
[dependencies]
9+
prost = "0.11"
10+
prost-types = "0.11"
11+
prost-build = "0.11"
12+
proc-macro2 = "1.0"
13+
quote = "1.0"
14+
anyhow = "^1.0"
15+
lazy_static = "1.4"
16+
regex = "1.7"

codegen/src/codegen.rs

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
use anyhow::{anyhow, Context, Result};
2+
use prost_build::Config;
3+
use std::{
4+
fs::{self, File},
5+
io::{Read, Write},
6+
path::Path,
7+
};
8+
9+
use crate::svcgen::{AsyncMode, TtrpcServiceGenerator};
10+
11+
pub struct Codegen<'a, P: AsRef<Path>> {
12+
out_dir: &'a P,
13+
protos: &'a [P],
14+
includes: &'a [P],
15+
/// Whether to enable serde
16+
serde: bool,
17+
async_mode: AsyncMode,
18+
}
19+
20+
impl<'a, P> Codegen<'a, P>
21+
where
22+
P: AsRef<Path>,
23+
{
24+
pub fn generate(&self) -> Result<()> {
25+
self.compile_protos().context("Compile protos")?;
26+
self.write_header().context("Write header")?;
27+
28+
Ok(())
29+
}
30+
31+
fn write_header(&self) -> Result<()> {
32+
let dir = fs::read_dir(self.out_dir.as_ref()).context("Read out_dir")?;
33+
34+
for child in dir {
35+
let entry = child.context("Unable to get entry")?;
36+
let path = entry.path();
37+
let ext = match path.extension() {
38+
Some(ext) => match ext.to_str() {
39+
Some(ext) => ext,
40+
None => "",
41+
},
42+
None => "",
43+
};
44+
if entry.file_name() == "mod.rs" || ext != "rs" {
45+
continue;
46+
}
47+
48+
let header = format!(
49+
r#"// This file is generated by ttrpc-codegen {}. Do not edit
50+
// @generated
51+
52+
"#,
53+
env!("CARGO_PKG_VERSION")
54+
);
55+
56+
let mut buf = Vec::<u8>::new();
57+
buf.write(header.as_bytes()).context("Write header")?;
58+
let mut f = File::open(&path).context(format!("Open rust file {:?}", path))?;
59+
f.read_to_end(&mut buf)
60+
.context(format!("Read from rust file {:?}", path))?;
61+
let mut f = File::create(&path).context(format!("Open rust file {:?}", path))?;
62+
f.write_all(buf.as_slice())
63+
.context(format!("Write to rust file {:?}", path))?;
64+
}
65+
66+
Ok(())
67+
}
68+
69+
fn compile_protos(&self) -> Result<()> {
70+
let mut config = Config::new();
71+
config.out_dir(self.out_dir.as_ref());
72+
config.service_generator(Box::new(TtrpcServiceGenerator::new(self.async_mode)));
73+
config.protoc_arg("--experimental_allow_proto3_optional");
74+
config.compile_well_known_types();
75+
config.include_file("_include.rs");
76+
if self.serde {
77+
config.message_attribute(".", "#[derive(::serde::Serialize, ::serde::Deserialize)]");
78+
}
79+
config
80+
.compile_protos(self.protos, self.includes)
81+
.context("Compile protos by prost")?;
82+
Ok(())
83+
}
84+
}
85+
86+
#[derive(Default)]
87+
pub struct CodegenBuilder<'a, P: AsRef<Path>> {
88+
out_dir: Option<&'a P>,
89+
protos: Option<&'a [P]>,
90+
includes: Option<&'a [P]>,
91+
/// Whether to enable serde
92+
serde: Option<bool>,
93+
async_mode: Option<AsyncMode>,
94+
}
95+
96+
impl<'a, P> CodegenBuilder<'a, P>
97+
where
98+
P: AsRef<Path>,
99+
{
100+
pub fn new() -> Self {
101+
Self {
102+
out_dir: None,
103+
protos: None,
104+
includes: None,
105+
serde: None,
106+
async_mode: None,
107+
}
108+
}
109+
110+
pub fn set_out_dir(mut self, out_dir: &'a P) -> Self {
111+
self.out_dir = Some(out_dir);
112+
self
113+
}
114+
115+
pub fn set_protos(mut self, protos: &'a [P]) -> Self {
116+
self.protos = Some(protos);
117+
self
118+
}
119+
120+
pub fn set_includes(mut self, includes: &'a [P]) -> Self {
121+
self.includes = Some(includes);
122+
self
123+
}
124+
125+
pub fn set_serde(mut self, serde: bool) -> Self {
126+
self.serde = Some(serde);
127+
self
128+
}
129+
130+
pub fn set_async_mode(mut self, async_mode: AsyncMode) -> Self {
131+
self.async_mode = Some(async_mode);
132+
self
133+
}
134+
135+
pub fn build(&self) -> Result<Codegen<'a, P>> {
136+
let out_dir = match self.out_dir {
137+
Some(out_dir) => out_dir,
138+
None => return Err(anyhow!("The out dir is required.")),
139+
};
140+
141+
let protos = match self.protos {
142+
Some(protos) => protos,
143+
None => return Err(anyhow!("The protos are required.")),
144+
};
145+
146+
let includes = match self.includes {
147+
Some(includes) => includes,
148+
None => return Err(anyhow!("The includes are required.")),
149+
};
150+
151+
let serde = match self.serde {
152+
Some(serde) => serde,
153+
None => false,
154+
};
155+
156+
let async_mode = match self.async_mode {
157+
Some(mode) => mode,
158+
None => AsyncMode::None,
159+
};
160+
161+
Ok(Codegen {
162+
out_dir,
163+
protos,
164+
includes,
165+
serde,
166+
async_mode,
167+
})
168+
}
169+
}

codegen/src/lib.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
mod codegen;
2+
mod svcgen;
3+
mod util;
4+
5+
pub use codegen::{Codegen, CodegenBuilder};
6+
pub use svcgen::AsyncMode;

0 commit comments

Comments
 (0)