derive-getters-0.5.0/.build.yml000064400000000000000000000011421046102023000144550ustar 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 cargo +stable install cargo-hack --locked - build: | cd derive-getters cargo build cargo build --release - test: | cd derive-getters cargo hack test --feature-powerset cargo hack test --feature-powerset --release derive-getters-0.5.0/.cargo_vcs_info.json0000644000000001360000000000100137670ustar { "git": { "sha1": "e99a3aaea103629fa5ffa958517890246f0b1b8c" }, "path_in_vcs": "" }derive-getters-0.5.0/.gitignore000064400000000000000000000000221046102023000145410ustar 00000000000000target Cargo.lock derive-getters-0.5.0/.travis.yml000064400000000000000000000001431046102023000146660ustar 00000000000000language: rust rust: - stable - beta - nightly matrix: allow_failures: - rust: nightly derive-getters-0.5.0/CHANGELOG.md000064400000000000000000000007221046102023000143710ustar 00000000000000# Changelog All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] ## [0.5.0] - 2024-08-08 ### Changed - Minimum Supported Rust Version (MSRV) bumped to v1.61.0 ### Added - `auto_copy_getters` feature. Thank you Denis Gorbachev derive-getters-0.5.0/Cargo.toml0000644000000032220000000000100117640ustar # 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.61.0" name = "derive-getters" version = "0.5.0" authors = ["Stephan Luther "] build = false autobins = false autoexamples = false autotests = false autobenches = 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" path = "src/lib.rs" proc-macro = true [[test]] name = "tests" path = "tests/lib.rs" [[test]] name = "all_ref_getters" path = "tests/all_ref_getters.rs" [[test]] name = "auto_copy_getters" path = "tests/auto_copy_getters.rs" required-features = ["auto_copy_getters"] [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" [features] auto_copy_getters = [] default = [] derive-getters-0.5.0/Cargo.toml.orig000064400000000000000000000017661046102023000154600ustar 00000000000000[package] name = "derive-getters" version = "0.5.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.61.0" autotests = false readme = "README.md" [lib] name = "derive_getters" proc-macro = true [[test]] name = "tests" path = "tests/lib.rs" [[test]] name = "all_ref_getters" path = "tests/all_ref_getters.rs" [[test]] name = "auto_copy_getters" path = "tests/auto_copy_getters.rs" required-features = ["auto_copy_getters"] [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" [features] default = [] auto_copy_getters = [] derive-getters-0.5.0/LICENSE000064400000000000000000000020571046102023000135700ustar 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.5.0/README.md000064400000000000000000000174011046102023000140410ustar 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) ## Installation Add to your `Cargo.toml`: ```toml [dependencies] derive-getters = "0.5.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; ``` ## Features * `auto_copy_getters` - enable to automatically generate copy getters for primitive types (return by value instead of by reference). ## 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 } } ``` You can also derive "copy getters" that return by value instead of by reference: ```rust #[derive(Getters)] pub struct MyCheesyStruct { #[getter(copy)] x: i64, #[getter(copy)] y: i64, } ``` This will generate the following impl 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 } } ``` If you want to automatically generate copy getters for primitive types, you can enable the `auto_copy_getters` feature. It is recommended to enable `auto_copy_getters` because it makes your code faster (no need to dereference a pointer). The Rust reference [recommends implementing the `Copy` trait](https://doc.rust-lang.org/std/marker/trait.Copy.html#when-should-my-type-be-copy). This crate can also handle structs with simple generic parameters and lifetime annotations. Check [docs](https://docs.rs/derive-getters) 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 `Getters` attributes: * `#[getter(skip)]` on the field to skip generating a getter for it. * `#[getter(copy)]` on the field to return a value by copy instead of by reference (note: the value must implement the `Copy` trait). * `#[getter(rename = "name")]` on the field to change the getter name to "name". `Dissolve` attributes: * `#[dissolve(rename = "name")]` on the struct 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! ## Testing With the presence of the new `#[cfg]` feature `auto_copy_getters`, not all tests will run with the usual `cargo test`. To run all tests in one go, give the [cargo-hack](https://crates.io/crates/cargo-hack) crate a try. After installing it, run; ``` cargo hack test --feature-powerset ``` ## Caveats 1. `Getters` cannot be derived for unit structs, tuple structs, or enums. 2. `Dissolve` cannot be derived for unit structs or enums. 3. Without `auto_copy_getters`, all getter methods return an immutable reference, `&`, to their field. This means for some types it can get awkward. But with `auto_copy_getters` feature set there won't be any references used in the generated getters for primitive `Copy` types. ## Alternatives [getset](https://github.com/Hoverbear/getset). derive-getters-0.5.0/src/dissolve.rs000064400000000000000000000144021046102023000155450ustar 00000000000000//! Dissolve internals use std::{convert::TryFrom, iter::Extend}; use proc_macro2::{Delimiter, Group, Span, TokenStream}; use quote::quote; use syn::{ parse::{Parse, ParseStream}, punctuated::Punctuated, token::Paren, AttrStyle, Attribute, DataStruct, DeriveInput, Error, Fields, FieldsNamed, FieldsUnnamed, Ident, Index, LitStr, Result, Type, TypeTuple, }; 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 types_len = types.len(); 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 body = if types_len > 0 { quote! { ( #fields ) } } else { // Don't output `()` to avoid a compiler warning on an empty struct TokenStream::new() }; 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 { #body } } ) } } 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.5.0/src/extract.rs000064400000000000000000000010321046102023000153620ustar 00000000000000//! Common functions use syn::{Data, DataStruct, DeriveInput, Error, Result}; use crate::faultmsg::{Problem, StructIs}; 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.5.0/src/faultmsg.rs000064400000000000000000000046761046102023000155530ustar 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, TokensFollowCopy, 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::TokensFollowCopy => { write!(f, "tokens are not meant to follow copy 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.5.0/src/getters.rs000064400000000000000000000236051046102023000153770ustar 00000000000000//! Getters internals use std::convert::TryFrom; use proc_macro2::{Span, TokenStream}; use quote::{quote, ToTokens}; use syn::parse::{Parse, ParseStream}; use syn::{ Attribute, DataStruct, DeriveInput, Error, Fields, FieldsNamed, Ident, LitStr, Result, Type, }; use crate::{extract::named_struct, faultmsg::Problem}; #[derive(Debug, Clone, PartialEq, Eq)] enum Action { Skip, Rename(Ident), Copy, } impl Parse for Action { fn parse(input: ParseStream) -> Result { syn::custom_keyword!(skip); syn::custom_keyword!(rename); syn::custom_keyword!(copy); 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 if input.peek(copy) { let _ = input.parse::()?; if !input.is_empty() { Err(Error::new(Span::call_site(), Problem::TokensFollowCopy)) } else { Ok(Action::Copy) } } 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 enum ReturnKind { Copy, Reference, } impl Default for ReturnKind { fn default() -> Self { ReturnKind::Reference } } pub struct Field { ty: Type, name: Ident, getter: Ident, return_kind: ReturnKind, 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), .. } => Ok(None), Work { special, docs } => { let ty = field.ty.clone(); let getter = match &special { Some(Action::Rename(ident)) => ident.clone(), _ => name.clone(), }; let return_kind = match &special { Some(Action::Copy) => ReturnKind::Copy, _ => { #[cfg(feature = "auto_copy_getters")] if type_implements_copy(&ty) { ReturnKind::Copy } else { ReturnKind::Reference } #[cfg(not(feature = "auto_copy_getters"))] ReturnKind::Reference } }; Ok(Some(Field { ty, name, getter, return_kind, 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 } ) } _ => match self.return_kind { ReturnKind::Copy => quote!( #(#doc_comments)* pub fn #getter_name(&self) -> #returns { self.#field_name } ), ReturnKind::Reference => 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(()) } } #[cfg(feature = "auto_copy_getters")] fn type_implements_copy(ty: &syn::Type) -> bool { match ty { Type::Array(array) => type_implements_copy(&array.elem), // Assuming array.elem is the type of the elements and we recursively check if it implements Copy Type::BareFn(_) => true, // Function pointers implement Copy Type::Group(group) => type_implements_copy(&group.elem), Type::ImplTrait(_) => false, // ImplTrait does not implement Copy Type::Infer(_) => false, // Infer does not implement Copy Type::Macro(_) => false, // Macros do not implement Copy Type::Never(_) => true, // The Never type (!) implements Copy Type::Paren(paren) => type_implements_copy(&paren.elem), Type::Path(path) => type_path_implements_copy(path), Type::Ptr(_) => false, // Raw pointers do not implement Copy Type::Reference(_) => false, // References do not implement Copy Type::Slice(slice) => type_implements_copy(&slice.elem), // Similar to arrays Type::TraitObject(_) => false, // Trait objects do not implement Copy Type::Tuple(tuple) => tuple.elems.iter().all(type_implements_copy), // All elements in the tuple must implement Copy Type::Verbatim(_) => false, // Verbatim is a catch-all and does not implement Copy _ => false, // Catch all for any other types not explicitly matched } } #[cfg(feature = "auto_copy_getters")] fn type_path_implements_copy(path: &syn::TypePath) -> bool { match path.to_token_stream().to_string().as_str() { "u8" | "u16" | "u32" | "u64" | "u128" | "i8" | "i16" | "i32" | "i64" | "i128" | "f32" | "f64" | "bool" | "char" | "usize" | "isize" => true, _ => false, } } derive-getters-0.5.0/src/lib.rs000064400000000000000000000147441046102023000144740ustar 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 User { //! name: String, //! } //! //! let user = User { name: "John Doe".to_string() }; //! assert!(user.name() == "John Doe"); //! ``` //! //! 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::{parse_macro_input, DeriveInput}; mod dissolve; mod extract; mod faultmsg; mod getters; /// 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.5.0/tests/all_ref_getters.rs000064400000000000000000000012171046102023000174310ustar 00000000000000#[test] #[cfg(not(feature = "auto_copy_getters"))] fn tests() { let t = trybuild::TestCases::new(); t.pass("tests/allref/01-legacy.rs"); t.pass("tests/allref/02-simple-single-generic.rs"); t.pass("tests/allref/03-simple-multi-generic.rs"); t.pass("tests/allref/04-simple-lifetime-annot.rs"); t.pass("tests/allref/05-skip-rename-copy-attributes.rs"); t.pass("tests/allref/06-plays-with-others.rs"); t.pass("tests/allref/07-dissolve-basic.rs"); t.pass("tests/allref/08-dissolve-generic-and-ref.rs"); t.pass("tests/allref/09-dissolve-tuple-struct.rs"); t.compile_fail("tests/allref/10-dissolve-unit-struct.rs"); } derive-getters-0.5.0/tests/allref/01-legacy.rs000064400000000000000000000037311046102023000172220ustar 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.5.0/tests/allref/02-simple-single-generic.rs000064400000000000000000000025371046102023000221440ustar 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.5.0/tests/allref/03-simple-multi-generic.rs000064400000000000000000000042001046102023000220030ustar 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.5.0/tests/allref/04-simple-lifetime-annot.rs000064400000000000000000000020671046102023000221640ustar 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.5.0/tests/allref/05-skip-rename-copy-attributes.rs000064400000000000000000000055311046102023000233310ustar 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 CopyMany { #[getter(copy)] field1: u64, #[getter(copy)] field2: bool, field3: String, } #[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, #[getter(copy)] v5: bool } impl<'a, 'b, 'c, T> Combination<'a, 'b, 'c, T> { pub fn new(v1: &'a str, v2: &'b [u8], v3: &'c T, v4: u64, v5: bool) -> Self { Combination { v1, v2, v3, v4, v5 } } } #[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 hello = "Hello"; let s5 = CopyMany { field1: 1, field2: true, field3: hello.to_string() }; assert!(s5.field1() == 1); assert!(s5.field2() == true); assert!(s5.field3() == hello); let gt = GenericType; let buffer: [u8; 12] = [88; 12]; let s6 = Combination::new("Hello", &buffer, >, 64, true); //assert!(s5.skip_me() == "Hello"); assert!(s6.buffer() == &buffer); //assert!(s5.v3() == >); assert!(*s6.keep_me() == 64); assert!(s6.v5() == true) } derive-getters-0.5.0/tests/allref/06-plays-with-others.rs000064400000000000000000000013421046102023000213620ustar 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.5.0/tests/allref/07-dissolve-basic.rs000064400000000000000000000031021046102023000206630ustar 00000000000000//! First tests for new `dissolve` functionality use serde::{Serialize, Deserialize}; use derive_getters::Dissolve; #[derive(Dissolve, Serialize, Deserialize)] struct Empty {} #[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 e = Empty {}; let unit = e.dissolve(); assert!(unit == ()); 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.5.0/tests/allref/08-dissolve-generic-and-ref.rs000064400000000000000000000026771046102023000225510ustar 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.5.0/tests/allref/09-dissolve-tuple-struct.rs000064400000000000000000000017551046102023000222730ustar 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.5.0/tests/allref/10-dissolve-unit-struct.rs000064400000000000000000000001651046102023000221030ustar 00000000000000use derive_getters::Dissolve; #[derive(Debug, Copy, Clone, Dissolve)] struct Unit; fn main() { let _ = Unit; } derive-getters-0.5.0/tests/allref/10-dissolve-unit-struct.stderr000064400000000000000000000004711046102023000227620ustar 00000000000000error: unit struct has nothing to dissolve --> tests/allref/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.5.0/tests/auto_copy_getters.rs000064400000000000000000000013141046102023000200250ustar 00000000000000#[test] #[cfg(feature = "auto_copy_getters")] fn tests() { let t = trybuild::TestCases::new(); t.pass("tests/autocopy/00-auto-copy.rs"); t.pass("tests/autocopy/01-legacy.rs"); t.pass("tests/autocopy/02-simple-single-generic.rs"); t.pass("tests/autocopy/03-simple-multi-generic.rs"); t.pass("tests/autocopy/04-simple-lifetime-annot.rs"); t.pass("tests/autocopy/05-skip-rename-copy-attributes.rs"); t.pass("tests/autocopy/06-plays-with-others.rs"); t.pass("tests/autocopy/07-dissolve-basic.rs"); t.pass("tests/autocopy/08-dissolve-generic-and-ref.rs"); t.pass("tests/autocopy/09-dissolve-tuple-struct.rs"); t.compile_fail("tests/autocopy/10-dissolve-unit-struct.rs"); } derive-getters-0.5.0/tests/autocopy/00-auto-copy.rs000064400000000000000000000005701046102023000202710ustar 00000000000000use derive_getters::Getters; #[derive(Getters)] pub struct Auto { num: u32, string: String, } fn main() { let num = 0; let string = "Hello".to_string(); let auto = Auto { num, string: string.clone(), }; assert_eq!(num, auto.num()); // getter returns a copy assert_eq!(&string, auto.string()); // getter returns a reference } derive-getters-0.5.0/tests/autocopy/01-legacy.rs000064400000000000000000000037521046102023000176230ustar 00000000000000//! Legacy tests from 0.0.7 version modified for autocopy. 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.5.0/tests/autocopy/02-simple-single-generic.rs000064400000000000000000000025341046102023000225370ustar 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.5.0/tests/autocopy/03-simple-multi-generic.rs000064400000000000000000000041751046102023000224140ustar 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.5.0/tests/autocopy/04-simple-lifetime-annot.rs000064400000000000000000000020661046102023000225610ustar 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.5.0/tests/autocopy/05-skip-rename-copy-attributes.rs000064400000000000000000000055211046102023000237260ustar 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 CopyMany { #[getter(copy)] field1: u64, #[getter(copy)] field2: bool, field3: String, } #[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, #[getter(copy)] v5: bool } impl<'a, 'b, 'c, T> Combination<'a, 'b, 'c, T> { pub fn new(v1: &'a str, v2: &'b [u8], v3: &'c T, v4: u64, v5: bool) -> Self { Combination { v1, v2, v3, v4, v5 } } } #[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 hello = "Hello"; let s5 = CopyMany { field1: 1, field2: true, field3: hello.to_string() }; assert!(s5.field1() == 1); assert!(s5.field2() == true); assert!(s5.field3() == hello); let gt = GenericType; let buffer: [u8; 12] = [88; 12]; let s6 = Combination::new("Hello", &buffer, >, 64, true); //assert!(s5.skip_me() == "Hello"); assert!(s6.buffer() == &buffer); //assert!(s5.v3() == >); assert!(s6.keep_me() == 64); assert!(s6.v5() == true) } derive-getters-0.5.0/tests/autocopy/06-plays-with-others.rs000064400000000000000000000013401046102023000217560ustar 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.5.0/tests/autocopy/07-dissolve-basic.rs000064400000000000000000000031021046102023000212610ustar 00000000000000//! First tests for new `dissolve` functionality use serde::{Serialize, Deserialize}; use derive_getters::Dissolve; #[derive(Dissolve, Serialize, Deserialize)] struct Empty {} #[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 e = Empty {}; let unit = e.dissolve(); assert!(unit == ()); 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.5.0/tests/autocopy/08-dissolve-generic-and-ref.rs000064400000000000000000000026771046102023000231470ustar 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.5.0/tests/autocopy/09-dissolve-tuple-struct.rs000064400000000000000000000017551046102023000226710ustar 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.5.0/tests/autocopy/10-dissolve-unit-struct.rs000064400000000000000000000001651046102023000225010ustar 00000000000000use derive_getters::Dissolve; #[derive(Debug, Copy, Clone, Dissolve)] struct Unit; fn main() { let _ = Unit; } derive-getters-0.5.0/tests/autocopy/10-dissolve-unit-struct.stderr000064400000000000000000000004731046102023000233620ustar 00000000000000error: unit struct has nothing to dissolve --> tests/autocopy/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.5.0/tests/lib.rs000064400000000000000000000003041046102023000150320ustar 00000000000000#[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"); }