Skip to content

Commit

Permalink
added codegen
Browse files Browse the repository at this point in the history
  • Loading branch information
d3rpp committed Nov 5, 2024
1 parent 730fd6c commit 21c0ac9
Show file tree
Hide file tree
Showing 10 changed files with 227 additions and 2 deletions.
4 changes: 4 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
license = "MIT"
readme = "./README.md"
repository = "https://github.com/d3rpp/chur"
version = "0.3.0"
version = "0.3.1-dev.1"
publish = true

[workspace.dependencies]
Expand Down
14 changes: 14 additions & 0 deletions crates/chur-build/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,15 @@
repository.workspace = true
version.workspace = true

[features]
default = ["codegen"]
codegen = [
"dep:proc-macro2",
"dep:syn",
"dep:quote",
"dep:prettyplease"
]

[dependencies]
lazy_static.workspace = true
thiserror.workspace = true
Expand All @@ -23,3 +32,8 @@
sha.workspace = true

archiver-rs.workspace = true

proc-macro2 = { version = "^1.0", optional = true }
syn = { version = "^2.0", optional = true }
quote = { version = "^1.0", optional = true }
prettyplease = { version = "^0.2", optional = true }
3 changes: 3 additions & 0 deletions crates/chur-build/src/cfg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ pub struct Config {

pub(crate) dependencies: Vec<Dependency>,
pub(crate) file_descriptors: bool,

#[cfg(feature = "codegen")]
pub(crate) codegen: Option<PathBuf>,
}

impl Config {
Expand Down
24 changes: 24 additions & 0 deletions crates/chur-build/src/cfg/builder.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
use std::fmt::Display;

#[cfg(feature = "codegen")]
use std::path::PathBuf;

use crate::{defined_constants::ROOT_MANIFEST_DIR, dependency::Dependency};

use super::Config;
Expand All @@ -13,6 +16,9 @@ pub struct ConfigBuilder {

protos: Vec<String>,
file_descriptors: bool,

#[cfg(feature = "codegen")]
codegen: Option<String>,
}

impl ConfigBuilder {
Expand Down Expand Up @@ -51,11 +57,26 @@ impl ConfigBuilder {
self
}

/// Generate file descriptors
pub fn file_descriptors(mut self, file_descriptors: bool) -> Self {
self.file_descriptors = file_descriptors;
self
}

#[cfg(feature = "codegen")]
/// Instead of just making the manifest, forcing to use [`chur::include_tree`][include_tree]
/// just dump the code into a file at the provided path.
///
/// This works better with rust-analyzer.
///
/// Provided path should be relative to the workspace `Cargo.toml`.
///
/// [include_tree]: https://docs.rs/chur/latest/chur/macro.include_tree.html
pub fn codegen(mut self, codegen_path: impl ToString) -> Self {
self.codegen = Some(codegen_path.to_string());
self
}

/// Build the [ConfigBuilder] into a [Config]
///
/// This changes the directories used to be absolute.
Expand All @@ -73,6 +94,9 @@ impl ConfigBuilder {
protos,
dependencies: self.dependencies,
file_descriptors: self.file_descriptors,

#[cfg(feature = "codegen")]
codegen: self.codegen.map(PathBuf::from),
})
}
}
16 changes: 16 additions & 0 deletions crates/chur-build/src/execute.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
#[cfg(feature = "codegen")]
use std::io::Write;

