Skip to content

Commit

Permalink
Merge branch 'master' into dynamic-load_data
Browse files Browse the repository at this point in the history
  • Loading branch information
philss authored Apr 17, 2023
2 parents 51bf30e + f1aaa3b commit f40cfec
Show file tree
Hide file tree
Showing 27 changed files with 325 additions and 208 deletions.
120 changes: 35 additions & 85 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,46 +10,24 @@ on:
- cron: '0 0 * * 3'

jobs:
clippy:
name: Clippy ${{matrix.os}}
runs-on: ${{matrix.os}}-latest
strategy:
matrix:
os: [ubuntu, windows, macos]
steps:
- uses: actions/checkout@v1
- uses: actions-rs/toolchain@v1
with:
toolchain: stable
components: clippy
override: true
- uses: actions-rs/clippy-check@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
args: --all-targets --all-features -- -D warnings

format:
name: Format
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions-rs/toolchain@v1
- uses: actions/checkout@v3

- uses: dtolnay/rust-toolchain@stable
with:
toolchain: stable
components: rustfmt
override: true
components: rustfmt

- name: Install Erlang/Elixir
uses: erlef/setup-beam@v1
with:
otp-version: "25"
elixir-version: "1.13"
elixir-version: "1.14"

- name: Check cargo fmt
uses: actions-rs/cargo@v1
with:
command: fmt
args: --all -- --check
run: cargo fmt --all -- --check

- name: Check mix format (rustler_mix)
working-directory: rustler_mix
Expand All @@ -59,96 +37,63 @@ jobs:
working-directory: rustler_tests
run: mix format --check-formatted

rustler_mix_test:
runs-on: ubuntu-latest
clippy:
name: Clippy ${{matrix.os}}
runs-on: ${{matrix.os}}-latest
strategy:
matrix:
os: [ubuntu, windows, macos]
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v3

- name: Install Rust stable toolchain
uses: actions-rs/toolchain@v1
- uses: dtolnay/rust-toolchain@stable
with:
toolchain: stable
override: true
components: clippy

- name: Install Erlang/Elixir
uses: erlef/setup-beam@v1
with:
otp-version: "24"
elixir-version: 1.13

- name: Test rustler_mix
working-directory: rustler_mix
run: ./test.sh
- run: cargo clippy --all-targets --all-features -- -D warnings

test:
name: OTP ${{matrix.pair.erlang}} / Elixir ${{matrix.pair.elixir}} / Rust ${{matrix.rust}} / OS ${{matrix.os}}
runs-on: ${{matrix.os}}-latest
needs: [format, clippy]
strategy:
matrix:
pair:
- { erlang: "25", elixir: "1.14" }
- { erlang: "25", elixir: "1.13" }
- { erlang: "25", elixir: "1.14", latest: true }
- { erlang: "24", elixir: "1.13" }
- { erlang: "24", elixir: "1.12" }
- { erlang: "24", elixir: "1.11" }
- { erlang: "23", elixir: "1.11" }
- { erlang: "23", elixir: "1.12" }
rust:
- stable
- beta
- nightly
os:
- windows
# TODO change this to ubuntu after OTP 23 is deprecated, see
- macos-11
- windows-latest
# TODO change this to ubuntu-latest after OTP 23 is deprecated, see
# https://github.com/erlef/setup-beam#compatibility-between-operating-system-and-erlangotp
# for compatibility
- ubuntu-20.04
runs-on: ${{matrix.os}}

steps:
- name: Checkout sources
uses: actions/checkout@v1
uses: actions/checkout@v3

- name: Install Erlang/Elixir
uses: erlef/setup-beam@v1
with:
otp-version: ${{matrix.pair.erlang}}
elixir-version: ${{matrix.pair.elixir}}
if: "!startsWith(matrix.os, 'macos')"

- name: Install Rust ${{matrix.rust}} toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: ${{matrix.rust}}
override: true

- run: cargo test

- name: Test rustler_mix
working-directory: rustler_mix
run: |
mix deps.get
mix test
- name: Test rustler_tests
working-directory: rustler_tests
run: |
mix deps.get
mix test
test_macos:
name: macos-11 test
runs-on: macos-11
steps:
- name: Checkout sources
uses: actions/checkout@v1

