diff --git a/.travis/test_nightly.sh b/.travis/test_nightly.sh index f2b024c..7ed93dd 100755 --- a/.travis/test_nightly.sh +++ b/.travis/test_nightly.sh @@ -5,6 +5,7 @@ set -ex cargo bench --verbose cargo test --verbose --manifest-path=macros/Cargo.toml +cargo test --verbose --manifest-path=derive/Cargo.toml # Build test for the serde feature cargo build --verbose --features "serde" diff --git a/derive/Cargo.toml b/derive/Cargo.toml new file mode 100644 index 0000000..fbb5154 --- /dev/null +++ b/derive/Cargo.toml @@ -0,0 +1,27 @@ +[package] +authors = ["The Rust Project Developers"] +description = "Numeric syntax extensions" +documentation = "http://rust-num.github.io/num" +homepage = "https://github.com/rust-num/num" +keywords = ["mathematics", "numerics"] +license = "MIT/Apache-2.0" +name = "num-derive" +repository = "https://github.com/rust-num/num" +version = "0.1.33" + +[dependencies] +quote = "0.1.3" +syn = "0.7.0" + +[dev-dependencies] +compiletest_rs = "0.2.2" + +[dev-dependencies.num] +path = ".." +version = "0.1" + +[lib] +crate-type = ["rustc-macro"] +name = "num_derive" +rustc-macro = true +test = false diff --git a/derive/src/lib.rs b/derive/src/lib.rs new file mode 100644 index 0000000..3283bb0 --- /dev/null +++ b/derive/src/lib.rs @@ -0,0 +1,76 @@ +// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![crate_type = "rustc-macro"] +#![feature(rustc_macro, rustc_macro_lib)] + +extern crate syn; +#[macro_use] +extern crate quote; +extern crate rustc_macro; + +use rustc_macro::TokenStream; + +use syn::Body::Enum; +use syn::VariantData::Unit; + +#[rustc_macro_derive(FromPrimitive)] +pub fn from_primitive(input: TokenStream) -> TokenStream { + let source = input.to_string(); + + let ast = syn::parse_macro_input(&source).unwrap(); + let name = &ast.ident; + + let variants = match ast.body { + Enum(ref variants) => variants, + _ => { + panic!("`FromPrimitive` can be applied only to the enums, {} is not an enum", + name) + } + }; + + let mut idx = 0; + let variants: Vec<_> = variants.iter() + .map(|variant| { + let ident = &variant.ident; + match variant.data { + Unit => (), + _ => { + panic!("`FromPrimitive` can be applied only to unitary enums, {}::{} is either struct or tuple", name, ident) + }, + } + if let Some(val) = variant.discriminant { + idx = val.value; + } + let tt = quote!(#idx => Some(#name::#ident)); + idx += 1; + tt + }) + .collect(); + + let res = quote! { + #ast + + impl ::num::traits::FromPrimitive for #name { + fn from_i64(n: i64) -> Option { + Self::from_u64(n as u64) + } + + fn from_u64(n: u64) -> Option { + match n { + #(variants,)* + _ => None, + } + } + } + }; + + res.to_string().parse().unwrap() +} diff --git a/derive/tests/compile-fail/derive_on_struct.rs b/derive/tests/compile-fail/derive_on_struct.rs new file mode 100644 index 0000000..6ce4cd1 --- /dev/null +++ b/derive/tests/compile-fail/derive_on_struct.rs @@ -0,0 +1,25 @@ + +// Copyright 2013-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(rustc_macro)] + +extern crate num; +#[macro_use] +extern crate num_derive; + +#[derive(Debug, PartialEq, FromPrimitive)] //~ ERROR +struct Color { + r: u8, + g: u8, + b: u8, +} + +fn main() {} diff --git a/derive/tests/compile-fail/enum_with_associated_data.rs b/derive/tests/compile-fail/enum_with_associated_data.rs new file mode 100644 index 0000000..312f0f7 --- /dev/null +++ b/derive/tests/compile-fail/enum_with_associated_data.rs @@ -0,0 +1,24 @@ + +// Copyright 2013-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(rustc_macro)] + +extern crate num; +#[macro_use] +extern crate num_derive; + +#[derive(Debug, PartialEq, FromPrimitive)] //~ ERROR +enum Color { + Rgb(u8, u8, u8), + Hsv(u8, u8, u8), +} + +fn main() {} diff --git a/derive/tests/compiletest.rs b/derive/tests/compiletest.rs new file mode 100644 index 0000000..27c212b --- /dev/null +++ b/derive/tests/compiletest.rs @@ -0,0 +1,25 @@ +extern crate compiletest_rs as compiletest; + +use std::path::PathBuf; +use std::env::var; + +fn run_mode(mode: &'static str) { + let mut config = compiletest::default_config(); + + let cfg_mode = mode.parse().ok().expect("Invalid mode"); + + config.target_rustcflags = Some("-L target/debug/ -L target/debug/deps/".to_owned()); + if let Ok(name) = var::<&str>("TESTNAME") { + let s : String = name.to_owned(); + config.filter = Some(s) + } + config.mode = cfg_mode; + config.src_base = PathBuf::from(format!("tests/{}", mode)); + + compiletest::run_tests(&config); +} + +#[test] +fn compile_test() { + run_mode("compile-fail"); +} diff --git a/derive/tests/empty_enum.rs b/derive/tests/empty_enum.rs new file mode 100644 index 0000000..6b1c22b --- /dev/null +++ b/derive/tests/empty_enum.rs @@ -0,0 +1,26 @@ + +// Copyright 2013-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(rustc_macro)] + +extern crate num; +#[macro_use] +extern crate num_derive; + +#[derive(Debug, PartialEq, FromPrimitive)] +enum Color {} + +#[test] +fn test_empty_enum() { + let v: [Option; 1] = [num::FromPrimitive::from_u64(0)]; + + assert_eq!(v, [None]); +} diff --git a/derive/tests/trivial.rs b/derive/tests/trivial.rs new file mode 100644 index 0000000..298ca6d --- /dev/null +++ b/derive/tests/trivial.rs @@ -0,0 +1,33 @@ +// Copyright 2013-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(rustc_macro)] + +extern crate num; +#[macro_use] +extern crate num_derive; + +#[derive(Debug, PartialEq, FromPrimitive)] +enum Color { + Red, + Blue, + Green, +} + +#[test] +fn test_from_primitive_for_trivial_case() { + let v: [Option; 4] = [num::FromPrimitive::from_u64(0), + num::FromPrimitive::from_u64(1), + num::FromPrimitive::from_u64(2), + num::FromPrimitive::from_u64(3)]; + + assert_eq!(v, + [Some(Color::Red), Some(Color::Blue), Some(Color::Green), None]); +} diff --git a/derive/tests/with_custom_values.rs b/derive/tests/with_custom_values.rs new file mode 100644 index 0000000..4ecf672 --- /dev/null +++ b/derive/tests/with_custom_values.rs @@ -0,0 +1,33 @@ +// Copyright 2013-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(rustc_macro)] + +extern crate num; +#[macro_use] +extern crate num_derive; + +#[derive(Debug, PartialEq, FromPrimitive)] +enum Color { + Red, + Blue = 5, + Green, +} + +#[test] +fn test_from_primitive_for_enum_with_custom_value() { + let v: [Option; 4] = [num::FromPrimitive::from_u64(0), + num::FromPrimitive::from_u64(5), + num::FromPrimitive::from_u64(6), + num::FromPrimitive::from_u64(3)]; + + assert_eq!(v, + [Some(Color::Red), Some(Color::Blue), Some(Color::Green), None]); +}