derive-getters-0.4.0/.build.yml000064400000000000000000000010021046102023000144470ustar 00000000000000image: debian/stable packages: - build-essential - pkg-config - curl secrets: - 1aeb435f-7837-4290-9e6e-915401a14d6e sources: - git@git.sr.ht:~kvsari/derive-getters tasks: - setup: | curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs > rustup.sh chmod +x rustup.sh ./rustup.sh -y cat .cargo/env >> .buildenv - build: | cd derive-getters cargo build cargo build --release - test: | cd derive-getters cargo test cargo test --release derive-getters-0.4.0/.cargo_vcs_info.json0000644000000001360000000000100137660ustar { "git": { "sha1": "0cbdab1b58a72c62284c25d2b2d0b31dd6c169f0" }, "path_in_vcs": "" }derive-getters-0.4.0/.gitignore000064400000000000000000000000221046102023000145400ustar 00000000000000target Cargo.lock derive-getters-0.4.0/.travis.yml000064400000000000000000000001431046102023000146650ustar 00000000000000language: rust rust: - stable - beta - nightly matrix: allow_failures: - rust: nightly derive-getters-0.4.0/Cargo.toml0000644000000025160000000000100117700ustar # 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" rust-version = "1.60.0" name = "derive-getters" version = "0.4.0" authors = ["Stephan Luther "] autotests = false description = "Simple boilerplate getters generator." homepage = "https://sr.ht/~kvsari/derive-getters/" readme = "README.md" keywords = [ "getter", "getters", "derive", "macro", ] categories = ["development-tools::procedural-macro-helpers"] license = "MIT" repository = "https://git.sr.ht/~kvsari/derive-getters" [lib] name = "derive_getters" proc-macro = true [[test]] name = "tests" path = "tests/lib.rs" [dependencies.proc-macro2] version = "1.0" [dependencies.quote] version = "1.0" [dependencies.syn] version = "2.0" features = ["extra-traits"] [dev-dependencies.serde] version = "1.0" features = ["derive"] [dev-dependencies.trybuild] version = "1.0" [dev-dependencies.version-sync] version = "0.9" derive-getters-0.4.0/Cargo.toml.orig000064400000000000000000000014161046102023000154470ustar 00000000000000[package] name = "derive-getters" version = "0.4.0" authors = ["Stephan Luther "] license = "MIT" description = "Simple boilerplate getters generator." repository = "https://git.sr.ht/~kvsari/derive-getters" homepage = "https://sr.ht/~kvsari/derive-getters/" keywords = ["getter", "getters", "derive", "macro"] categories = ["development-tools::procedural-macro-helpers"] edition = "2021" rust-version = "1.60.0" autotests = false readme = "README.md" [lib] name = "derive_getters" proc-macro = true [[test]] name = "tests" path = "tests/lib.rs" [dependencies] quote = "1.0" syn = { version = "2.0", features = ["extra-traits"] } proc-macro2 = "1.0" [dev-dependencies] trybuild = "1.0" serde = { version = "1.0", features = ["derive"] } version-sync = "0.9" derive-getters-0.4.0/LICENSE000064400000000000000000000020571046102023000135670ustar 00000000000000MIT License Copyright (c) 2021 Stephan Luther 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. derive-getters-0.4.0/README.md000064400000000000000000000142241046102023000140400ustar 00000000000000# Derive Getters Simple `Getters` derive macro for generating field getter methods on a named struct. Included is an additional derive, `Dissolve`, that consumes the struct returning a tuple of all fields in the order they were declared. Dissolve can also be derived for tuple structs. Any doc comments on the target struct fields are replicated for the corresponding getters. If no comment is present one shall be generated. The need for the `Getters` macro came about when I was making various data structures for JSON to deserialize into. These data structures had many fields in them to access and they weren't going to change once created. One could use `pub` everywhere but that would enable mutating the fields which is what this derive aims to avoid. Getters will be generated according to [convention](https://github.com/rust-lang/rfcs/blob/master/text/0344-conventions-galore.md#gettersetter-apis). This means that the generated methods will reside within the struct namespace. With regards to `Dissolve`, sometimes during conversion a structure must be consumed. One easy way to do this is to return a tuple of all the structs fields. Thus `Dissolve` can be considered a 'get (move) everything' method call. ## What this crate won't do There are no mutable getters and it's not planned. There are no setters either nor will there ever be. ## Rust Docs [Documentation is here.](https://docs.rs/derive-getters/0.4.0) ## Installation Add to your `Cargo.toml`: ```toml [dependencies] derive-getters = "0.4.0" ``` Then import the `Getters` or `Dissolve` macro in whichever module it's needed (assuming 2018 edition). ```rust use derive_getters::{Getters, Dissolve}; ``` Otherwise just import at crate root. ```rust #[macro_use] extern crate derive_getters; ``` ## Usage When you have a struct you want to automatically derive getters for... Just add the derive at the top like so; ```rust #[derive(Getters)] pub struct MyCheesyStruct { x: i64, y: i64, } ``` A new impl will be produced for `MyCheesyStruct`. ```rust /// Auto-generated by `derive_getters::Getters`. impl MyCheesyStruct { /// Get field `x` from instance of `MyCheesyStruct`. pub fn x(&self) -> &i64 { &self.x } /// Get field `y` from instance of `MyCheesyStruct`. pub fn y(&self) -> &i64 { &self.y } } ``` This crate can also handle structs with simple generic parameters and lifetime annotations. Check [docs](https://docs.rs/derive-getters/0.4.0) for further details. ```rust #[derive(Getters)] pub struct StructWithGeneric<'a, T> { concrete: f64, generic: T, text: &'a str, } ``` With `Dissolve`, use it like so; ```rust #[derive(Dissolve)] pub struct Solid { a: u64, b: f64, c: i64, } ``` An impl will be produced for `Solid` like so; ```rust /// Auto-generated by `derive_getters::Dissolve`. impl Solid { /// Dissolve `Solid` into a tuple consisting of its fields in order of declaration. pub fn dissolve(self) -> (u64, f64, i64) { (self.a, self.b, self.c) } } ``` ### Attributes This macro comes with two optional field attributes for `Getters`. * `#[getter(skip)]` to skip generating getters for a field. * `#[getter(rename = "name")]` to change the getter name to "name". And one optional struct attribute for `Dissolve`. * `#[dissolve(rename = "name")]` to change the name of the dissolve function to "name". ## Comment Preservation Doc comments on fields shall be copied over onto their respective getters. If there is no comment for the field then one shall be generated. For example, this struct; ```rust #[derive(Getters, Dissolve)] #[dissolve(rename = "melt")] struct Funky<'a, T> { number: i64, /// This is commented. commented: T, /// This comment is over /// many lines. /// Here's another line. #[getter(rename = "floating")] many_line: f32, /** * This is one of those * pesky multi-line * comments. */ multi_line: &'a str, /// We don't care for this field. #[getter(skip)] ignore_me: u16, #[doc = r" A doc comment"] one_comment: u64, #[doc = include_str!("../COMMENTS.md")] incl_comment: u64, #[doc(hidden)] /// This comment should be hidden. hideme: u64, // This comment won't show up. bad: u64, } ``` Shall have this auto-generated for it; ```rust /// Auto-generated by `derive_getters::Getters`. impl<'a, T> Funky<'a, T> { /// Get field `number` from instance of `Funky`. pub fn number(&self) -> &i64 { &self.number } /// This is commented. pub fn commented(&self) -> &T { &self.commented } /// This comment is over /// many lines. /// Here's another line. pub fn floating(&self) -> &f32 { &self.many_line } /** * This is one of those * pesky multi-line * comments. */ pub fn multi_line(&'a self) -> &'a str { self.multi_line } /// A doc comment pub fn one_comment(&self) -> &u64 { &self.one_comment } /**Comments sourced from a file included at compile time. */ pub fn incl_comment(&self) -> &u64 { &self.incl_comment } #[doc(hidden)] /// This comment should be hidden. pub fn hideme(&self) -> &u64 { &self.hideme } /// Get field `bad` from instance of `Funky`. pub fn bad(&self) -> &u64 { &self.bad } } /// Auto-generated by `derive_getters::Dissolve`. impl<'a, T> Funky<'a, T> { /// Dissolve `Funky` into a tuple consisting of its fields in order of declaration. pub fn melt(self) -> (i64, T, f32, &'a str, u16, u64, u64, u64, u64) { ( self.number, self.commented, self.many_line, self.multi_line, self.ignore_me, self.one_comment, self.incl_comment, self.hideme, self.bad, ) } } ``` See? Now with comments! ## Caveats 1. `Getters` cannot be derived for unit structs, tuple structs, or enums. 2. `Dissolve` cannot be derived for unit structs or enums. 3. All getter methods return an immutable reference, `&`, to their field. This means for some types it can get awkward. ## Alternatives [getset](https://github.com/Hoverbear/getset). derive-getters-0.4.0/src/dissolve.rs000064400000000000000000000141101046102023000155400ustar 00000000000000//! Dissolve internals use std::{ iter::Extend, convert::TryFrom, }; use proc_macro2::{TokenStream, Span, Group, Delimiter}; use quote::quote; use syn::{ DataStruct, DeriveInput, Fields, FieldsNamed, FieldsUnnamed, Type, Ident, Index, Result, Error, TypeTuple, AttrStyle, LitStr, Attribute, token::Paren, punctuated::Punctuated, parse::{Parse, ParseStream}, }; use crate::{ extract::named_struct, faultmsg::Problem, }; pub enum IndexOrName { Index(Index), Name(Ident), } pub struct Field { ty: Type, name: IndexOrName, } impl Field { fn from_field(field: &syn::Field) -> Result { let name: Ident = field.ident .clone() .ok_or(Error::new(Span::call_site(), Problem::UnnamedField))?; Ok(Field { ty: field.ty.clone(), name: IndexOrName::Name(name), }) } fn from_fields_named(fields_named: &FieldsNamed) -> Result> { fields_named.named .iter() .map(Field::from_field) .collect() } fn from_fields_unnamed(fields_unnamed: &FieldsUnnamed) -> Result> { fields_unnamed.unnamed .iter() .enumerate() .map(|(i, field)| Ok(Field { ty: field.ty.clone(), name: IndexOrName::Index(Index::from(i)), })) .collect() } fn from_struct(structure: &DataStruct) -> Result> { match structure.fields { Fields::Named(ref fields) => Self::from_fields_named(fields), Fields::Unnamed(ref fields) => Self::from_fields_unnamed(fields), Fields::Unit => Err( Error::new(Span::call_site(), Problem::UnitStruct) ), } } } struct Rename { name: Ident, } impl Parse for Rename { fn parse(input: ParseStream) -> Result { syn::custom_keyword!(rename); if input.peek(rename) { let _ = input.parse::()?; let _ = input.parse::()?; let name = input.parse::()?; if !input.is_empty() { Err(Error::new(Span::call_site(), Problem::TokensFollowNewName)) } else { let name = Ident::new(name.value().as_str(), Span::call_site()); Ok(Rename { name } ) } } else { Err(Error::new(Span::call_site(), Problem::InvalidAttribute)) } } } fn dissolve_rename_from(attributes: &[Attribute]) -> Result> { let mut current: Option = None; for attr in attributes { if attr.style != AttrStyle::Outer { continue; } if attr.path().is_ident("dissolve") { let rename = attr.parse_args::()?; current = Some(rename.name); } } Ok(current) } pub struct NamedStruct<'a> { original: &'a DeriveInput, name: Ident, fields: Vec, dissolve_rename: Option, } impl<'a> NamedStruct<'a> { pub fn emit(&self) -> TokenStream { let (impl_generics, struct_generics, where_clause) = self.original.generics .split_for_impl(); let struct_name = &self.name; let types: Punctuated = self.fields .iter() .fold(Punctuated::new(), |mut p, field| { p.push(field.ty.clone()); p }); let return_type = if types.len() > 1 { let tup_group = Group::new(Delimiter::Parenthesis, quote!(#types)); let type_tuple = TypeTuple { paren_token: Paren { span: tup_group.delim_span() }, elems: types, }; quote!(#type_tuple) } else { if let Some(elem) = types.first() { quote!(#elem) } else { quote!(()) } }; let fields: TokenStream = self.fields .iter() .enumerate() .fold(TokenStream::new(), |mut ts, (count, field)| { if count > 0 { ts.extend(quote!(,)) } let field_name = &field.name; let field_expr = match field_name { IndexOrName::Name(name) => { quote!( self.#name ) }, IndexOrName::Index(i) => { quote!( self.#i ) }, }; ts.extend(field_expr); ts }); let dissolve = Ident::new("dissolve", Span::call_site()); let fn_name = self.dissolve_rename .as_ref() .unwrap_or(&dissolve); let impl_comment = " Auto-generated by `derive_getters::Dissolve`."; let impl_doc_comment = quote!(#[doc=#impl_comment]); let fn_comment = format!( " Dissolve `{}` into a tuple consisting of its fields in order of declaration.", struct_name, ); let fn_doc_comment = quote!(#[doc=#fn_comment]); quote!( #impl_doc_comment impl #impl_generics #struct_name #struct_generics #where_clause { #fn_doc_comment pub fn #fn_name(self) -> #return_type { ( #fields ) } } ) } } impl<'a> TryFrom<&'a DeriveInput> for NamedStruct<'a> { type Error = Error; fn try_from(node: &'a DeriveInput) -> Result { let struct_data = named_struct(node)?; let fields = Field::from_struct(struct_data)?; let rename = dissolve_rename_from(node.attrs.as_slice())?; Ok(NamedStruct { original: node, name: node.ident.clone(), fields, dissolve_rename: rename, }) } } derive-getters-0.4.0/src/extract.rs000064400000000000000000000010001046102023000153540ustar 00000000000000//! Common functions use syn::{DataStruct, DeriveInput, Data, Error, Result}; use crate::faultmsg::{StructIs, Problem}; pub fn named_struct(node: &DeriveInput) -> Result<&DataStruct> { match node.data { Data::Struct(ref structure) => Ok(structure), Data::Enum(_) => Err( Error::new_spanned(node, Problem::NotNamedStruct(StructIs::Enum)) ), Data::Union(_) => Err( Error::new_spanned(node, Problem::NotNamedStruct(StructIs::Union)) ), } } derive-getters-0.4.0/src/faultmsg.rs000064400000000000000000000044601046102023000155410ustar 00000000000000//! Error type. use std::fmt; #[derive(Debug)] #[allow(dead_code)] pub enum StructIs { Unnamed, Enum, Union, Unit, } impl fmt::Display for StructIs { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Self::Unnamed => write!(f, "an unnamed struct"), Self::Enum => write!(f, "an enum"), Self::Union => write!(f, "a union"), Self::Unit => write!(f, "a unit struct"), } } } // Almost an error type! But `syn` already has an error type so this just fills the // `T: Display` part to avoid strings littering the source. #[derive(Debug)] #[allow(dead_code)] pub enum Problem { NotNamedStruct(StructIs), UnnamedField, UnitStruct, InnerAttribute, EmptyAttribute, NoGrouping, NonParensGrouping, EmptyGrouping, TokensFollowSkip, TokensFollowNewName, InvalidAttribute, BotchedDocComment, } impl fmt::Display for Problem { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Self::NotNamedStruct(is) => { write!(f, "type must be a named struct, not {}", is) }, Self::UnnamedField => write!(f, "struct fields must be named"), Self::UnitStruct => write!(f, "unit struct has nothing to dissolve"), Self::InnerAttribute => { write!(f, "attribute is an outer not inner attribute") }, Self::EmptyAttribute => write!(f, "attribute has no tokens"), Self::NoGrouping => write!(f, "attribute tokens must be grouped"), Self::NonParensGrouping => { write!(f, "attribute tokens must be within parenthesis") }, Self::EmptyGrouping => { write!(f, "no attribute tokens within parenthesis grouping") }, Self::TokensFollowSkip => { write!(f, "tokens are not meant to follow skip attribute") }, Self::TokensFollowNewName => { write!(f, "no further tokens must follow new name") }, Self::InvalidAttribute => { write!(f, "invalid attribute") }, Self::BotchedDocComment => { write!(f, "Doc comment is botched") }, } } } derive-getters-0.4.0/src/getters.rs000064400000000000000000000157671046102023000154100ustar 00000000000000//! Getters internals use std::convert::TryFrom; use proc_macro2::{TokenStream, Span}; use quote::{quote, ToTokens}; use syn::{ DataStruct, DeriveInput, Fields, FieldsNamed, Type, Ident, LitStr, Result, Error, Attribute, parse::{Parse, ParseStream}, }; use crate::{ extract::named_struct, faultmsg::Problem, }; #[derive(Debug, Clone, PartialEq, Eq)] enum Action { Skip, Rename(Ident), } impl Parse for Action { fn parse(input: ParseStream) -> Result { syn::custom_keyword!(skip); syn::custom_keyword!(rename); if input.peek(skip) { let _ = input.parse::()?; if !input.is_empty() { Err(Error::new(Span::call_site(), Problem::TokensFollowSkip)) } else { Ok(Action::Skip) } } else if input.peek(rename) { let _ = input.parse::()?; let _ = input.parse::()?; let name = input.parse::()?; if !input.is_empty() { Err(Error::new(Span::call_site(), Problem::TokensFollowNewName)) } else { Ok(Action::Rename(Ident::new(name.value().as_str(), Span::call_site()))) } } else { Err(Error::new(Span::call_site(), Problem::InvalidAttribute)) } } } #[derive(Debug, Clone)] struct Doc(TokenStream); #[derive(Debug, Clone)] struct Work { // Whether to carry out an action (skip or rename) on the field. special: Option, // The documentation, if any, on the field. docs: Vec, } impl TryFrom<&[Attribute]> for Work { type Error = Error; fn try_from(attributes: &[Attribute]) -> Result { let mut special: Option = None; let mut docs: Vec = Vec::new(); for attr in attributes { if attr.path().is_ident("getter") { special = Some(attr.parse_args::()?); } if attr.path().is_ident("doc") { docs.push(Doc(attr.to_token_stream())); } } Ok(Work { special, docs }) } } pub struct Field { ty: Type, name: Ident, getter: Ident, docs: Vec, } impl Field { fn from_field(field: &syn::Field) -> Result> { let name: Ident = field.ident .clone() .ok_or(Error::new(Span::call_site(), Problem::UnnamedField))?; let work = Work::try_from(field.attrs.as_slice())?; match work { Work { special: Some(Action::Skip), docs: _ } => Ok(None), Work { special: Some(Action::Rename(ident)), docs } => { Ok(Some(Field { ty: field.ty.clone(), name, getter: ident, docs })) }, Work { special: None, docs } => { Ok(Some(Field { ty: field.ty.clone(), name: name.clone(), getter: name, docs, })) }, } } fn from_fields_named(fields_named: &FieldsNamed) -> Result> { fields_named.named .iter() .try_fold(Vec::new(), |mut fields, field| { if let Some(field) = Field::from_field(field)? { fields.push(field); } Ok(fields) }) } fn from_struct(structure: &DataStruct) -> Result> { let fields_named = match structure.fields { Fields::Named(ref fields) => Ok(fields), Fields::Unnamed(_) => Err( Error::new(Span::call_site(), Problem::UnnamedField) ), Fields::Unit => Err( Error::new(Span::call_site(), Problem::UnitStruct) ), }?; Self::from_fields_named(fields_named) } fn emit(&self, struct_name: &Ident) -> TokenStream { let returns = &self.ty; let field_name = &self.name; let getter_name = &self.getter; let doc_comments: Vec = if self.docs.is_empty() { let comment = format!( " Get field `{}` from instance of `{}`.", field_name, struct_name, ); vec![quote!(#[doc=#comment])] } else { self.docs .iter() .map(|d| d.0.to_owned()) .collect() }; match &self.ty { Type::Reference(tr) => { let lifetime = tr.lifetime.as_ref(); quote!( #(#doc_comments)* pub fn #getter_name(&#lifetime self) -> #returns { self.#field_name } ) }, _ => { quote!( #(#doc_comments)* pub fn #getter_name(&self) -> &#returns { &self.#field_name } ) }, } } } pub struct NamedStruct<'a> { original: &'a DeriveInput, name: Ident, fields: Vec, } impl<'a> NamedStruct<'a> { pub fn emit(&self) -> TokenStream { let (impl_generics, struct_generics, where_clause) = self.original.generics .split_for_impl(); let struct_name = &self.name; let methods: Vec = self.fields .iter() .map(|field| field.emit(&self.name)) .collect(); let impl_comment = " Auto-generated by `derive_getters::Getters`."; let impl_doc_comment = quote!(#[doc=#impl_comment]); quote!( #impl_doc_comment impl #impl_generics #struct_name #struct_generics #where_clause { #(#methods)* } ) } } impl<'a> TryFrom<&'a DeriveInput> for NamedStruct<'a> { type Error = Error; fn try_from(node: &'a DeriveInput) -> Result { let struct_data = named_struct(node)?; let fields = Field::from_struct(struct_data)?; Ok(NamedStruct { original: node, name: node.ident.clone(), fields, }) } } #[cfg(test)] mod test { use super::*; #[test] fn parse_action() -> Result<()> { let a: Action = syn::parse_str("skip")?; assert!(a == Action::Skip); let r: Result = syn::parse_str("skip = blah"); assert!(r.is_err()); let a: Action = syn::parse_str("rename = \"hello\"")?; let check = Action::Rename(Ident::new("hello", Span::call_site())); assert!(a == check); let r: Result = syn::parse_str("rename + \"chooga\""); assert!(r.is_err()); let r: Result = syn::parse_str("rename = \"chooga\" | bongle"); assert!(r.is_err()); Ok(()) } } derive-getters-0.4.0/src/lib.rs000064400000000000000000000147361046102023000144740ustar 00000000000000//! This library provides two derive macros. One, `Getters` for autogenerating getters and //! `Dissolve` for consuming a struct returning a tuple of all fields. They can only be //! used on named structs. //! //! # Derives //! //! Only named structs can derive `Getters` or `Dissolve`. //! //! # `Getter` methods generated //! //! The getter methods generated shall bear the same name as the struct fields and be //! publicly visible. The methods return an immutable reference to the struct field of the //! same name. If there is already a method defined with that name there'll be a collision. //! In these cases one of two attributes can be set to either `skip` or `rename` the getter. //! //! //! # `Getters` Usage //! //! In lib.rs or main.rs; //! //! ```edition2021 //! use derive_getters::Getters; //! //! #[derive(Getters)] //! struct Number { //! num: u64, //! } //! //! let number = Number { num: 655 }; //! assert!(number.num() == &655); //! ``` //! //! Here, a method called `num()` has been created for the `Number` struct which gives a //! reference to the `num` field. //! //! This macro can also derive on structs that have simple generic types. For example; //! //! ```edition2021 //! # use derive_getters::Getters; //! #[derive(Getters)] //! struct Generic { //! gen_t: T, //! gen_u: U, //! } //! # //! # fn main() { } //! ``` //! //! The macro can also handle generic types with trait bounds. For example; //! ```edition2021 //! # use derive_getters::Getters; //! #[derive(Getters)] //! struct Generic { //! gen_t: T, //! gen_u: U, //! } //! # //! # fn main() { } //! ``` //! The trait bounds can also be declared in a `where` clause. //! //! Additionaly, simple lifetimes are OK too; //! ```edition2021 //! # use derive_getters::Getters; //! #[derive(Getters)] //! struct Annotated<'a, 'b, T> { //! stuff: &'a T, //! comp: &'b str, //! num: u64, //! } //! # //! # fn main() { } //! ``` //! //! # `Getter` Attributes //! Getters can be further configured to either skip or rename a getter. //! //! * #[getter(skip)] //! Will skip generating a getter for the field being decorated. //! //! * #[getter(rename = "name")] //! Changes the name of the getter (default is the field name) to "name". //! //!```edition2021 //! # use derive_getters::Getters; //! #[derive(Getters)] //! struct Attributed { //! keep_me: u64, //! //! #[getter(skip)] //! skip_me: u64, //! //! #[getter(rename = "number")] //! rename_me: u64, //! } //! # //! # fn main() { } //! ``` //! //! # `Dissolve` method generated //! //! Deriving `Dissolve` on a named or unit struct will generate a method `dissolve(self)` //! which shall return a tuple of all struct fields in the order they were defined. Calling //! this method consumes the struct. The name of this method can be changed with an //! attribute. //! //! # `Dissolve` usage //! //! ```edition2021 //! # use derive_getters::Dissolve; //! #[derive(Dissolve)] //! struct Stuff { //! name: String, //! price: f64, //! count: usize, //! } //! //! fn main() { //! let stuff = Stuff { //! name: "Hogie".to_owned(), //! price: 123.4f64, //! count: 100, //! }; //! //! let (n, p, c) = stuff.dissolve(); //! assert!(n == "Hogie"); //! assert!(p == 123.4f64); //! assert!(c == 100); //! } //! ``` //! //! # `Dissolve` can be derived on tuple structs. //! //! ```edition2021 //! # use derive_getters::Dissolve; //! #[derive(Dissolve)] //! struct Stuff(String, f64, usize); //! //! fn main() { //! let stuff = Stuff("Hogie".to_owned(), 123.4f64, 100); //! //! let (n, p, c) = stuff.dissolve(); //! assert!(n == "Hogie"); //! assert!(p == 123.4f64); //! assert!(c == 100); //! } //! ``` //! //! # `Dissolve` Attributes //! You can rename the `dissolve` function by using a struct attribute. //! //! * #[dissolve(rename = "name")] //! //! ```edition2021 //! # use derive_getters::Dissolve; //! #[derive(Dissolve)] //! #[dissolve(rename = "shatter")] //! struct Numbers { //! a: u64, //! b: i64, //! c: f64, //! } //! # //! # fn main() { } //! ``` //! //! # Comment Replication/Generation //! //! Comments are produced for the auto-generated getters or dissolver. A comment is also //! generated for the impl block. //! //! ## Replication of comments //! //! Any field comments are replicated for the getter. If the field on the target struct //! has a comment; the getter for it shall have the exact same comment. //! //! ```edition2021 //! # use derive_getters::Getters; //! #[derive(Getters)] //! struct Number { //! /// My special number. //! num: u64, //! } //! # //! # fn main() { } //!``` //! //! ## Generation of comments //! //! If no comment is present for the field, one shall be generated like so; //! " Get field `{}` from instance of `{}`." //! //! A comment for the dissolve function shall be similarily generated; //! "Dissolve `{}` into a tuple consisting of its fields in order of declaration." //! //! The impl block for the getters or dissolve function also has a comment generated; //! "Auto-generated by `derive_getters::Getters`." and or //! "Auto-generated by `derive_getters::Dissolve`". //! //! # Panics //! //! If `Getters` is derived on unit, unnamed structs, enums or unions. //! If `Dissolve` is dervied on unnamed structs, enums or unions. //! //! # Cannot Do //! Const generics aren't handled by this macro nor are they tested. use std::convert::TryFrom; extern crate proc_macro; use syn::{DeriveInput, parse_macro_input}; mod faultmsg; mod dissolve; mod getters; mod extract; /// Generate getter methods for all named struct fields in a seperate struct `impl` block. /// Getter methods share the name of the field they're 'getting'. Methods return an /// immutable reference to the field. #[proc_macro_derive(Getters, attributes(getter))] pub fn getters(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let ast = parse_macro_input!(input as DeriveInput); getters::NamedStruct::try_from(&ast) .map(|ns| ns.emit()) .unwrap_or_else(|err| err.to_compile_error()) .into() } /// Produce a `dissolve` method that consumes the named struct returning a tuple of all the /// the struct fields. #[proc_macro_derive(Dissolve, attributes(dissolve))] pub fn dissolve(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let ast = parse_macro_input!(input as DeriveInput); dissolve::NamedStruct::try_from(&ast) .map(|ns| ns.emit()) .unwrap_or_else(|err| err.to_compile_error()) .into() } derive-getters-0.4.0/tests/01-legacy.rs000064400000000000000000000037311046102023000157540ustar 00000000000000//! Legacy tests from 0.0.7 version. use derive_getters::Getters; #[derive(Getters)] struct Number { num: u64, } fn number_num() { let number = Number { num: 655 }; assert!(number.num() == &655); } #[derive(Getters)] struct ManyNumbers { integer: u64, floating: f64, byte: u8, } fn many_numbers() { let numbers = ManyNumbers { integer: 655, floating: 45.5, byte: 122, }; assert!(numbers.integer() == &655); assert!(numbers.floating() == &45.5); assert!(numbers.byte() == &122); } #[derive(Getters)] struct Textual { author: Option, heading: String, lines: Vec, } fn textual_struct() { let article = Textual { author: None, heading: "abcdefg".to_string(), lines: vec![ "hijk".to_string(), "lmno".to_string(), "pqrs".to_string(), "tuvw".to_string(), ], }; assert!(article.author() == &None); assert!(article.heading() == "abcdefg"); assert!(article.lines().len() == 4); assert!(article.lines()[0] == "hijk"); assert!(article.lines()[1] == "lmno"); assert!(article.lines()[2] == "pqrs"); assert!(article.lines()[3] == "tuvw"); let book = Textual { author: Some("name".to_string()), heading: "1234".to_string(), lines: vec![ "2345".to_string(), "3456".to_string(), "4567".to_string(), "5678".to_string(), ], }; assert!(book.author() == &Some("name".to_string())); } /// There shouldn't be any dead code warnings on unused methods, only on unused slots which /// are not the libraries fault. #[derive(Getters)] struct DeadCode { x: u64, y: u64, z: u64, } #[test] fn dead_code_struct() { let dc = DeadCode { x: 1, y: 2, z: 3, }; assert!(*dc.x() == 1); } fn main() { number_num(); many_numbers(); textual_struct(); } derive-getters-0.4.0/tests/02-simple-single-generic.rs000064400000000000000000000025371046102023000206760ustar 00000000000000use derive_getters::Getters; #[derive(Getters)] struct SimpleSingleGeneric { concrete: u16, generic: T, } impl SimpleSingleGeneric { pub fn new(concrete: u16, generic: T) -> Self { SimpleSingleGeneric { concrete, generic } } } #[derive(PartialEq, Eq, PartialOrd, Ord)] pub enum Enumeration { One, Two, Three, } #[derive(Getters)] struct InvolvedSingleGeneric { concrete: u16, generic: T, } impl InvolvedSingleGeneric { pub fn new(concrete: u16, generic: T) -> Self { InvolvedSingleGeneric { concrete, generic } } } #[derive(Getters)] struct WhereClauseSingleGeneric where T: Eq + Ord { concrete: u16, generic: T, } impl WhereClauseSingleGeneric where T: Eq + Ord { pub fn new(concrete: u16, generic: T) -> Self { WhereClauseSingleGeneric { concrete, generic } } } fn main() { let ssg = SimpleSingleGeneric::new(23, "Hello".to_string()); assert!(*ssg.concrete() == 23); assert!(ssg.generic() == "Hello"); let isg = InvolvedSingleGeneric::new(44, Enumeration::Two); assert!(*isg.concrete() == 44); assert!(*isg.generic() == Enumeration::Two); let wcsg = WhereClauseSingleGeneric::new(99, Enumeration::Three); assert!(*wcsg.concrete() == 99); assert!(*wcsg.generic() == Enumeration::Three); } derive-getters-0.4.0/tests/03-simple-multi-generic.rs000064400000000000000000000042001046102023000205350ustar 00000000000000use std::ops::Div; use derive_getters::Getters; #[derive(Getters)] struct SimpleMultiGeneric { concrete: u16, generic_t: T, generic_u: U, generic_v: V, } impl SimpleMultiGeneric { pub fn new(concrete: u16, generic_t: T, generic_u: U, generic_v: V) -> Self { SimpleMultiGeneric { concrete, generic_t, generic_u, generic_v } } } #[derive(PartialEq, Eq, PartialOrd, Ord)] pub enum Enumeration { One, Two, Three, } #[derive(Getters)] struct InvolvedMultiGeneric { concrete: u16, generic_t: T, generic_u: U, generic_v: V, } impl InvolvedMultiGeneric { pub fn new(concrete: u16, generic_t: T, generic_u: U, generic_v: V) -> Self { InvolvedMultiGeneric { concrete, generic_t, generic_u, generic_v } } } #[derive(Getters)] struct WhereClauseMultiGeneric where T: Eq + Ord, U: Div, V: Clone, { concrete: u16, generic_t: T, generic_u: U, generic_v: V, } impl WhereClauseMultiGeneric where T: Eq + Ord, U: Div, V: Clone, { pub fn new(concrete: u16, generic_t: T, generic_u: U, generic_v: V) -> Self { WhereClauseMultiGeneric { concrete, generic_t, generic_u, generic_v } } } fn main() { let smg = SimpleMultiGeneric::new( 23, "Hello".to_string(), 55f64, [1, 2, 3], ); assert!(*smg.concrete() == 23); assert!(smg.generic_t() == "Hello"); assert!(*smg.generic_u() == 55f64); assert!(*smg.generic_v() == [1, 2, 3]); let img = InvolvedMultiGeneric::new( 44, Enumeration::Two, 1024f32, "String".to_string(), ); assert!(*img.concrete() == 44); assert!(*img.generic_t() == Enumeration::Two); assert!(*img.generic_u() == 1024f32); assert!(img.generic_v() == "String"); let wcmg = WhereClauseMultiGeneric::new( 99, Enumeration::Three, 1222f64, "Might".to_string() ); assert!(*wcmg.concrete() == 99); assert!(*wcmg.generic_t() == Enumeration::Three); assert!(*wcmg.generic_u() == 1222f64); assert!(wcmg.generic_v() == "Might"); } derive-getters-0.4.0/tests/04-simple-lifetime-annot.rs000064400000000000000000000020671046102023000207160ustar 00000000000000use derive_getters::Getters; #[derive(Getters)] struct LifetimeAnnotated<'a> { val1: u64, val2: String, buffer: &'a [u8], } impl<'a> LifetimeAnnotated<'a> { pub fn new>(v1: u64, v2: T, buf: &'a [u8]) -> Self { LifetimeAnnotated { val1: v1, val2: v2.into(), buffer: buf, } } } #[derive(Getters)] struct MultiAnnotated<'a, 'b, 'c, T> { v1: &'a str, v2: &'b [u8], v3: &'c T, } impl<'a, 'b, 'c, T> MultiAnnotated<'a, 'b, 'c, T> { pub fn new(v1: &'a str, v2: &'b [u8], v3: &'c T) -> Self { MultiAnnotated { v1, v2, v3 } } } #[derive(PartialEq, Eq)] struct GenericType; fn main() { let buffer: [u8; 12] = [88; 12]; let la = LifetimeAnnotated::new(44, "Annot", &buffer); assert!(*la.val1() == 44); assert!(la.val2() == "Annot"); assert!(la.buffer() == &buffer); let gt = GenericType; let ma = MultiAnnotated::new("Hello", &buffer, >); assert!(ma.v1() == "Hello"); assert!(ma.v2() == &buffer); assert!(ma.v3() == >); } derive-getters-0.4.0/tests/05-skip-rename-attributes.rs000064400000000000000000000046751046102023000211230ustar 00000000000000use derive_getters::Getters; #[derive(Getters)] struct SkipAField { keep: u64, #[getter(skip)] skip: String, } impl SkipAField { pub fn new>(keep: u64, skip: T) -> Self { SkipAField { keep, skip: skip.into() } } } #[derive(Getters)] struct SkipMany { keep1: u64, #[getter(skip)] skip1: String, #[getter(skip)] skip2: String, #[getter(skip)] skip3: String, #[getter(skip)] skip4: String, keep2: String, } impl SkipMany { pub fn new>( keep1: u64, skip1: T, skip2: T, skip3: T, skip4: T, keep2: T ) -> Self { SkipMany { keep1, skip1: skip1.into(), skip2: skip2.into(), skip3: skip3.into(), skip4: skip4.into(), keep2: keep2.into(), } } } #[derive(Getters)] struct Rename { #[getter(rename = "number")] field: u64, } #[derive(Getters)] struct RenameMany { #[getter(rename = "number1")] field1: u64, #[getter(rename = "number2")] field2: u64, field3: u64, #[getter(rename = "number3")] field4: u64, } #[derive(Getters)] struct Combination<'a, 'b, 'c, T> { #[getter(rename = "skip_me")] #[getter(skip)] v1: &'a str, #[getter(rename = "buffer")] v2: &'b [u8], #[getter(skip)] v3: &'c T, #[getter(skip)] #[getter(rename = "keep_me")] v4: u64, } impl<'a, 'b, 'c, T> Combination<'a, 'b, 'c, T> { pub fn new(v1: &'a str, v2: &'b [u8], v3: &'c T, v4: u64) -> Self { Combination { v1, v2, v3, v4 } } } #[derive(PartialEq, Eq)] struct GenericType; fn main() { let s1 = SkipAField::new(45, "You can't get me."); assert!(*s1.keep() == 45); let s2 = SkipMany::new(33, "Dodge", "Duck", "Dip", "Dive", "...dodge!"); assert!(*s2.keep1() == 33); //assert!(s2.skip1() == "Dodge"); assert!(s2.keep2() == "...dodge!"); let s3 = Rename { field: 35 }; assert!(*s3.number() == 35); let s4 = RenameMany { field1: 1, field2: 2, field3: 3, field4: 4 }; assert!(*s4.number1() == 1); assert!(*s4.number2() == 2); assert!(*s4.field3() == 3); assert!(*s4.number3() == 4); let gt = GenericType; let buffer: [u8; 12] = [88; 12]; let s5 = Combination::new("Hello", &buffer, >, 64); //assert!(s5.skip_me() == "Hello"); assert!(s5.buffer() == &buffer); //assert!(s5.v3() == >); assert!(*s5.keep_me() == 64); } derive-getters-0.4.0/tests/06-plays-with-others.rs000064400000000000000000000013421046102023000201140ustar 00000000000000use serde::{Serialize, Deserialize}; use derive_getters::Getters; #[derive(Debug, Copy, Clone, PartialEq, Eq, Getters, Serialize, Deserialize)] struct Plays { #[getter(rename = "skip_me")] #[getter(skip)] v1: u64, #[serde(skip)] #[getter(rename = "buffer")] v2: [u8; 12], #[getter(skip)] #[getter(rename = "keep_me")] #[serde(rename = "value3")] v3: u64, } impl Plays { pub fn new(v1: u64, v2: [u8; 12], v3: u64) -> Self { Plays { v1, v2, v3 } } } fn main() { let buffer: [u8; 12] = [88; 12]; let c = Plays::new(46, buffer, 64); //assert!(c.skip_me() == "Hello"); assert!(c.buffer() == &buffer); //assert!(c.v3() == >); assert!(*c.keep_me() == 64); } derive-getters-0.4.0/tests/07-dissolve-basic.rs000064400000000000000000000026741046102023000174320ustar 00000000000000//! First tests for new `dissolve` functionality use serde::{Serialize, Deserialize}; use derive_getters::Dissolve; #[derive(Dissolve, Serialize, Deserialize)] struct Number { num: u64, } #[derive(Copy, Clone, PartialEq, Eq, Serialize, Deserialize)] struct Inner { a: u64, b: i64, } #[derive(Dissolve, Serialize, Deserialize)] struct ManyStuff { name: String, price: f64, count: usize, inner: Inner, } #[derive(Dissolve, Serialize, Deserialize)] #[dissolve(rename = "shatter")] struct LotsOfStuff { name: String, price: f64, count: usize, inner: Inner, } impl LotsOfStuff { fn dissolve(&self) -> f64 { self.inner.b as f64 * self.price } } fn main() { let n = Number { num: 64 }; let number = n.dissolve(); assert!(number == 64); let inner = Inner { a: 22, b: -33 }; let stuff = ManyStuff { name: "Hogie".to_owned(), price: 123.4f64, count: 100, inner, }; let (n, p, c, i) = stuff.dissolve(); assert!(n == "Hogie"); assert!(p == 123.4f64); assert!(c == 100); assert!(i == inner); //let _ = stuff.dissolve(); let stuff = LotsOfStuff { name: "Hogie".to_owned(), price: 123.4f64, count: 100, inner, }; let (n, p, c, i) = stuff.shatter(); assert!(n == "Hogie"); assert!(p == 123.4f64); assert!(c == 100); assert!(i == inner); //let _ = stuff.shatter(); } derive-getters-0.4.0/tests/08-dissolve-generic-and-ref.rs000064400000000000000000000026771046102023000213030ustar 00000000000000//! Try with generics and references. use derive_getters::{Getters, Dissolve}; #[derive(Copy, Clone, PartialEq, Eq)] struct ConcreteType { a: u64, b: i64, } #[derive(Getters, Dissolve)] struct MultiAnnotated<'a, 'b, 'c, T> { v1: &'a str, v2: &'b [u8], v3: &'c T, owned: String, } impl<'a, 'b, 'c, T> MultiAnnotated<'a, 'b, 'c, T> { pub fn new(v1: &'a str, v2: &'b [u8], v3: &'c T, owned: String) -> Self { MultiAnnotated { v1, v2, v3, owned } } } #[derive(Getters, Dissolve)] #[dissolve(rename = "unmake")] struct PolyAnnotated<'a, 'b, 'c, T> { v1: &'a str, v2: &'b [u8], v3: &'c T, owned: String, } impl<'a, 'b, 'c, T> PolyAnnotated<'a, 'b, 'c, T> { pub fn new(v1: &'a str, v2: &'b [u8], v3: &'c T, owned: String) -> Self { PolyAnnotated { v1, v2, v3, owned } } pub fn dissolve(self) -> String { self.owned } } fn main() { let buffer: [u8; 12] = [88; 12]; let gt = ConcreteType { a: 44, b: -100 }; let ma = MultiAnnotated::new("Hi", &buffer, >, "Another".to_owned()); let (v1, v2, v3, owned) = ma.dissolve(); assert!(v1 == "Hi"); assert!(v2 == &buffer); assert!(*v3 == gt); assert!(owned == "Another"); let pa = PolyAnnotated::new("Hi", &buffer, >, "Another".to_owned()); let (v1, v2, v3, owned) = pa.unmake(); assert!(v1 == "Hi"); assert!(v2 == &buffer); assert!(*v3 == gt); assert!(owned == "Another"); } derive-getters-0.4.0/tests/09-dissolve-tuple-struct.rs000064400000000000000000000017551046102023000210250ustar 00000000000000//! Try with generics and references. use derive_getters::Dissolve; #[derive(Debug, Copy, Clone, PartialEq, Eq, Dissolve)] struct SimpleUnnamed(u64, i64); #[derive(Dissolve)] struct MultiAnnotated<'a, 'b, 'c, T>(&'a str, &'b [u8], &'c T, String); #[derive(Dissolve)] #[dissolve(rename = "unmake")] struct PolyAnnotated<'a, 'b, 'c, T>(&'a str, &'b [u8], &'c T, String); fn main() { let buffer: [u8; 12] = [88; 12]; let su = SimpleUnnamed(44,-100); let (a, b) = su.dissolve(); assert_eq!(44, a); assert_eq!(-100, b); let ma = MultiAnnotated("Hi", &buffer, &su, "Another".to_owned()); let (v1, v2, v3, owned) = ma.dissolve(); assert!(v1 == "Hi"); assert!(v2 == &buffer); assert!(*v3 == su); assert!(owned == "Another"); let pa = PolyAnnotated("Hi", &buffer, &su, "Another".to_owned()); let (v1, v2, v3, owned) = pa.unmake(); assert!(v1 == "Hi"); assert!(v2 == &buffer); assert!(*v3 == su); assert!(owned == "Another"); } derive-getters-0.4.0/tests/10-dissolve-unit-struct.rs000064400000000000000000000001651046102023000206350ustar 00000000000000use derive_getters::Dissolve; #[derive(Debug, Copy, Clone, Dissolve)] struct Unit; fn main() { let _ = Unit; } derive-getters-0.4.0/tests/10-dissolve-unit-struct.stderr000064400000000000000000000004621046102023000215140ustar 00000000000000error: unit struct has nothing to dissolve --> tests/10-dissolve-unit-struct.rs:3:30 | 3 | #[derive(Debug, Copy, Clone, Dissolve)] | ^^^^^^^^ | = note: this error originates in the derive macro `Dissolve` (in Nightly builds, run with -Z macro-backtrace for more info) derive-getters-0.4.0/tests/lib.rs000064400000000000000000000013361046102023000150370ustar 00000000000000#[test] fn tests() { let t = trybuild::TestCases::new(); t.pass("tests/01-legacy.rs"); t.pass("tests/02-simple-single-generic.rs"); t.pass("tests/03-simple-multi-generic.rs"); t.pass("tests/04-simple-lifetime-annot.rs"); t.pass("tests/05-skip-rename-attributes.rs"); t.pass("tests/06-plays-with-others.rs"); t.pass("tests/07-dissolve-basic.rs"); t.pass("tests/08-dissolve-generic-and-ref.rs"); t.pass("tests/09-dissolve-tuple-struct.rs"); t.compile_fail("tests/10-dissolve-unit-struct.rs"); } #[test] fn test_readme_deps() { version_sync::assert_markdown_deps_updated!("README.md"); } #[test] fn test_html_root_url() { version_sync::assert_html_root_url_updated!("src/lib.rs"); }