9 个版本

0.4.0-alpha.02021 年 5 月 9 日
0.3.3 2020 年 9 月 24 日
0.3.2 2020 年 7 月 8 日
0.3.1 2020 年 2 月 11 日
0.2.1 2019 年 6 月 1 日

#184过程宏

Download history 63/week @ 2024-01-01 252/week @ 2024-01-08 136/week @ 2024-01-15 181/week @ 2024-01-22 193/week @ 2024-01-29 136/week @ 2024-02-05 138/week @ 2024-02-12 150/week @ 2024-02-19 140/week @ 2024-02-26 131/week @ 2024-03-04 128/week @ 2024-03-11 173/week @ 2024-03-18 194/week @ 2024-03-25 205/week @ 2024-04-01 134/week @ 2024-04-08 134/week @ 2024-04-15

672 每月下载量
用于 32 个 crates (10 个直接使用)

Apache-2.0 OR MIT

61KB
1.5K SLoC

Rust-Property

License GitHub Actions Crate Badge Crate Doc MSRV 1.31.0

自动生成结构体的一些常用方法。

用法

将 derive proc-macro #[derive(Property)] 应用到结构体上,并使用 #[property(..)] 来配置它。

属性有三个级别

  • 设置 crate 属性可以更改整个 crate 中所有容器(结构体)的默认设置。

    受过程宏限制,这里我们不得不使用一种巧妙的方法来设置 crate 属性

    #[property_default(get(..), set(..), ..)]
    struct PropertyCrateConf; // This struct is only used for introducing the attribute macro, and it will be removed in the macro.
    
  • 设置容器属性可以更改容器中所有字段的默认属性。

  • 通过设置字段属性来更改单个字段的设置。

如果没有设置属性,则将应用默认属性

#[property(
    get(crate, prefix = "", suffix = "", type="auto"),
    set(crate, prefix = "set_", type = "ref"),
    mut(crate, prefix = "mut_"),
    clr(crate, prefix = "clear_", scope = "option")
    ord(asc)
)]

有六种可配置的属性:skipgetsetmutclrord

  • 如果设置了 skip 属性,则不会生成任何方法。

  • 方法的可视性可以通过以下方式设置:#[property(get(visibility-type))]

    存在四种可见性类型:disablepubliccrate(所有方法的默认值)和private

  • 方法名可以通过两种方式设置

    1. 通过以下方式分配完整的名称:#[property(get(name = "method-name"))]

    2. 通过以下方式设置prefix和/或suffix#[property(set(prefix = "set_"), mut(suffix = "mut_"))]

    所有字段的默认设置如下:#[property(get(prefix = "", suffix = ""), set(prefix = "set_"), mut(prefix = "mut_"))]

  • get 方法的返回类型可以通过以下方式设置:#[property(get(type = "return-type"))]

    返回类型有四种:auto(默认),refclonecopy

  • set 方法的输入类型和返回类型可以通过以下方式设置:#[property(set(type = "set-type"))]

    输入类型有四种:ref(默认),ownnonereplace

    • ref:输入是一个可变引用,返回值也是可变引用。

    • own:输入是一个拥有对象,返回值也是拥有对象。

    • none:输入是一个可变引用,没有返回值。

    • replace:输入是一个可变引用,返回旧值。

  • set 方法还有一个额外的属性

    • full_option:如果值是 Option<T>,则默认参数是 T,无需此属性。
  • clr 方法将字段设置为其默认值。它有一个 scope 属性

    • auto:将为某些预设类型生成 clr 方法,例如 VecOption 等。

    • option:(默认)只为 Option 生成 clr 方法。

    • all:将为所有类型生成 clr 方法。

  • 如果有一个字段具有多个 ord 属性,将自动实现 PartialEqPartialOrd

    • ord 字段属性需要一个序列号,它是一个带 _ 前缀的无符号数。

      序列号可以是断续的,但任意两个数字不能相等。

      如果 ord 属性是容器属性,则不允许序列号。

    • 有两种排序类型:ascdesc

      默认为升序(asc),如果设置了desc,则可以更改为降序。

操作示例

原始代码

#![no_std]

#[cfg(not(feature = "std"))]
extern crate alloc;

#[cfg(feature = "std")]
extern crate std as alloc;

use alloc::{string::String, vec::Vec};

use property::{property_default, Property};

#[property_default(get(public), ord(desc), clr(scope = "option"))]
struct PropertyCrateConf;

#[derive(Copy, Clone)]
pub enum Species {
    Dog,
    Cat,
    Bird,
    Other,
}

