Skip to content

Commit

Permalink
Add lightning-macros crate
Browse files Browse the repository at this point in the history
Previously, we used the `bdk_macros` dependency for some simple proc
macros in `lightning-transaction-sync`. However, post-1.0 BDK doesn't
further maintain this crate and will at some point probably yank it
together with the old `bdk` crate that was split up.

Here, we create a new crate for utility proc macros and ~~steal~~ add
what we currently use (slightly modified for the latest `syn` version's
API though). In the future we may want to expand this crate, e.g., for
some `maybe_async` macros in the context of an `async KVStore`
implementation.
  • Loading branch information
tnull committed Sep 21, 2024
1 parent 66fb520 commit 3bc10e9
Show file tree
Hide file tree
Showing 4 changed files with 157 additions and 0 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ members = [
"lightning-rapid-gossip-sync",
"lightning-custom-message",
"lightning-transaction-sync",
"lightning-macros",
"possiblyrandom",
]

Expand Down
1 change: 1 addition & 0 deletions ci/ci-tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ WORKSPACE_MEMBERS=(
lightning-rapid-gossip-sync
lightning-custom-message
lightning-transaction-sync
lightning-macros
possiblyrandom
)

Expand Down
26 changes: 26 additions & 0 deletions lightning-macros/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
[package]
name = "lightning-macros"
version = "0.1.0"
authors = ["Elias Rohrer"]
license = "MIT OR Apache-2.0"
repository = "https://github.com/lightningdevkit/rust-lightning/"
description = """
Proc macros used by LDK
"""
edition = "2021"

[package.metadata.docs.rs]
rustdoc-args = ["--cfg", "docsrs"]

[lib]
proc-macro = true

[features]

[dependencies]
syn = { version = "2.0.77", default-features = false, features = ["parsing", "printing", "proc-macro", "full"] }
proc-macro2 = { version = "1.0.86", default-features = false, features = ["proc-macro"] }
quote = { version = "1.0", default-features = false, features = ["proc-macro"] }

[lints]
workspace = true
129 changes: 129 additions & 0 deletions lightning-macros/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
// This file is Copyright its original authors, visible in version control
// history.
//
// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
// You may not use this file except in accordance with one or both of these
// licenses.

#![crate_name = "lightning_macros"]

//! Proc macros used by LDK

#![cfg_attr(not(test), no_std)]
#![deny(missing_docs)]
#![forbid(unsafe_code)]
#![deny(rustdoc::broken_intra_doc_links)]
#![deny(rustdoc::private_intra_doc_links)]
#![cfg_attr(docsrs, feature(doc_auto_cfg))]

use proc_macro::TokenStream;
use quote::quote;
use syn::spanned::Spanned;
use syn::{parse, ImplItemFn, ItemImpl, ItemTrait, Token};

fn add_async_trait(mut parsed: ItemTrait) -> TokenStream {
let output = quote! {
#[cfg(not(feature = "async-interface"))]
#parsed
};

for mut item in &mut parsed.items {
if let syn::TraitItem::Fn(f) = &mut item {
f.sig.asyncness = Some(Token![async](f.span()));
}
}

let output = quote! {
#output

#[cfg(feature = "async-interface")]
#[async_trait(?Send)]
#parsed
};

output.into()
}

fn add_async_method(mut parsed: ImplItemFn) -> TokenStream {
let output = quote! {
#[cfg(not(feature = "async-interface"))]
#parsed
};

parsed.sig.asyncness = Some(Token![async](parsed.span()));

let output = quote! {
#output

#[cfg(feature = "async-interface")]
#parsed
};

output.into()
}

fn add_async_impl_trait(mut parsed: ItemImpl) -> TokenStream {
let output = quote! {
#[cfg(not(feature = "async-interface"))]
#parsed
};

for mut item in &mut parsed.items {
if let syn::ImplItem::Fn(f) = &mut item {
f.sig.asyncness = Some(Token![async](f.span()));
}
}

let output = quote! {
#output

#[cfg(feature = "async-interface")]
#[async_trait(?Send)]
#parsed
};

output.into()
}

/// Makes a method or every method of a trait `async`, if the `async-interface` feature is enabled.
///
/// Requires the `async-trait` crate as a dependency whenever this attribute is used on a trait
/// definition or trait implementation.
#[proc_macro_attribute]
pub fn maybe_async(_attr: TokenStream, item: TokenStream) -> TokenStream {
if let Ok(parsed) = parse(item.clone()) {
add_async_trait(parsed)
} else if let Ok(parsed) = parse(item.clone()) {
add_async_method(parsed)
} else if let Ok(parsed) = parse(item) {
add_async_impl_trait(parsed)
} else {
(quote! {
compile_error!("#[maybe_async] can only be used on methods, trait or trait impl blocks")
})
.into()
}
}

/// Awaits, if the `async-interface` feature is enabled.
#[proc_macro]
pub fn maybe_await(expr: TokenStream) -> TokenStream {
let expr: proc_macro2::TokenStream = expr.into();
let quoted = quote! {
{
#[cfg(not(feature = "async-interface"))]
{
#expr
}

#[cfg(feature = "async-interface")]
{
#expr.await
}
}
};

quoted.into()
}

0 comments on commit 3bc10e9

Please sign in to comment.