smart-default-0.7.1/.cargo_vcs_info.json 0000644 00000000136 00000000001 0013611 0 ustar {
"git": {
"sha1": "e03d1dee473603df260f474460448b2e1e96f327"
},
"path_in_vcs": ""
} smart-default-0.7.1/.github/workflows/ci.yml 0000644 0000000 0000000 00000006561 10461020230 0017124 0 ustar 0000000 0000000 name: CI
on:
pull_request:
push:
branches: [master]
# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
permissions:
contents: read
pages: write
id-token: write
checks: write
jobs:
ci:
name: CI
needs: [test, clippy, docs]
runs-on: ubuntu-latest
steps:
- name: Done
run: exit 0
test:
name: Tests
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest]
rust: [1.69.0, nightly]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
- name: Install rust
uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ matrix.rust }}
- name: Ready cache
if: matrix.os == 'ubuntu-latest'
run: sudo chown -R $(whoami):$(id -ng) ~/.cargo/
- name: Cache cargo
uses: actions/cache@v1
id: cache
with:
path: ~/.cargo
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
- name: Test smart-default
run: cargo test --all-targets
fmt:
name: Rustfmt
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: dtolnay/rust-toolchain@master
with:
toolchain: nightly
components: rustfmt
- name: Run fmt --all -- --check
run: cargo fmt --all -- --check
clippy:
name: Clippy
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: dtolnay/rust-toolchain@master
with:
toolchain: nightly
components: clippy
- name: Cache cargo
uses: actions/cache@v1
id: cache
with:
path: ~/.cargo
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
- name: Run clippy --all-targets --
uses: actions-rs/clippy-check@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
args: --all-targets --
docs:
name: Docs
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: dtolnay/rust-toolchain@master
with:
toolchain: nightly
- name: Cache cargo
uses: actions/cache@v1
id: cache
with:
path: ~/.cargo
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
- name: Run doc tests
run: cargo test --doc
- name: Check smart-default docs
run: cargo doc --no-deps
docs-ghpages:
name: Update Docs in GitHub Pages
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/master'
steps:
- uses: actions/checkout@v3
- name: Build docs
env:
GITHUB_REPO: ${{ github.repository }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |-
cargo doc --verbose &&
echo "" > target/doc/index.html
- name: Upload artifact
uses: actions/upload-pages-artifact@v1
with:
path: target/doc
deploy-ghpages:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
needs: docs-ghpages
if: github.ref == 'refs/heads/master'
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v1
smart-default-0.7.1/.gitignore 0000644 0000000 0000000 00000000522 10461020230 0014370 0 ustar 0000000 0000000 # Generated by Cargo
# will have compiled files and executables
/target/
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
# More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock
Cargo.lock
# These are backup files generated by rustfmt
**/*.rs.bk
/target/
**/*.rs.bk
Cargo.lock
smart-default-0.7.1/CHANGELOG.md 0000644 0000000 0000000 00000003325 10461020230 0014215 0 ustar 0000000 0000000 # Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
## [Unreleased]
## 0.7.1 - 2023-04-24
### Fixed
- Fixed bug where the macro fails on valid default expression that is also a
valid attribute meta because it was expecting the `_code` hack.
## 0.7.0 - 2023-04-23
### Changed
- Update `syn` to version 2
## 0.6.0 - 2019-12-13
### Changed
- Update `syn`, `quote` and `proc-macro2` versions to `1.*.*`.
## 0.5.2 - 2019-04-13
### Fixed
- Omit linting of generated code by adding `#[automatically_derived]` attribute.
## 0.5.1 - 2019-03-01
### Fixed
- Don't use a multi-pattern `if let`, as this unnecessarily requires version
1.33.0 of Rust.
## 0.5.0 - 2019-03-01
### Changed
- When the default is a string literal, strap an `.into()` after it to
automatically convert it to `String` when needed.
## 0.4.0 - 2019-02-19
### Added
- `#[default(_code = "...")]` syntax for defaults that cannot be parsed as
attributes no matter what.
## 0.3.0 - 2018-11-02
### Changed
- Require Rust 1.30+.
- Use direct attribute value instead of having to wrap them in strings.
- Moved the docs from the module level to the custom derive.
### Added
- `#[default(...)]` syntax in addition to `#[default = ...]`. This is required
to deal with some parsing limitations.
## 0.2.0 - 2017-08-21
### Added
- Support generic types.
- Generate doc for the trait impl that describes the default values.
## 0.1.0 - 2017-08-18
### Added
- Custom derive `SmartDefault` for implementing the `Default` trait.
- `#[default = ...]` attribute on fields for smart-default.
smart-default-0.7.1/Cargo.lock 0000644 00000002134 00000000001 0011564 0 ustar # This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "proc-macro2"
version = "1.0.56"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc"
dependencies = [
"proc-macro2",
]
[[package]]
name = "smart-default"
version = "0.7.1"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "syn"
version = "2.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "unicode-ident"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc"
smart-default-0.7.1/Cargo.toml 0000644 00000001734 00000000001 0011614 0 ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
#
# When uploading crates to the registry Cargo will automatically
# "normalize" Cargo.toml files for maximal compatibility
# with all versions of Cargo and also rewrite `path` dependencies
# to registry (e.g., crates.io) dependencies.
#
# If you are reading this file be aware that the original Cargo.toml
# will likely look very different (and much more reasonable).
# See Cargo.toml.orig for the original contents.
[package]
edition = "2021"
name = "smart-default"
version = "0.7.1"
authors = ["IdanArye "]
description = "Rust custom-derive macro for Default with more control on the fields"
documentation = "https://idanarye.github.io/rust-smart-default/"
readme = "README.md"
keywords = ["default"]
license = "MIT"
repository = "https://github.com/idanarye/rust-smart-default"
[lib]
proc-macro = true
[dependencies.proc-macro2]
version = "1"
[dependencies.quote]
version = "1"
[dependencies.syn]
version = "2"
smart-default-0.7.1/Cargo.toml.orig 0000644 0000000 0000000 00000000721 10461020230 0015270 0 ustar 0000000 0000000 [package]
name = "smart-default"
description = "Rust custom-derive macro for Default with more control on the fields"
version = "0.7.1"
edition = "2021"
authors = ["IdanArye "]
license = "MIT"
repository = "https://github.com/idanarye/rust-smart-default"
documentation = "https://idanarye.github.io/rust-smart-default/"
readme = "README.md"
keywords = ["default"]
[lib]
proc-macro = true
[dependencies]
syn = "2"
quote = "1"
proc-macro2 = "1"
smart-default-0.7.1/LICENSE 0000644 0000000 0000000 00000002052 10461020230 0013405 0 ustar 0000000 0000000 MIT License
Copyright (c) 2017 Idan Arye
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
smart-default-0.7.1/README.md 0000644 0000000 0000000 00000002044 10461020230 0013660 0 ustar 0000000 0000000 [](https://github.com/idanarye/rust-smart-default/actions)
[](https://crates.io/crates/smart-default)
[](https://idanarye.github.io/rust-smart-default/)
# Rust SmartDefault
Custom derive for automatically implementing the `Default` trait with customized default values:
```rust
use smart_default::SmartDefault;
#[derive(SmartDefault)]
enum Foo {
Bar,
#[default]
Baz {
#[default = 12]
a: i32,
b: i32,
#[default(Some(Default::default()))]
c: Option,
#[default(_code = "vec![1, 2, 3]")]
d: Vec,
#[default = "four"]
e: String,
},
Qux(i32),
}
assert!(Foo::default() == Foo::Baz {
a: 12,
b: 0,
c: Some(0),
d: vec![1, 2, 3],
e: "four".to_owned(),
});
```
Requires Rust 1.30+ (for non-string values in attributes)
smart-default-0.7.1/examples/example.rs 0000644 0000000 0000000 00000001203 10461020230 0016214 0 ustar 0000000 0000000 use smart_default::SmartDefault;
#[derive(PartialEq, SmartDefault, Debug)]
#[allow(dead_code)]
enum Foo {
Bar,
#[default]
Baz {
#[default(12)]
a: i32,
b: i32,
#[default(Some(Default::default()))]
c: Option,
#[default(_code = "vec![1, 2, 3]")]
d: Vec,
#[default = "four"]
e: String,
},
Qux(i32),
}
fn main() {
assert!(
Foo::default()
== Foo::Baz {
a: 12,
b: 0,
c: Some(0),
d: vec![1, 2, 3],
e: "four".to_owned(),
}
);
}
smart-default-0.7.1/src/body_impl.rs 0000644 0000000 0000000 00000013111 10461020230 0015511 0 ustar 0000000 0000000 use proc_macro2::TokenStream;
use quote::quote;
use syn::parse::Error;
use syn::spanned::Spanned;
use syn::DeriveInput;
use crate::default_attr::{ConversionStrategy, DefaultAttr};
use crate::util::find_only;
pub fn impl_my_derive(input: &DeriveInput) -> Result {
let name = &input.ident;
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
let (default_expr, doc) = match input.data {
syn::Data::Struct(ref body) => {
let (body_assignment, doc) = default_body_tt(&body.fields)?;
(
quote! {
#name #body_assignment
},
format!("Return `{}{}`", name, doc),
)
}
syn::Data::Enum(ref body) => {
let default_variant = find_only(body.variants.iter(), |variant| {
if let Some(meta) = DefaultAttr::find_in_attributes(&variant.attrs)? {
if meta.code.is_none() {
Ok(true)
} else {
Err(Error::new(
meta.code.span(),
"Attribute #[default] on variants should have no value",
))
}
} else {
Ok(false)
}
})?
.ok_or_else(|| Error::new(input.span(), "No default variant"))?;
let default_variant_name = &default_variant.ident;
let (body_assignment, doc) = default_body_tt(&default_variant.fields)?;
(
quote! {
#name :: #default_variant_name #body_assignment
},
format!("Return `{}::{}{}`", name, default_variant_name, doc),
)
}
syn::Data::Union(_) => {
panic!()
}
};
Ok(quote! {
#[automatically_derived]
impl #impl_generics Default for #name #ty_generics #where_clause {
#[doc = #doc]
fn default() -> Self {
#default_expr
}
}
})
}
/// Return a token-tree for the default "body" - the part after the name that contains the values.
/// That is, the `{ ... }` part for structs, the `(...)` part for tuples, and nothing for units.
fn default_body_tt(body: &syn::Fields) -> Result<(TokenStream, String), Error> {
let mut doc = String::new();
use std::fmt::Write;
let body_tt = match body {
syn::Fields::Named(ref fields) => {
doc.push_str(" {");
let result = {
let field_assignments = fields
.named
.iter()
.map(|field| {
let field_name = field.ident.as_ref();
let (default_value, default_doc) = field_default_expr_and_doc(field)?;
write!(
&mut doc,
"\n {}: {},",
field_name.expect("field value in struct is empty"),
default_doc
)
.unwrap();
// let default_value = default_value.into_token_stream();
Ok(quote! { #field_name : #default_value })
})
.collect::, Error>>()?;
quote! {
{
#( #field_assignments ),*
}
}
};
if doc.ends_with(',') {
doc.pop();
doc.push('\n');
};
doc.push('}');
result
}
syn::Fields::Unnamed(ref fields) => {
doc.push('(');
let result = {
let field_assignments = fields
.unnamed
.iter()
.map(|field| {
let (default_value, default_doc) = field_default_expr_and_doc(field)?;
write!(&mut doc, "{}, ", default_doc).unwrap();
Ok(default_value)
})
.collect::, Error>>()?;
quote! {
(
#( #field_assignments ),*
)
}
};
if doc.ends_with(", ") {
doc.pop();
doc.pop();
};
doc.push(')');
result
}
&syn::Fields::Unit => quote! {},
};
Ok((body_tt, doc))
}
/// Return a default expression for a field based on it's `#[default = "..."]` attribute. Panic
/// if there is more than one, of if there is a `#[default]` attribute without value.
fn field_default_expr_and_doc(field: &syn::Field) -> Result<(TokenStream, String), Error> {
if let Some(default_attr) = DefaultAttr::find_in_attributes(&field.attrs)? {
let conversion_strategy = default_attr.conversion_strategy();
let field_value = default_attr.code.ok_or_else(|| {
Error::new(field.span(), "Expected #[default = ...] or #[default(...)]")
})?;
let field_value = match conversion_strategy {
ConversionStrategy::NoConversion => field_value,
ConversionStrategy::Into => quote!((#field_value).into()),
};
let field_doc = format!("{}", field_value);
Ok((field_value, field_doc))
} else {
Ok((
quote! {
Default::default()
},
"Default::default()".to_owned(),
))
}
}
smart-default-0.7.1/src/default_attr.rs 0000644 0000000 0000000 00000006163 10461020230 0016222 0 ustar 0000000 0000000 use proc_macro2::TokenStream;
use quote::ToTokens;
use syn::{parse::Error, MetaNameValue};
use crate::util::find_only;
#[derive(Debug, Clone, Copy)]
pub enum ConversionStrategy {
NoConversion,
Into,
}
pub struct DefaultAttr {
pub code: Option,
conversion_strategy: Option,
}
impl DefaultAttr {
pub fn find_in_attributes(attrs: &[syn::Attribute]) -> Result