#[derive(Property)]
#[property(set(private), mut(disable))]
pub struct Pet {
    #[property(get(name = "identification"), set(disable), ord(asc, _2))]
    id: [u8; 32],
    name: String,
    #[property(set(crate, type = "own"), ord(_0))]
    age: u32,
    #[property(get(type = "copy"))]
    species: Species,
    #[property(get(prefix = "is_"), ord(_1))]
    died: bool,
    #[property(get(type = "clone"), set(type = "none"))]
    owner: String,
    #[property(clr(scope = "auto"))]
    family_members: Vec<String>,
    #[property(get(type = "ref"), mut(crate))]
    info: String,
    #[property(get(disable), set(type = "replace"))]
    pub tag: Vec<String>,
    #[property(mut(public, suffix = "_mut"))]
    note: Option<String>,
    #[property(set(type = "replace", full_option))]
    price: Option<u32>,
    #[property(skip)]
    pub reserved: String,
}

生成代码

impl Pet {
    #[inline]
    pub fn identification(&self) -> &[u8] {
        &self.id[..]
    }
    #[inline]
    pub fn name(&self) -> &str {
        &self.name[..]
    }
    #[inline]
    fn set_name<T: Into<String>>(&mut self, val: T) -> &mut Self {
        self.name = val.into();
        self
    }
    #[inline]
    pub fn age(&self) -> u32 {
        self.age
    }
    #[inline]
    pub(crate) fn set_age<T: Into<u32>>(mut self, val: T) -> Self {
        self.age = val.into();
        self
    }
    #[inline]
    pub fn species(&self) -> Species {
        self.species
    }
    #[inline]
    fn set_species<T: Into<Species>>(&mut self, val: T) -> &mut Self {
        self.species = val.into();
        self
    }
    #[inline]
    pub fn is_died(&self) -> bool {
        self.died
    }
    #[inline]
    fn set_died<T: Into<bool>>(&mut self, val: T) -> &mut Self {
        self.died = val.into();
        self
    }
    #[inline]
    pub fn owner(&self) -> String {
        self.owner.clone()
    }
    #[inline]
    fn set_owner<T: Into<String>>(&mut self, val: T) {
        self.owner = val.into();
    }
    #[inline]
    pub fn family_members(&self) -> &[String] {
        &self.family_members[..]
    }
    #[inline]
    fn set_family_members<T: Into<String>>(
        &mut self,
        val: impl IntoIterator<Item = T>,
    ) -> &mut Self {
        self.family_members = val.into_iter().map(Into::into).collect();
        self
    }
    #[inline]
    pub(crate) fn clear_family_members(&mut self) {
        self.family_members.clear();
    }
    #[inline]
    pub fn info(&self) -> &String {
        &self.info
    }
    #[inline]
    fn set_info<T: Into<String>>(&mut self, val: T) -> &mut Self {
        self.info = val.into();
        self
    }
    #[inline]
    pub(crate) fn mut_info(&mut self) -> &mut String {
        &mut self.info
    }
    #[inline]
    fn set_tag<T: Into<String>>(&mut self, val: impl IntoIterator<Item = T>) -> Vec<String> {
        ::core::mem::replace(&mut self.tag, val.into_iter().map(Into::into).collect())
    }
    #[inline]
    pub fn note(&self) -> Option<&String> {
        self.note.as_ref()
    }
    #[inline]
    fn set_note<T: Into<String>>(&mut self, val: T) -> &mut Self {
        self.note = Some(val.into());
        self
    }
    #[inline]
    pub fn note_mut(&mut self) -> &mut Option<String> {
        &mut self.note
    }
    #[inline]
    pub(crate) fn clear_note(&mut self) {
        self.note = None;
    }
    #[inline]
    pub fn price(&self) -> Option<u32> {
        self.price
    }
    #[inline]
    fn set_price<T: Into<Option<u32>>>(&mut self, val: T) -> Option<u32> {
        ::core::mem::replace(&mut self.price, val.into())
    }
    #[inline]
    pub(crate) fn clear_price(&mut self) {
        self.price = None;
    }
}
impl PartialEq for Pet {
    fn eq(&self, other: &Self) -> bool {
        self.age == other.age && self.died == other.died && self.id == other.id
    }
}
impl PartialOrd for Pet {
    fn partial_cmp(&self, other: &Self) -> Option<::core::cmp::Ordering> {
        let result = other.age.partial_cmp(&self.age);
        if result != Some(::core::cmp::Ordering::Equal) {
            return result;
        }
        let result = other.died.partial_cmp(&self.died);
        if result != Some(::core::cmp::Ordering::Equal) {
            return result;
        }
        let result = self.id.partial_cmp(&other.id);
        if result != Some(::core::cmp::Ordering::Equal) {
            return result;
        }
        Some(::core::cmp::Ordering::Equal)
    }
}

享受它吧!

支持的最低Rust版本

Rust 1.31.0.

许可协议

根据您的选择,许可协议可以是Apache License, Version 2.0MIT License

依赖项

约1.5MB
约34K SLoC