use crate::{
defined_constants::{
DEPENDENCY_INCLUDE_DIR, GENERATED_DESCRIPTORS_FILE, GENERATED_SOURCES_DIR,
Expand Down Expand Up @@ -39,5 +42,18 @@ pub fn execute(cfg: Config) -> ChurResult<()> {

builder.compile(&cfg.protos, &include_dirs)?;


#[cfg(feature = "codegen")]
if let Some(codegen_output) = cfg.codegen {
let absolute_output = crate::defined_constants::ROOT_MANIFEST_DIR.join(codegen_output);
let mut output_file = std::fs::OpenOptions::new().truncate(true).write(true).open(absolute_output)?;

let parsed = syn::parse2(crate::include_tree::include_tree()).unwrap();

let output_string = prettyplease::unparse(&parsed);

output_file.write_all(output_string.as_bytes())?;
}

Ok(())
}
142 changes: 142 additions & 0 deletions crates/chur-build/src/include_tree.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
use std::{
env, path::{Path, PathBuf}
};

use proc_macro2::{Span, TokenStream};
use syn::{Ident, LitStr};

use quote::{quote, quote_spanned};

#[derive(Debug, Default)]
struct Mod(String, Vec<TreeItem>);

#[derive(Debug)]
struct File(String);

#[derive(Debug)]
enum TreeItem {
Mod(Mod),
File(File),
}

fn mod_to_token_stream(mod_item: Mod, root_dir: &Path) -> TokenStream {
let mod_children = mod_item.1.into_iter().map(|item| match item {
TreeItem::Mod(mod_item) => mod_to_token_stream(mod_item, root_dir),
TreeItem::File(file_item) => file_to_token_stream(file_item, root_dir),
});

let mod_name = syn::parse_str::<Ident>(mod_item.0.as_str()).expect("invalid mod ident");

quote! {
pub mod #mod_name {
#(#mod_children)*
}
}
}

fn file_to_token_stream(file_item: File, root_dir: &Path) -> TokenStream {
let path = root_dir
.join(format!("{}.rs", file_item.0))
.display()
.to_string();
let path_tok = syn::Lit::Str(LitStr::new(&path, Span::call_site()));

quote!(include!(#path_tok);)
}

fn insert_into_mod<'a>(
mod_item: &mut Mod,
mut path_chunk_iter: impl Iterator<Item = &'a str>,
original_path_name: String,
) {
if let Some(mod_name) = path_chunk_iter.next() {
let child_mod = mod_item.1.iter_mut().find_map(|m| {
if let TreeItem::Mod(existant_mod) = m {
if existant_mod.0 == mod_name {
Some(existant_mod)
} else {
None
}
} else {
None
}
});

if let Some(child) = child_mod {
insert_into_mod(child, path_chunk_iter, original_path_name);
} else {
let mut new_mod = Mod(mod_name.to_string(), vec![]);

insert_into_mod(&mut new_mod, path_chunk_iter, original_path_name);
mod_item.1.push(TreeItem::Mod(new_mod))
}
} else {
mod_item.1.push(TreeItem::File(File(original_path_name)))
}
}

pub(super) fn include_tree() -> TokenStream {
let out_dir = env::var("OUT_DIR").unwrap();

// path to file descriptor if present
let mut fd: Option<PathBuf> = None;

let out_dir_path = Path::new(out_dir.as_str());
let dir_contents = out_dir_path.read_dir().unwrap().filter_map(|entry| {
if let Ok(entry) = entry {
if entry.path().is_file() {
if let Some(file_name) = entry.path().file_name() {
let fn_string = file_name.to_string_lossy();
if fn_string.starts_with("__fd") {
fd = Some(entry.path())
} else if let Some(ext) = entry.path().extension() {
if ext == "rs" {
return Some(
entry
.file_name()
.to_string_lossy()
.to_string()
.trim_end_matches(".rs")
.to_string(),
);
}
} else {
return None;
}
} else {
return None;
}
}
}

None
});

let mut root_tree = Mod::default();

dir_contents.for_each(|item| {
insert_into_mod(&mut root_tree, item.split('.'), item.clone());
});

let tree_items = root_tree.1.into_iter().map(|item| match item {
TreeItem::Mod(mod_item) => mod_to_token_stream(mod_item, out_dir_path),
TreeItem::File(file_item) => file_to_token_stream(file_item, out_dir_path),
});

let call_site = Span::call_site();

let fd_token_stream = if let Some(fd_path) = fd {
let file_path = fd_path.to_str().unwrap();
Some(
quote_spanned!(call_site=> pub const FILE_DESCRIPTOR_BYTES: &'static [u8] = include_bytes!(#file_path);),
)
} else {
None
};

quote! {
#(#tree_items)*

#fd_token_stream
}
}
3 changes: 3 additions & 0 deletions crates/chur-build/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ mod defined_constants;
mod execute;
mod manifest;

#[cfg(feature = "codegen")]
mod include_tree;

pub mod dependency;
pub mod error;

Expand Down
1 change: 1 addition & 0 deletions example/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ fn main() -> Result<(), ChurError> {
])
.dependency(Dependency::github("googleapis/api-common-protos", None))
.file_descriptors(true)
.codegen("example/src/pb.rs")
.build()
.unwrap();

Expand Down
20 changes: 19 additions & 1 deletion example/src/pb.rs
Original file line number Diff line number Diff line change
@@ -1 +1,19 @@
chur::include_tree!();
pub mod google {
pub mod r#type {
include!(
"/home/d3rpp/code/chur/target/debug/build/chur-example-a6531aff33ada8de/out/google.r#type.rs"
);
}
}
pub mod example {
pub mod hello_world {
pub mod v1 {
include!(
"/home/d3rpp/code/chur/target/debug/build/chur-example-a6531aff33ada8de/out/example.hello_world.v1.rs"
);
}
}
}
pub const FILE_DESCRIPTOR_BYTES: &'static [u8] = include_bytes!(
"/home/d3rpp/code/chur/target/debug/build/chur-example-a6531aff33ada8de/out/__fd.bin"
);

0 comments on commit 21c0ac9

Please sign in to comment.