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

officially support #![no_std] #41 #45

Merged
merged 3 commits into from
Mar 25, 2017
Merged
Show file tree
Hide file tree
Changes from 2 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 derive_builder/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
### Fixed
- use full path for result #39
- support `#[deny(missing_docs)]` #37
- support `#![no_std]` via `#[builder(no_std)]` #41

## [0.3.0] - 2017-02-05

Expand Down
1 change: 1 addition & 0 deletions derive_builder/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ with [cargo-edit](https://github.com/killercup/cargo-edit):
* **Setter visibility**: You can opt into private setter by preceding your struct with `#[builder(private)]`.
* **Setter type conversions**: With ``#[builder(setter(into))]`, setter methods will be generic over the input types – you can then supply every argument that implements the [`Into`][into] trait for the field type.
* **Generic structs**: Are also supported, but you **must not** use a type parameter named `VALUE`, if you also activate setter type conversions.
* **no_std support**: Just add `#[builder(no_std)]` to your struct and add `extern crate collections` to your crate. The latter requires the _nightly_ toolchain.
* **Logging**: If anything works unexpectedly you can enable detailed logs in two steps. First, add `features = ["logging"]` to the `derive_builder` dependency in `Cargo.toml`. Second, set this environment variable before calling cargo `RUST_LOG=derive_builder=trace`.

For more information and examples please take a look at our [documentation][doc].
Expand Down
7 changes: 7 additions & 0 deletions derive_builder/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,13 @@
//! # fn main() {}
//! ```
//!
//! # **`#![no_std]`** Support (on Nightly)
//!
//! You can activate support for `#![no_std]` by adding `#[builder(no_std)]` to your struct
//! and `#![feature(collections)] extern crate collections` to your crate.
//!
//! The latter requires the _nightly_ toolchain.
//!
//! # Troubleshooting
//!
//! ## Gotchas
Expand Down
5 changes: 5 additions & 0 deletions derive_builder/src/options/field_mode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,11 @@ impl OptionsBuilderMode for FieldMode {
fn where_diagnostics(&self) -> String {
format!("on field `{}`", self.field_ident.as_ref())
}

fn no_std(&mut self, _x: bool) {
panic!("Support for `#![no_std]` can only be set on the stuct level (but found {}).",
self.where_diagnostics())
}
}

impl From<OptionsBuilder<FieldMode>> for FieldOptions {
Expand Down
48 changes: 48 additions & 0 deletions derive_builder/src/options/macros.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/// Helper macro to generate a setter for some `Option<T>`.
///
/// The setter will panic if the Option is already initialized.
///
/// # Examples
///
/// ```rust, ignore
/// OptionsBuilder {
/// foo: Option<u32>,
/// }
///
/// impl OptionsBuilder {
/// impl_setter!{
/// ident: foo,
/// desc: "foo",
/// map: |x: u32| { x },
/// }
/// }
/// ```
macro_rules! impl_setter {
(
ident: $ident:ident,
desc: $desc:expr,
map: |$x:ident: $ty:ty| {$( $map:tt )*},
) => {
impl_setter!{
ident: $ident for $ident,
desc: $desc,
map: |$x: $ty| {$( $map )*},
}
};
(
ident: $setter:ident for $field:ident,
desc: $desc:expr,
map: |$x:ident: $ty:ty| {$( $map:tt )*},
) => {
fn $setter(&mut self, $x: $ty) {
if let Some(ref current) = self.$field {
panic!("Failed to set {} to `{:?}` (already defined as `{:?}`) {}.",
$desc,
$x,
current,
self.where_diagnostics());
}
self.$field = Some({$( $map )*});
}
}
}
39 changes: 7 additions & 32 deletions derive_builder/src/options/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use syn;
use derive_builder_core::BuilderPattern;

#[macro_use]
mod macros;
mod field_mode;
mod field_options;
mod struct_mode;
Expand Down Expand Up @@ -46,6 +48,7 @@ pub trait OptionsBuilderMode: ::std::fmt::Debug {
fn push_deprecation_note<T: Into<String>>(&mut self, x: T) -> &mut Self;
/// Provide a diagnostic _where_-clause for panics.
fn where_diagnostics(&self) -> String;
fn no_std(&mut self, x: bool);
}

impl<Mode> From<Mode> for OptionsBuilder<Mode> {
Expand All @@ -63,37 +66,6 @@ impl<Mode> From<Mode> for OptionsBuilder<Mode> {
}
}

macro_rules! impl_setter {
(
ident: $ident:ident,
desc: $desc:expr,
map: |$x:ident: $ty:ty| {$( $map:tt )*},
) => {
impl_setter!{
ident: $ident for $ident,
desc: $desc,
map: |$x: $ty| {$( $map )*},
}
};
(
ident: $setter:ident for $field:ident,
desc: $desc:expr,
map: |$x:ident: $ty:ty| {$( $map:tt )*},
) => {
fn $setter(&mut self, $x: $ty) -> &mut Self {
if let Some(ref current) = self.$field {
panic!("Failed to set {} to `{:?}` (already defined as `{:?}`) {}.",
$desc,
$x,
current,
self.where_diagnostics());
}
self.$field = Some({$( $map )*});
self
}
}
}

impl<Mode> OptionsBuilder<Mode> where
Mode: OptionsBuilderMode
{
Expand Down Expand Up @@ -215,10 +187,13 @@ impl<Mode> OptionsBuilder<Mode> where
"default" => {
self.default_expression(DefaultExpression::Trait)
},
"no_std" => {
self.mode.no_std(true)
},
_ => {
panic!("Unknown option `{}` {}", ident.as_ref(), self.where_diagnostics())
}
};
}
}

