Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add WATT support to cqrs-codegen (#3) #5

Merged
merged 4 commits into from
Nov 7, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
members = [
"cqrs",
"cqrs-codegen",
"cqrs-codegen/impl",
"cqrs-core",
"cqrs-postgres",
"cqrs-proptest",
Expand Down
1 change: 1 addition & 0 deletions cqrs-codegen/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/.watch-cqrs-codegen-impl
13 changes: 8 additions & 5 deletions cqrs-codegen/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name = "cqrs-codegen"
version = "0.1.0-dev"
edition = "2018"
# authors =
description = "Code generation for CQRS/ES"
description = "Code generation interface for CQRS/ES"
# license = "Apache-2.0"
readme = "../README.md"
# documentation = "https://docs.rs/cqrs-core"
Expand All @@ -12,11 +12,14 @@ readme = "../README.md"
[lib]
proc-macro = true

[features]
default = ["watt"]
no-watt = ["cqrs-codegen-impl", "syn"]

[dependencies]
proc-macro2 = "1.0.6"
quote = "1.0.2"
syn = "1.0.6"
synstructure = "0.12.1"
cqrs-codegen-impl = { version = "0.1.0-dev", path = "./impl", optional = true }
syn = { version = "1.0.7", optional = true }
watt = { version = "0.3.0", optional = true }

[dev-dependencies]
cqrs = { version = "0.3", path = "../cqrs" }
147 changes: 147 additions & 0 deletions cqrs-codegen/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
use std::{
env,
error::Error,
fs::{self, OpenOptions},
io::Write as _,
path::Path,
process,
};

fn main() -> Result<(), Box<dyn Error>> {
let watt = env::var("CARGO_FEATURE_WATT");
let no_watt = env::var("CARGO_FEATURE_NO_WATT");

if watt.is_ok() && no_watt.is_ok() {
panic!(
"Both 'watt' and 'no-watt' features specified; \
exactly one of the two features have to be specified",
);
} else if watt.is_err() && no_watt.is_err() {
panic!(
"Neither 'watt', nor 'no-watt' feature specified; \
exactly one of the two features have to be specified",
);
}

if no_watt.is_ok() {
return Ok(());
}

let codegen_wasm_exists = Path::new("./src/codegen.wasm").is_file();
let impl_exists = Path::new("./impl").is_dir();

if !codegen_wasm_exists && !impl_exists {
panic!(
"Neither './src/codegen.wasm' file, nor './impl/' directory exist, \
so it's impossible to build cqrs-codegen with 'watt' feature",
);
}

let watch = Path::new("./.watch-cqrs-codegen-impl").exists();

if watch {
if impl_exists {
// Have to explicitly exclude 'codegen.wasm', cause otherwise each
// rebuild triggers 'rerun-if-changed' and forces rebuild on next
// run (which triggers 'rerun-if-changed'...).
rerun_if_changed_recursive_with_exceptions("./src", &["codegen.wasm"])?;
rerun_if_changed_recursive("./impl/src")?;
} else {
println!(
"cargo:warning='./.watch-cqrs-codegen-impl' file exists, \
but './impl/' directory doesn't; \
'./src/codegen.wasm' won't be rebuilt",
);
}
}

if (!codegen_wasm_exists || watch) && impl_exists {
let root = env::current_dir()?;

env::set_current_dir("./impl")?;

fs::copy("./Cargo.toml", "./Cargo.toml.orig")?;

writeln!(
OpenOptions::new().append(true).open("./Cargo.toml")?,
"[workspace]",
)?;

let status = process::Command::new(env::var("CARGO")?)
.args(&[
"build",
"--release",
"--target",
"wasm32-unknown-unknown",
"--features",
"watt",
"--target-dir",
"target",
])
.status()?;

if !status.success() {
panic!(
"cargo-build for cqrs-codegen-impl returned \
non-zero status code",
);
}

fs::copy("./Cargo.toml.orig", "./Cargo.toml")?;

// Removing ./Cargo.toml.orig is not critical,
// so result is explicitly ignored.
drop(fs::remove_file("./Cargo.toml.orig"));

env::set_current_dir(root)?;

fs::copy(
"./impl/target/wasm32-unknown-unknown/release/cqrs_codegen_impl.wasm",
"./src/codegen.wasm",
)?;
}

Ok(())
}

fn rerun_if_changed(path: &str) {
println!("cargo:rerun-if-changed={}", path);
}

fn rerun_if_changed_recursive<P>(path: P) -> Result<(), Box<dyn Error>>
where
P: AsRef<Path>,
{
rerun_if_changed_recursive_with_exceptions(path, &[])
}

fn rerun_if_changed_recursive_with_exceptions<P>(
path: P,
exceptions: &[&str],
) -> Result<(), Box<dyn Error>>
where
P: AsRef<Path>,
{
for path in fs::read_dir(path)? {
let path = path?;

if exceptions
.iter()
.any(|&exception| path.file_name() == exception)
{
continue;
}

let path_type = path.file_type()?;

let path = path.path();

if path_type.is_dir() {
rerun_if_changed_recursive(path)?;
} else if path_type.is_file() {
rerun_if_changed(path.to_str().ok_or("Failed to convert PathBuf to &str")?);
}
}

Ok(())
}
2 changes: 2 additions & 0 deletions cqrs-codegen/impl/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/Cargo.lock
ffuugoo marked this conversation as resolved.
Show resolved Hide resolved
/target/
26 changes: 26 additions & 0 deletions cqrs-codegen/impl/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
[package]
name = "cqrs-codegen-impl"
version = "0.1.0-dev"
edition = "2018"
# authors =
description = "Code generation implementation for CQRS/ES"
# license = "Apache-2.0"
readme = "../README.md"
# documentation = "https://docs.rs/cqrs-core"
# repository = "/~https://github.com/cq-rs/cqrs"

[lib]
crate-type = ["rlib", "cdylib"]

[features]
default = []
watt = []

[dependencies]
proc-macro2 = "1.0.6"
quote = "1.0.2"
syn = "1.0.6"
synstructure = { git = "/~https://github.com/ffuugoo/synstructure", default-features = false }

[patch.crates-io]
proc-macro2 = { git = "/~https://github.com/dtolnay/watt" }
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ use synstructure::Structure;

use crate::util::{self, TryInto as _};

pub(crate) use event::derive;
pub(crate) use registered_event::derive as registered_derive;
pub(crate) use versioned_event::derive as versioned_derive;
pub use event::derive as event_derive;
pub use registered_event::derive as registered_event_derive;
pub use versioned_event::derive as versioned_event_derive;

/// Name of the attribute, used for this family of derives.
const ATTR_NAME: &str = "event";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ fn derive_struct(input: syn::DeriveInput) -> Result<proc_macro2::TokenStream> {
/// Implements [`crate::derive_registered_event`] macro expansion for enums
/// via [`synstructure`].
fn derive_enum(input: syn::DeriveInput) -> Result<proc_macro2::TokenStream> {
util::assert_attr_does_not_exist(&input.attrs, super::ATTR_NAME)?;

let mut structure = Structure::try_new(&input)?;

super::render_enum_proxy_method_calls(
Expand Down
38 changes: 38 additions & 0 deletions cqrs-codegen/impl/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
mod event;
mod util;

use proc_macro2::TokenStream;

#[cfg(not(feature = "watt"))]
/// Re-exports proc macro `$fn` as is.
macro_rules! export {
($fn:ident) => {
pub use event::$fn;
};
}

#[cfg(feature = "watt")]
/// Re-exports proc macro `$fn` via WASM ABI.
macro_rules! export {
($fn:ident) => {
#[no_mangle]
pub extern "C" fn $fn(input: TokenStream) -> TokenStream {
expand(syn::parse2(input), event::$fn)
}
};
}

/// Performs expansion of a given proc macro implementation.
pub fn expand<TS: From<TokenStream>>(
input: syn::Result<syn::DeriveInput>,
macro_impl: fn(syn::DeriveInput) -> syn::Result<TokenStream>,
) -> TS {
match input.and_then(|input| macro_impl(input)) {
Ok(res) => res.into(),
Err(err) => err.to_compile_error().into(),
}
}

export!(event_derive);
export!(registered_event_derive);
export!(versioned_event_derive);
File renamed without changes.
Binary file added cqrs-codegen/src/codegen.wasm
Binary file not shown.
51 changes: 31 additions & 20 deletions cqrs-codegen/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,31 @@
extern crate proc_macro;

mod event;
mod util;

use proc_macro::TokenStream;
use syn::parse::Result;

#[cfg(all(not(feature = "watt"), feature = "no-watt"))]
/// Imports proc macro from implementation crate as is.
macro_rules! import {
($input:expr, $fn:ident) => {
cqrs_codegen_impl::expand(syn::parse($input), cqrs_codegen_impl::$fn)
};
}

#[cfg(all(feature = "watt", not(feature = "no-watt")))]
/// Imports proc macro from implementation crate via WASM ABI.
macro_rules! import {
($input:expr, $fn:ident) => {
wasm::MACRO.proc_macro(stringify!($fn), $input)
};
}

#[cfg(all(feature = "watt", not(feature = "no-watt")))]
mod wasm {
/// Generated WASM of implementation crate.
static WASM: &[u8] = include_bytes!("codegen.wasm");

/// Callable interface of the generated [`WASM`].
pub static MACRO: watt::WasmMacro = watt::WasmMacro::new(WASM);
}

/// Derives [`cqrs::Event`] implementation for structs and enums.
///
Expand Down Expand Up @@ -48,8 +69,8 @@ use syn::parse::Result;
/// }
/// ```
#[proc_macro_derive(Event, attributes(event))]
pub fn derive_event(input: TokenStream) -> TokenStream {
expand(input, event::derive)
pub fn event_derive(input: TokenStream) -> TokenStream {
import!(input, event_derive)
}

/// Derives [`cqrs::RegisteredEvent`] implementation for structs and enums.
Expand Down Expand Up @@ -92,8 +113,8 @@ pub fn derive_event(input: TokenStream) -> TokenStream {
/// }
/// ```
#[proc_macro_derive(RegisteredEvent)]
pub fn derive_registered_event(input: TokenStream) -> TokenStream {
expand(input, event::registered_derive)
pub fn registered_event_derive(input: TokenStream) -> TokenStream {
import!(input, registered_event_derive)
}

/// Derives [`cqrs::VersionedEvent`] implementation for structs and enums.
Expand Down Expand Up @@ -138,16 +159,6 @@ pub fn derive_registered_event(input: TokenStream) -> TokenStream {
/// }
/// ```
#[proc_macro_derive(VersionedEvent, attributes(event))]
pub fn derive_versioned_event(input: TokenStream) -> TokenStream {
expand(input, event::versioned_derive)
}

type MacroImpl = fn(syn::DeriveInput) -> Result<proc_macro2::TokenStream>;

/// Expands given input [`TokenStream`] with a given macro implementation.
fn expand(input: TokenStream, macro_impl: MacroImpl) -> TokenStream {
match syn::parse(input).and_then(|i| macro_impl(i)) {
Ok(res) => res.into(),
Err(err) => err.to_compile_error().into(),
}
pub fn versioned_event_derive(input: TokenStream) -> TokenStream {
import!(input, versioned_event_derive)
}