- name: Install Erlang/Elixir
- name: Install Erlang/Elixir with Brew
run: |
brew install elixir
mix local.hex --force
if: "startsWith(matrix.os, 'macos')"

- name: Install Rust ${{matrix.rust}} toolchain
uses: actions-rs/toolchain@v1
uses: dtolnay/rust-toolchain@master
with:
toolchain: stable
override: true
toolchain: ${{matrix.rust}}

- run: cargo test

Expand All @@ -163,3 +108,8 @@ jobs:
run: |
mix deps.get
mix test
- name: Test mix project with example created from template
working-directory: rustler_mix
run: ./test.sh
if: "startsWith(matrix.os, 'ubuntu') && matrix.pair.latest"
2 changes: 1 addition & 1 deletion rustler/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use std::env;
use std::process::Command;

// keep this sorted by version number
const NIF_VERSION: &[&str] = &["2.14", "2.15", "2.16"];
const NIF_VERSION: &[&str] = &["2.14", "2.15", "2.16", "2.17"];

fn main() {
let latest_version = NIF_VERSION.last().unwrap().to_string();
Expand Down
7 changes: 5 additions & 2 deletions rustler_codegen/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,17 @@ version = "0.27.0" # rustler_codegen version
authors = ["Hansihe <hansihe@hansihe.com>"]
license = "MIT/Apache-2.0"
readme = "../README.md"
edition = "2018"
edition = "2021"

[lib]
name = "rustler_codegen"
proc_macro = true

[dependencies]
syn = { version = "1.0.5", features = ["full", "extra-traits"] }
syn = { version = "2.0", features = ["full", "extra-traits"] }
quote = "1.0"
heck = "0.4"
proc-macro2 = "1.0"

[dev-dependencies]
trybuild = "1.0"
89 changes: 42 additions & 47 deletions rustler_codegen/src/context.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
// TODO When we settle for a minimum version of Rust >= 1.42, remove this.
#![allow(clippy::match_like_matches_macro)]

use heck::ToSnakeCase;
use proc_macro2::{Span, TokenStream};
use quote::quote;
use syn::{Data, Field, Fields, Ident, Lit, Meta, NestedMeta, Variant};
use syn::{Data, Field, Fields, Ident, Lit, Meta, Variant};

use super::RustlerAttr;

Expand Down Expand Up @@ -49,10 +46,7 @@ impl<'a> Context<'a> {
};

let is_tuple_struct = match ast.data {
Data::Struct(ref data_struct) => match data_struct.fields {
Fields::Unnamed(_) => true,
_ => false,
},
Data::Struct(ref data_struct) => matches!(data_struct.fields, Fields::Unnamed(_)),
_ => false,
};

Expand All @@ -71,17 +65,15 @@ impl<'a> Context<'a> {
}

pub fn encode(&self) -> bool {
self.attrs.iter().any(|attr| match attr {
RustlerAttr::Encode => true,
_ => false,
})
self.attrs
.iter()
.any(|attr| matches!(attr, RustlerAttr::Encode))
}

pub fn decode(&self) -> bool {
self.attrs.iter().any(|attr| match attr {
RustlerAttr::Decode => true,
_ => false,
})
self.attrs
.iter()
.any(|attr| matches!(attr, RustlerAttr::Decode))
}

pub fn field_atoms(&self) -> Option<Vec<TokenStream>> {
Expand Down Expand Up @@ -142,23 +134,21 @@ impl<'a> Context<'a> {
}

fn encode_decode_attr_set(attrs: &[RustlerAttr]) -> bool {
attrs.iter().any(|attr| match attr {
RustlerAttr::Encode => true,
RustlerAttr::Decode => true,
_ => false,
})
attrs
.iter()
.any(|attr| matches!(attr, RustlerAttr::Encode | RustlerAttr::Decode))
}

fn get_rustler_attrs(attr: &syn::Attribute) -> Vec<RustlerAttr> {
attr.path
attr.path()
.segments
.iter()
.filter_map(|segment| {
let meta = attr.parse_meta().expect("can parse meta");
let meta = &attr.meta;
match segment.ident.to_string().as_ref() {
"rustler" => Some(Context::parse_rustler(&meta)),
"tag" => Context::try_parse_tag(&meta),
"module" => Context::try_parse_module(&meta),
"rustler" => Some(Context::parse_rustler(meta)),
"tag" => Context::try_parse_tag(meta),
"module" => Context::try_parse_module(meta),
_ => None,
}
})
Expand All @@ -168,42 +158,47 @@ impl<'a> Context<'a> {

fn parse_rustler(meta: &Meta) -> Vec<RustlerAttr> {
if let Meta::List(ref list) = meta {
return list
.nested
.iter()
.map(Context::parse_nested_rustler)
.collect();
}

panic!("Expected encode and/or decode in rustler attribute");
}
let mut attrs: Vec<RustlerAttr> = vec![];
let _ = list.parse_nested_meta(|nested_meta| {
if nested_meta.path.is_ident("encode") {
attrs.push(RustlerAttr::Encode);
Ok(())
} else if nested_meta.path.is_ident("decode") {
attrs.push(RustlerAttr::Decode);
Ok(())
} else {
Err(nested_meta.error("Expected encode and/or decode in rustler attribute"))
}
});

fn parse_nested_rustler(nested: &NestedMeta) -> RustlerAttr {
if let NestedMeta::Meta(Meta::Path(ref path)) = nested {
match path.segments[0].ident.to_string().as_ref() {
"encode" => return RustlerAttr::Encode,
"decode" => return RustlerAttr::Decode,
other => panic!("Unexpected literal {}", other),
}
return attrs;
}

panic!("Expected encode and/or decode in rustler attribute");
}

fn try_parse_tag(meta: &Meta) -> Option<Vec<RustlerAttr>> {
if let Meta::NameValue(ref name_value) = meta {
if let Lit::Str(ref tag) = name_value.lit {
return Some(vec![RustlerAttr::Tag(tag.value())]);
let expr = &name_value.value;

if let syn::Expr::Lit(lit_expr) = expr {
if let Lit::Str(ref tag) = lit_expr.lit {
return Some(vec![RustlerAttr::Tag(tag.value())]);
}
}
}
panic!("Cannot parse module")
}

fn try_parse_module(meta: &Meta) -> Option<Vec<RustlerAttr>> {
if let Meta::NameValue(name_value) = meta {
if let Lit::Str(ref module) = name_value.lit {
let ident = format!("Elixir.{}", module.value());
return Some(vec![RustlerAttr::Module(ident)]);
let expr = &name_value.value;

if let syn::Expr::Lit(lit_expr) = expr {
if let Lit::Str(ref module) = lit_expr.lit {
let ident = format!("Elixir.{}", module.value());
return Some(vec![RustlerAttr::Module(ident)]);
}
}
}
panic!("Cannot parse tag")
Expand Down
2 changes: 1 addition & 1 deletion rustler_codegen/src/encode_decode_templates.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ pub(crate) fn decoder(ctx: &Context, inner: TokenStream) -> TokenStream {

let mut impl_generics = generics.clone();
let decode_lifetime = syn::Lifetime::new("'__rustler_decode_lifetime", Span::call_site());
let lifetime_def = syn::LifetimeDef::new(decode_lifetime.clone());
let lifetime_def = syn::LifetimeParam::new(decode_lifetime.clone());
impl_generics
.params
.push(syn::GenericParam::Lifetime(lifetime_def));
Expand Down
11 changes: 9 additions & 2 deletions rustler_codegen/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,10 +91,17 @@ pub fn init(input: TokenStream) -> TokenStream {
/// ```
#[proc_macro_attribute]
pub fn nif(args: TokenStream, input: TokenStream) -> TokenStream {
let args = syn::parse_macro_input!(args as syn::AttributeArgs);
let mut nif_attributes = nif::NifAttributes::default();

if !args.is_empty() {
let nif_macro_parser = syn::meta::parser(|meta| nif_attributes.parse(meta));

syn::parse_macro_input!(args with nif_macro_parser);
}

let input = syn::parse_macro_input!(input as syn::ItemFn);

nif::transcoder_decorator(args, input).into()
nif::transcoder_decorator(nif_attributes, input).into()
}

/// Derives implementations for the `Encoder` and `Decoder` traits
Expand Down
Loading

0 comments on commit f40cfec

Please sign in to comment.