/// e.g `setter_prefix="with"` in `#[builder(setter_prefix="with")]`
Expand Down
19 changes: 18 additions & 1 deletion derive_builder/src/options/struct_mode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ pub struct StructMode {
builder_vis: Option<syn::Visibility>,
deprecation_notes: DeprecationNotes,
struct_size_hint: usize,
no_std: Option<bool>,
}

impl OptionsBuilder<StructMode> {
Expand All @@ -26,6 +27,7 @@ impl OptionsBuilder<StructMode> {
builder_vis: None,
deprecation_notes: Default::default(),
struct_size_hint: 0,
no_std: None,
});

builder.parse_attributes(&ast.attrs);
Expand All @@ -34,11 +36,19 @@ impl OptionsBuilder<StructMode> {
}
}

impl StructMode {
impl_setter!{
ident: builder_name,
desc: "builder name",
map: |x: String| { x },
}
}

impl OptionsBuilderMode for StructMode {
fn parse_builder_name(&mut self, name: &syn::Lit) {
trace!("Parsing builder name `{:?}`", name);
let value = parse_lit_as_string(name).unwrap();
self.builder_name = Some(value.clone());
self.builder_name(value.clone());
}

fn push_deprecation_note<T: Into<String>>(&mut self, x: T) -> &mut Self {
Expand All @@ -50,6 +60,12 @@ impl OptionsBuilderMode for StructMode {
fn where_diagnostics(&self) -> String {
format!("on struct `{}`", self.build_target_name)
}

impl_setter!{
ident: no_std,
desc: "no_std support",
map: |x: bool| { x },
}
}

impl From<OptionsBuilder<StructMode>> for (StructOptions, OptionsBuilder<FieldMode>) {
Expand Down Expand Up @@ -77,6 +93,7 @@ impl From<OptionsBuilder<StructMode>> for (StructOptions, OptionsBuilder<FieldMo
deprecation_notes: m.deprecation_notes,
generics: m.build_target_generics,
struct_size_hint: m.struct_size_hint,
no_std: m.no_std.unwrap_or(false),
};

(struct_options, field_defaults)
Expand Down
3 changes: 3 additions & 0 deletions derive_builder/src/options/struct_options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ pub struct StructOptions {
pub deprecation_notes: DeprecationNotes,
/// Number of fields on the target struct.
pub struct_size_hint: usize,
/// Whether the generated code should comply with `#![no_std]`.
pub no_std: bool,
}

impl StructOptions {
Expand Down Expand Up @@ -49,6 +51,7 @@ impl StructOptions {
target_ty_generics: Some(ty_generics),
initializers: Vec::with_capacity(self.struct_size_hint),
doc_comment: None,
no_std: self.no_std,
}
}
}
30 changes: 28 additions & 2 deletions derive_builder_core/src/build_method.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ pub struct BuildMethod<'a> {
pub initializers: Vec<Tokens>,
/// Doc-comment of the builder struct.
pub doc_comment: Option<syn::Attribute>,
/// Whether the generated code should comply with `#![no_std]`.
pub no_std: bool,
}

impl<'a> ToTokens for BuildMethod<'a> {
Expand All @@ -63,12 +65,21 @@ impl<'a> ToTokens for BuildMethod<'a> {
BuilderPattern::Immutable => quote!(&self),
};
let doc_comment = &self.doc_comment;

let (result, string) = if self.no_std {(
quote!(::core::result::Result),
quote!(::collections::string::String),
)} else {(
quote!(::std::result::Result),
quote!(::std::string::String),
)};

if self.enabled {
trace!("Deriving build method `{}`.", self.ident.as_ref());
tokens.append(quote!(
#doc_comment
#vis fn #ident(#self_param)
-> ::std::result::Result<#target_ty #target_ty_generics, ::std::string::String>
-> #result<#target_ty #target_ty_generics, #string>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the reason extern crate collections is required, right? Can we work around that? E.g., not give string error messages when using no_std but more simple ones?

Copy link
Owner Author

@colin-kiegel colin-kiegel Mar 25, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, I was thinking about that too.

I would say, that we could implement that as a separate feature. In fact this could just mean to provide more structured errors. I just opened issue #60 for that. How does that sound? :-)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

{
Ok(#target_ty {
#(#initializers)*
Expand Down Expand Up @@ -112,6 +123,7 @@ macro_rules! default_build_method {
target_ty_generics: None,
initializers: vec![quote!(foo: self.foo,)],
doc_comment: None,
no_std: false,
}
}
}
Expand All @@ -122,7 +134,7 @@ mod tests {
use super::*;

#[test]
fn test() {
fn std() {
let build_method = default_build_method!();

assert_eq!(quote!(#build_method), quote!(
Expand All @@ -133,4 +145,18 @@ mod tests {
}
));
}

#[test]
fn no_std() {
let mut build_method = default_build_method!();
build_method.no_std = true;

assert_eq!(quote!(#build_method), quote!(
pub fn build(&self) -> ::core::result::Result<Foo, ::collections::string::String> {
Ok(Foo {
foo: self.foo,
})
}
));
}
}
12 changes: 12 additions & 0 deletions derive_builder_test/tests/run-pass/no_std.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// requires nightly toolchain!
#![no_std]
#![feature(collections)]
#![allow(dead_code)]

#[macro_use]
extern crate derive_builder;
extern crate collections;

#[derive(Builder)]
#[builder(no_std)]
struct IgnoreEmptyStruct {}