From 753bffc2f8455195919c957ad55d1681f81db9a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jan=20Czocha=C5=84ski?= Date: Tue, 20 Jun 2023 19:59:07 +0200 Subject: [PATCH 1/6] Migrate to `syn` version `2.0` --- Cargo.lock | 31 ++-- core/Cargo.toml | 2 +- core/src/helpers.rs | 317 ++++++++++++++++++++++++++++++++++++ core/src/language/kotlin.rs | 2 +- core/src/language/scala.rs | 2 +- core/src/language/swift.rs | 2 +- core/src/lib.rs | 1 + core/src/parser.rs | 122 +++++++------- 8 files changed, 404 insertions(+), 75 deletions(-) create mode 100644 core/src/helpers.rs diff --git a/Cargo.lock b/Cargo.lock index ec4d558b..3d5de458 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -402,18 +402,18 @@ checksum = "9ff7415e9ae3fff1225851df9e0d9e4e5479f947619774677a63572e55e80eff" [[package]] name = "proc-macro2" -version = "1.0.44" +version = "1.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bd7356a8122b6c4a24a82b278680c73357984ca2fc79a0f9fa6dea7dced7c58" +checksum = "dec2b086b7a862cf4de201096214fa870344cf922b2b30c167badb3af3195406" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.21" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" +checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488" dependencies = [ "proc-macro2", ] @@ -497,7 +497,7 @@ checksum = "81fa1584d3d1bcacd84c277a0dfe21f5b0f6accf4a23d04d4c6d61f1af522b4c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.100", ] [[package]] @@ -528,6 +528,17 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "syn" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32d41677bcbe24c20c52e7c70b0d8db04134c5d1066bf98662e2871ad200ea3e" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "termcolor" version = "1.1.3" @@ -560,7 +571,7 @@ checksum = "f8b463991b4eab2d801e724172285ec4195c650e8ec79b149e6c2a8e6dd3f783" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.100", ] [[package]] @@ -596,7 +607,7 @@ name = "typeshare-annotation" version = "1.0.2" dependencies = [ "quote", - "syn", + "syn 1.0.100", ] [[package]] @@ -625,7 +636,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn", + "syn 2.0.18", "thiserror", ] @@ -667,7 +678,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn", + "syn 1.0.100", "wasm-bindgen-shared", ] @@ -689,7 +700,7 @@ checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.100", "wasm-bindgen-backend", "wasm-bindgen-shared", ] diff --git a/core/Cargo.toml b/core/Cargo.toml index cfdfb8b7..f64606f6 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -10,7 +10,7 @@ repository = "https://github.com/1Password/typeshare" [dependencies] proc-macro2 = "1" quote = "1" -syn = { version = "1.0", features = ["full"] } +syn = { version = "2.0.18", features = ["full"] } thiserror = "1" itertools = "0.10" lazy_format = "1.8" diff --git a/core/src/helpers.rs b/core/src/helpers.rs new file mode 100644 index 00000000..4026bae6 --- /dev/null +++ b/core/src/helpers.rs @@ -0,0 +1,317 @@ +use std::collections::{BTreeSet, HashMap, HashSet}; + +use proc_macro2::Ident; +use syn::{ + ext::IdentExt, parse::ParseBuffer, punctuated::Punctuated, Attribute, Expr, ExprLit, LitStr, + Meta, MetaList, MetaNameValue, Token, +}; + +use crate::{ + language::SupportedLanguage, + rename::RenameExt, + rust_types::{FieldDecorator, Id}, +}; + +const SERDE: &str = "serde"; +const TYPESHARE: &str = "typeshare"; + +/// Checks the given attrs for `#[typeshare]` +pub(crate) fn has_typeshare_annotation(attrs: &[syn::Attribute]) -> bool { + attrs + .iter() + .flat_map(|attr| attr.path().segments.clone()) + .any(|segment| segment.ident == TYPESHARE) +} + +pub(crate) fn serde_rename_all(attrs: &[syn::Attribute]) -> Option { + get_serde_name_value_meta_items(attrs, "rename_all").next() +} + +pub(crate) fn get_serde_name_value_meta_items<'a>( + attrs: &'a [syn::Attribute], + name: &'a str, +) -> impl Iterator + 'a { + attrs.iter().flat_map(move |attr| { + get_serde_meta_items(attr) + .iter() + .filter_map(|arg| match arg { + Meta::NameValue(name_value) if name_value.path.is_ident(name) => { + expr_to_string(&name_value.value) + } + _ => None, + }) + .collect::>() + }) +} + +// TODO: for now, this is a workaround until we can integrate serde_derive_internal +// into our parser. +/// Returns all arguments passed into `#[serde(...)]` attributes +pub(crate) fn get_serde_meta_items(attr: &syn::Attribute) -> Vec { + if attr.path().is_ident(SERDE) { + attr.parse_args_with(Punctuated::::parse_terminated) + .unwrap() + .iter() + .cloned() + .collect() + } else { + Vec::default() + } +} + +pub(crate) fn get_serialized_as_type(attrs: &[syn::Attribute]) -> Option { + get_typeshare_name_value_meta_items(attrs, "serialized_as").next() +} + +pub(crate) fn get_field_type_override(attrs: &[syn::Attribute]) -> Option { + get_typeshare_name_value_meta_items(attrs, "serialized_as").next() +} + +pub(crate) fn get_typeshare_name_value_meta_items<'a>( + attrs: &'a [syn::Attribute], + name: &'a str, +) -> impl Iterator + 'a { + attrs.iter().flat_map(move |attr| { + get_typeshare_meta_items(attr) + .iter() + .filter_map(|arg| match arg { + Meta::NameValue(name_value) if name_value.path.is_ident(name) => { + expr_to_string(&name_value.value) + } + _ => None, + }) + .collect::>() + }) +} + +/// Returns all arguments passed into `#[typeshare(...)]` attributes +pub(crate) fn get_typeshare_meta_items(attr: &syn::Attribute) -> Vec { + if attr.path().is_ident(TYPESHARE) { + attr.parse_args_with(Punctuated::::parse_terminated) + .iter() + .flat_map(|meta| meta.iter()) + .cloned() + .collect() + } else { + Vec::default() + } +} + +pub(crate) fn get_ident( + ident: Option<&proc_macro2::Ident>, + attrs: &[syn::Attribute], + rename_all: &Option, +) -> Id { + let original = ident.map_or("???".to_string(), |id| id.to_string().replace("r#", "")); + + let mut renamed = rename_all_to_case(original.clone(), rename_all); + + if let Some(s) = serde_rename(attrs) { + renamed = s; + } + + Id { original, renamed } +} + +pub(crate) fn rename_all_to_case(original: String, case: &Option) -> String { + match case { + None => original, + Some(value) => match value.as_str() { + "lowercase" => original.to_lowercase(), + "UPPERCASE" => original.to_uppercase(), + "PascalCase" => original.to_pascal_case(), + "camelCase" => original.to_camel_case(), + "snake_case" => original.to_snake_case(), + "SCREAMING_SNAKE_CASE" => original.to_screaming_snake_case(), + "kebab-case" => original.to_kebab_case(), + "SCREAMING-KEBAB-CASE" => original.to_screaming_kebab_case(), + _ => original, + }, + } +} + +pub(crate) fn serde_rename(attrs: &[syn::Attribute]) -> Option { + get_serde_name_value_meta_items(attrs, "rename").next() +} + +/// Parses any comment out of the given slice of attributes +pub(crate) fn parse_comment_attrs(attrs: &[Attribute]) -> Vec { + attrs + .iter() + .map(|attr| attr.meta.clone()) + .filter_map(|meta| match meta { + Meta::NameValue(name_value) if name_value.path.is_ident("doc") => { + expr_to_string(&name_value.value) + } + _ => None, + }) + .collect() +} + +// `#[typeshare(skip)]` or `#[serde(skip)]` +pub(crate) fn is_skipped(attrs: &[syn::Attribute]) -> bool { + attrs.iter().any(|attr| { + get_serde_meta_items(attr) + .into_iter() + .chain(get_typeshare_meta_items(attr).into_iter()) + .any(|arg| matches!(arg, Meta::Path(path) if path.is_ident("skip"))) + }) +} + +fn serde_attr(attrs: &[syn::Attribute], ident: &str) -> bool { + attrs.iter().any(|attr| { + get_serde_meta_items(attr) + .iter() + .any(|arg| matches!(arg, Meta::Path(path) if path.is_ident(ident))) + }) +} + +pub(crate) fn serde_default(attrs: &[syn::Attribute]) -> bool { + serde_attr(attrs, "default") +} + +pub(crate) fn serde_flatten(attrs: &[syn::Attribute]) -> bool { + serde_attr(attrs, "flatten") +} + +/// Checks the struct or enum for decorators like `#[typeshare(typescript(readonly)]` +/// Takes a slice of `syn::Attribute`, returns a `HashMap>`, where `language` is `SupportedLanguage` +/// and `decorator` is `FieldDecorator`. Field decorators are ordered in a `BTreeSet` for consistent code generation. +pub(crate) fn get_field_decorators( + attrs: &[Attribute], +) -> HashMap> { + let languages: HashSet = SupportedLanguage::all_languages().collect(); + + attrs + .iter() + .flat_map(get_typeshare_meta_items) + .flat_map(|meta| { + if let Meta::List(list) = meta { + Some(list) + } else { + None + } + }) + .flat_map(|list: MetaList| match list.path.get_ident() { + Some(ident) if languages.contains(&ident.try_into().unwrap()) => { + Some((ident.try_into().unwrap(), list)) + } + _ => None, + }) + .map(|(language, list): (SupportedLanguage, MetaList)| { + ( + language, + list.parse_args_with(|input: &ParseBuffer| { + let mut res: Vec = vec![]; + + loop { + if input.is_empty() { + break; + } + + let ident = input.call(Ident::parse_any)?; + + // Parse `readonly` or any other single ident optionally followed by a comma + if input.peek(Token![,]) || input.is_empty() { + input.parse::().unwrap_or_default(); + res.push(Meta::Path(ident.into())); + continue; + } + + if input.is_empty() { + break; + } + + // Parse `= "any | undefined"` or any other eq sign followed by a string literal + + let eq_token = input.parse::()?; + + let value: LitStr = input.parse()?; + res.push(Meta::NameValue(MetaNameValue { + path: ident.into(), + eq_token, + value: Expr::Lit(ExprLit { + attrs: Vec::new(), + lit: value.into(), + }), + })); + + if input.is_empty() { + break; + } + + input.parse::()?; + } + Ok(res) + }) + .iter() + .flatten() + .filter_map(|nested| match nested { + Meta::Path(path) if path.segments.len() == 1 => { + Some(FieldDecorator::Word(path.get_ident()?.to_string())) + } + Meta::NameValue(name_value) => Some(FieldDecorator::NameValue( + name_value.path.get_ident()?.to_string(), + expr_to_string(&name_value.value)?, + )), + // TODO: this should throw a visible error since it suggests a malformed + // attribute. + _ => None, + }) + .collect::>(), + ) + }) + .fold(HashMap::new(), |mut acc, (language, decorators)| { + acc.entry(language).or_default().extend(decorators); + acc + }) +} + +fn expr_to_string(expr: &Expr) -> Option { + match expr { + Expr::Lit(expr_lit) => literal_to_string(&expr_lit.lit), + _ => None, + } +} + +fn literal_to_string(lit: &syn::Lit) -> Option { + match lit { + syn::Lit::Str(str) => Some(str.value().trim().to_string()), + _ => None, + } +} + +/// Checks the struct or enum for decorators like `#[typeshare(swift = "Codable, Equatable")]` +/// Takes a slice of `syn::Attribute`, returns a `HashMap>`, where `language` is `SupportedLanguage` and `decoration_words` is `String` +pub(crate) fn get_decorators(attrs: &[syn::Attribute]) -> HashMap> { + // The resulting HashMap, Key is the language, and the value is a vector of decorators words that will be put onto structures + let mut out: HashMap> = HashMap::new(); + + for value in get_typeshare_name_value_meta_items(attrs, "swift") { + let decorators: Vec = value.split(',').map(|s| s.trim().to_string()).collect(); + + // lastly, get the entry in the hashmap output and extend the value, or insert what we have already found + let decs = out.entry(SupportedLanguage::Swift).or_insert_with(Vec::new); + decs.extend(decorators); + // Sorting so all the added decorators will be after the normal ([`String`], `Codable`) in alphabetical order + decs.sort_unstable(); + decs.dedup(); //removing any duplicates just in case + } + + //return our hashmap mapping of language -> Vec + out +} + +pub(crate) fn get_tag_key(attrs: &[syn::Attribute]) -> Option { + get_serde_name_value_meta_items(attrs, "tag").next() +} + +pub(crate) fn get_content_key(attrs: &[syn::Attribute]) -> Option { + get_serde_name_value_meta_items(attrs, "content").next() +} + +/// Removes `-` characters from identifiers +pub(crate) fn remove_dash_from_identifier(name: &str) -> String { + // Dashes are not valid in identifiers, so we map them to underscores + name.replace('-', "_") +} diff --git a/core/src/language/kotlin.rs b/core/src/language/kotlin.rs index cd0e22b2..f1583439 100644 --- a/core/src/language/kotlin.rs +++ b/core/src/language/kotlin.rs @@ -2,7 +2,7 @@ use super::Language; use crate::language::SupportedLanguage; use crate::rust_types::{RustTypeFormatError, SpecialRustType}; use crate::{ - parser::remove_dash_from_identifier, + helpers::remove_dash_from_identifier, rename::RenameExt, rust_types::{RustEnum, RustEnumVariant, RustField, RustStruct, RustTypeAlias}, }; diff --git a/core/src/language/scala.rs b/core/src/language/scala.rs index c086bd4b..2fe764c0 100644 --- a/core/src/language/scala.rs +++ b/core/src/language/scala.rs @@ -3,7 +3,7 @@ use crate::language::SupportedLanguage; use crate::parser::ParsedData; use crate::rust_types::{RustType, RustTypeFormatError, SpecialRustType}; use crate::{ - parser::remove_dash_from_identifier, + helpers::remove_dash_from_identifier, rust_types::{RustEnum, RustEnumVariant, RustField, RustStruct, RustTypeAlias}, }; use itertools::Itertools; diff --git a/core/src/language/swift.rs b/core/src/language/swift.rs index 06c23c89..8e1da1e5 100644 --- a/core/src/language/swift.rs +++ b/core/src/language/swift.rs @@ -1,7 +1,7 @@ use crate::rust_types::{RustTypeFormatError, SpecialRustType}; use crate::{ + helpers::remove_dash_from_identifier, language::{Language, SupportedLanguage}, - parser::remove_dash_from_identifier, rename::RenameExt, rust_types::{RustEnum, RustEnumVariant, RustStruct, RustTypeAlias}, }; diff --git a/core/src/lib.rs b/core/src/lib.rs index 65a8c8c3..b8544afc 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -7,6 +7,7 @@ use thiserror::Error; mod rename; +mod helpers; /// Implementations for each language converter pub mod language; /// Parsing Rust code into a format the `language` modules can understand diff --git a/core/src/parser.rs b/core/src/parser.rs index efc7b505..b785efa0 100644 --- a/core/src/parser.rs +++ b/core/src/parser.rs @@ -1,55 +1,21 @@ -use crate::rust_types::FieldDecorator; -use crate::{ - language::SupportedLanguage, - rename::RenameExt, - rust_types::{ - Id, RustEnum, RustEnumShared, RustEnumVariant, RustEnumVariantShared, RustField, RustItem, - RustStruct, RustType, RustTypeAlias, RustTypeParseError, - }, +use crate::helpers::{ + get_content_key, get_decorators, get_field_decorators, get_field_type_override, get_ident, + get_serialized_as_type, get_tag_key, has_typeshare_annotation, is_skipped, parse_comment_attrs, + serde_default, serde_flatten, serde_rename_all, }; -use proc_macro2::{Ident, Span}; -use std::collections::BTreeSet; -use std::{ - collections::{HashMap, HashSet}, - convert::TryFrom, -}; -use syn::{Attribute, Fields, ItemEnum, ItemStruct, ItemType}; -use syn::{GenericParam, Meta, NestedMeta}; -use thiserror::Error; - -// TODO: parsing is very opinionated and makes some decisions that should be -// getting made at code generation time. Fix this. -const SERDE: &str = "serde"; -const TYPESHARE: &str = "typeshare"; +use crate::rust_types::{ + RustEnum, RustEnumShared, RustEnumVariant, RustEnumVariantShared, RustField, RustItem, + RustStruct, RustType, RustTypeAlias, RustTypeParseError, +}; -/// The results of parsing Rust source input. -#[derive(Default, Debug)] -pub struct ParsedData { - /// Structs defined in the source - pub structs: Vec, - /// Enums defined in the source - pub enums: Vec, - /// Type aliases defined in the source - pub aliases: Vec, -} +use std::convert::TryFrom; -impl ParsedData { - /// Add the parsed data from `other` to `self`. - pub fn add(&mut self, mut other: Self) { - self.structs.append(&mut other.structs); - self.enums.append(&mut other.enums); - self.aliases.append(&mut other.aliases); - } +use syn::GenericParam; +use syn::{Fields, Item, ItemEnum, ItemStruct, ItemType}; +use thiserror::Error; - fn push_rust_thing(&mut self, rust_thing: RustItem) { - match rust_thing { - RustItem::Struct(s) => self.structs.push(s), - RustItem::Enum(e) => self.enums.push(e), - RustItem::Alias(a) => self.aliases.push(a), - } - } -} +const TYPESHARE: &str = "typeshare"; /// Errors that can occur while parsing Rust source input. #[derive(Debug, Error)] @@ -79,33 +45,67 @@ pub enum ParseError { SerdeFlattenNotAllowed, } -/// Parse the given Rust source string into `ParsedData`. -pub fn parse(input: &str) -> Result { - let mut parsed_data = ParsedData::default(); +/// The results of parsing Rust source input. +#[derive(Default, Debug)] +pub struct ParsedData { + /// Structs defined in the source + pub structs: Vec, + /// Enums defined in the source + pub enums: Vec, + /// Type aliases defined in the source + pub aliases: Vec, +} - // We will only produce output for files that contain the `#[typeshare]` - // attribute, so this is a quick and easy performance win - if !input.contains("typeshare") { - return Ok(parsed_data); +impl ParsedData { + /// Add the parsed data from `other` to `self`. + pub fn add(&mut self, mut other: Self) { + self.structs.append(&mut other.structs); + self.enums.append(&mut other.enums); + self.aliases.append(&mut other.aliases); } - // Parse and process the input, ensuring we parse only items marked with - // `#[typeshare] - let source = syn::parse_file(input)?; + fn push(&mut self, rust_thing: RustItem) { + match rust_thing { + RustItem::Struct(s) => self.structs.push(s), + RustItem::Enum(e) => self.enums.push(e), + RustItem::Alias(a) => self.aliases.push(a), + } + } - for item in flatten_items(source.items.iter()) { + fn parse(&mut self, item: &Item) -> Result<(), ParseError> { match item { syn::Item::Struct(s) if has_typeshare_annotation(&s.attrs) => { - parsed_data.push_rust_thing(parse_struct(s)?); + self.push(parse_struct(s)?); } syn::Item::Enum(e) if has_typeshare_annotation(&e.attrs) => { - parsed_data.push_rust_thing(parse_enum(e)?); + self.push(parse_enum(e)?); } syn::Item::Type(t) if has_typeshare_annotation(&t.attrs) => { - parsed_data.aliases.push(parse_type_alias(t)?); + self.aliases.push(parse_type_alias(t)?); } _ => {} } + + Ok(()) + } +} + +/// Parse the given Rust source string into `ParsedData`. +pub fn parse(input: &str) -> Result { + let mut parsed_data = ParsedData::default(); + + // We will only produce output for files that contain the `#[typeshare]` + // attribute, so this is a quick and easy performance win + if !input.contains(TYPESHARE) { + return Ok(parsed_data); + } + + // Parse and process the input, ensuring we parse only items marked with + // `#[typeshare]` + let source = syn::parse_file(input)?; + + for item in flatten_items(source.items.iter()) { + parsed_data.parse(item)?; } Ok(parsed_data) From 1b08927008e0dbfe4ca2dcd68f9080a51023b8cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jan=20Czocha=C5=84ski?= Date: Tue, 20 Jun 2023 23:07:20 +0200 Subject: [PATCH 2/6] Remove duplicate helper methods --- core/src/helpers.rs | 89 +++++++++++++++++++++------------------------ 1 file changed, 41 insertions(+), 48 deletions(-) diff --git a/core/src/helpers.rs b/core/src/helpers.rs index 4026bae6..eaa3dcbc 100644 --- a/core/src/helpers.rs +++ b/core/src/helpers.rs @@ -24,55 +24,24 @@ pub(crate) fn has_typeshare_annotation(attrs: &[syn::Attribute]) -> bool { } pub(crate) fn serde_rename_all(attrs: &[syn::Attribute]) -> Option { - get_serde_name_value_meta_items(attrs, "rename_all").next() -} - -pub(crate) fn get_serde_name_value_meta_items<'a>( - attrs: &'a [syn::Attribute], - name: &'a str, -) -> impl Iterator + 'a { - attrs.iter().flat_map(move |attr| { - get_serde_meta_items(attr) - .iter() - .filter_map(|arg| match arg { - Meta::NameValue(name_value) if name_value.path.is_ident(name) => { - expr_to_string(&name_value.value) - } - _ => None, - }) - .collect::>() - }) -} - -// TODO: for now, this is a workaround until we can integrate serde_derive_internal -// into our parser. -/// Returns all arguments passed into `#[serde(...)]` attributes -pub(crate) fn get_serde_meta_items(attr: &syn::Attribute) -> Vec { - if attr.path().is_ident(SERDE) { - attr.parse_args_with(Punctuated::::parse_terminated) - .unwrap() - .iter() - .cloned() - .collect() - } else { - Vec::default() - } + get_name_value_meta_items(attrs, "rename_all", SERDE).next() } pub(crate) fn get_serialized_as_type(attrs: &[syn::Attribute]) -> Option { - get_typeshare_name_value_meta_items(attrs, "serialized_as").next() + get_name_value_meta_items(attrs, "serialized_as", TYPESHARE).next() } pub(crate) fn get_field_type_override(attrs: &[syn::Attribute]) -> Option { - get_typeshare_name_value_meta_items(attrs, "serialized_as").next() + get_name_value_meta_items(attrs, "serialized_as", TYPESHARE).next() } -pub(crate) fn get_typeshare_name_value_meta_items<'a>( +pub(crate) fn get_name_value_meta_items<'a>( attrs: &'a [syn::Attribute], name: &'a str, + ident: &'static str, ) -> impl Iterator + 'a { attrs.iter().flat_map(move |attr| { - get_typeshare_meta_items(attr) + get_meta_items(attr, ident) .iter() .filter_map(|arg| match arg { Meta::NameValue(name_value) if name_value.path.is_ident(name) => { @@ -84,9 +53,9 @@ pub(crate) fn get_typeshare_name_value_meta_items<'a>( }) } -/// Returns all arguments passed into `#[typeshare(...)]` attributes -pub(crate) fn get_typeshare_meta_items(attr: &syn::Attribute) -> Vec { - if attr.path().is_ident(TYPESHARE) { +/// Returns all arguments passed into `#[{ident}(...)]` where `{ident}` can be `serde` or `typeshare` attributes +pub(crate) fn get_meta_items(attr: &syn::Attribute, ident: &str) -> Vec { + if attr.path().is_ident(ident) { attr.parse_args_with(Punctuated::::parse_terminated) .iter() .flat_map(|meta| meta.iter()) @@ -131,7 +100,7 @@ pub(crate) fn rename_all_to_case(original: String, case: &Option) -> Str } pub(crate) fn serde_rename(attrs: &[syn::Attribute]) -> Option { - get_serde_name_value_meta_items(attrs, "rename").next() + get_name_value_meta_items(attrs, "rename", SERDE).next() } /// Parses any comment out of the given slice of attributes @@ -151,16 +120,16 @@ pub(crate) fn parse_comment_attrs(attrs: &[Attribute]) -> Vec { // `#[typeshare(skip)]` or `#[serde(skip)]` pub(crate) fn is_skipped(attrs: &[syn::Attribute]) -> bool { attrs.iter().any(|attr| { - get_serde_meta_items(attr) + get_meta_items(attr, SERDE) .into_iter() - .chain(get_typeshare_meta_items(attr).into_iter()) + .chain(get_meta_items(attr, TYPESHARE).into_iter()) .any(|arg| matches!(arg, Meta::Path(path) if path.is_ident("skip"))) }) } fn serde_attr(attrs: &[syn::Attribute], ident: &str) -> bool { attrs.iter().any(|attr| { - get_serde_meta_items(attr) + get_meta_items(attr, SERDE) .iter() .any(|arg| matches!(arg, Meta::Path(path) if path.is_ident(ident))) }) @@ -184,7 +153,7 @@ pub(crate) fn get_field_decorators( attrs .iter() - .flat_map(get_typeshare_meta_items) + .flat_map(|attr| get_meta_items(attr, TYPESHARE)) .flat_map(|meta| { if let Meta::List(list) = meta { Some(list) @@ -287,7 +256,7 @@ pub(crate) fn get_decorators(attrs: &[syn::Attribute]) -> HashMap> = HashMap::new(); - for value in get_typeshare_name_value_meta_items(attrs, "swift") { + for value in get_name_value_meta_items(attrs, "swift", TYPESHARE) { let decorators: Vec = value.split(',').map(|s| s.trim().to_string()).collect(); // lastly, get the entry in the hashmap output and extend the value, or insert what we have already found @@ -303,11 +272,11 @@ pub(crate) fn get_decorators(attrs: &[syn::Attribute]) -> HashMap Option { - get_serde_name_value_meta_items(attrs, "tag").next() + get_name_value_meta_items(attrs, "tag", SERDE).next() } pub(crate) fn get_content_key(attrs: &[syn::Attribute]) -> Option { - get_serde_name_value_meta_items(attrs, "content").next() + get_name_value_meta_items(attrs, "content", SERDE).next() } /// Removes `-` characters from identifiers @@ -315,3 +284,27 @@ pub(crate) fn remove_dash_from_identifier(name: &str) -> String { // Dashes are not valid in identifiers, so we map them to underscores name.replace('-', "_") } + +#[test] +fn test_rename_all_to_case() { + let test_word = "test_case"; + + let tests = [ + ("lowercase", "test_case"), + ("UPPERCASE", "TEST_CASE"), + ("PascalCase", "TestCase"), + ("camelCase", "testCase"), + ("snake_case", "test_case"), + ("SCREAMING_SNAKE_CASE", "TEST_CASE"), + ("kebab-case", "test-case"), + ("SCREAMING-KEBAB-CASE", "TEST-CASE"), + ("invalid case", "test_case"), + ]; + + for test in tests { + assert_eq!( + rename_all_to_case(test_word.to_string(), &Some(test.0.to_string())), + test.1 + ); + } +} From cf44a31f74624cb7b67e906f33b870274532c593 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jan=20Czocha=C5=84ski?= Date: Sat, 1 Jul 2023 13:39:27 +0200 Subject: [PATCH 3/6] Update crate versions --- Cargo.lock | 59 ++++++++++++++++++------------------------- annotation/Cargo.toml | 2 +- annotation/src/lib.rs | 2 +- cli/Cargo.toml | 2 +- core/Cargo.toml | 2 +- lib/Cargo.toml | 2 +- 6 files changed, 29 insertions(+), 40 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3d5de458..e526df3b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -482,22 +482,22 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "serde" -version = "1.0.145" +version = "1.0.164" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "728eb6351430bccb993660dfffc5a72f91ccc1295abaa8ce19b27ebe4f75568b" +checksum = "9e8c8cf938e98f769bc164923b06dce91cea1751522f46f8466461af04c9027d" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.145" +version = "1.0.164" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81fa1584d3d1bcacd84c277a0dfe21f5b0f6accf4a23d04d4c6d61f1af522b4c" +checksum = "d9735b638ccc51c28bf6914d90a2e9725b377144fc612c49a611fddd1b631d68" dependencies = [ "proc-macro2", "quote", - "syn 1.0.100", + "syn", ] [[package]] @@ -517,17 +517,6 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" -[[package]] -name = "syn" -version = "1.0.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52205623b1b0f064a4e71182c3b18ae902267282930c6d5462c91b859668426e" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - [[package]] name = "syn" version = "2.0.18" @@ -556,22 +545,22 @@ checksum = "949517c0cf1bf4ee812e2e07e08ab448e3ae0d23472aee8a06c985f0c8815b16" [[package]] name = "thiserror" -version = "1.0.35" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c53f98874615aea268107765aa1ed8f6116782501d18e53d08b471733bea6c85" +checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.35" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8b463991b4eab2d801e724172285ec4195c650e8ec79b149e6c2a8e6dd3f783" +checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" dependencies = [ "proc-macro2", "quote", - "syn 1.0.100", + "syn", ] [[package]] @@ -607,7 +596,7 @@ name = "typeshare-annotation" version = "1.0.2" dependencies = [ "quote", - "syn 1.0.100", + "syn", ] [[package]] @@ -636,7 +625,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.18", + "syn", "thiserror", ] @@ -659,9 +648,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.83" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" +checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -669,24 +658,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.83" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" +checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 1.0.100", + "syn", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.83" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" +checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -694,22 +683,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.83" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" +checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", - "syn 1.0.100", + "syn", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.83" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" +checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" [[package]] name = "winapi" diff --git a/annotation/Cargo.toml b/annotation/Cargo.toml index cad2d2d3..cf30a27a 100644 --- a/annotation/Cargo.toml +++ b/annotation/Cargo.toml @@ -11,5 +11,5 @@ repository = "https://github.com/1Password/typeshare" proc-macro = true [dependencies] -syn = { version = "1.0", features = ["parsing", "proc-macro"] } +syn = { version = "2.0.18", features = ["parsing", "proc-macro"] } quote = "1.0" diff --git a/annotation/src/lib.rs b/annotation/src/lib.rs index 996b50d8..71d9a0d5 100644 --- a/annotation/src/lib.rs +++ b/annotation/src/lib.rs @@ -54,7 +54,7 @@ fn strip_configuration_attribute(item: &mut DeriveInput) { fn remove_configuration_from_attributes(attributes: &mut Vec) { const CONFIG_ATTRIBUTE_NAME: &str = "typeshare"; - attributes.retain(|x| x.path.to_token_stream().to_string() != CONFIG_ATTRIBUTE_NAME); + attributes.retain(|x| x.path().to_token_stream().to_string() != CONFIG_ATTRIBUTE_NAME); } fn remove_configuration_from_fields(fields: &mut Fields) { diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 1b41b11a..68dbf8ca 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -21,6 +21,6 @@ clap_complete_command = "0.3" ignore = "0.4" once_cell = "1" rayon = "1.5" -serde = { version = "1.0", features = ["derive"] } +serde = { version = "1.0.164", features = ["derive"] } toml = "0.5" typeshare-core = { path = "../core", version = "1.7.0" } diff --git a/core/Cargo.toml b/core/Cargo.toml index f64606f6..ad2a3677 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -11,7 +11,7 @@ repository = "https://github.com/1Password/typeshare" proc-macro2 = "1" quote = "1" syn = { version = "2.0.18", features = ["full"] } -thiserror = "1" +thiserror = "1.0.40" itertools = "0.10" lazy_format = "1.8" joinery = "2" diff --git a/lib/Cargo.toml b/lib/Cargo.toml index 425afa89..085f5934 100644 --- a/lib/Cargo.toml +++ b/lib/Cargo.toml @@ -10,6 +10,6 @@ repository = "https://github.com/1Password/typeshare" [dependencies] chrono = { version = "0.4", default-features = false, features = ["clock", "std", "wasmbind"] } -serde = { version = "1", features = ["derive"] } +serde = { version = "1.0.164", features = ["derive"] } serde_json = "1" typeshare-annotation = { path = "../annotation", version = "1.0.0" } From b0a6d21cd0d33873d337caba72a484e06f418fb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jan=20Czocha=C5=84ski?= Date: Tue, 30 Jan 2024 14:47:31 +0100 Subject: [PATCH 4/6] Rebase fixes --- core/src/helpers.rs | 4 +- core/src/parser.rs | 337 -------------------------------------------- 2 files changed, 2 insertions(+), 339 deletions(-) diff --git a/core/src/helpers.rs b/core/src/helpers.rs index eaa3dcbc..6aadc040 100644 --- a/core/src/helpers.rs +++ b/core/src/helpers.rs @@ -122,7 +122,7 @@ pub(crate) fn is_skipped(attrs: &[syn::Attribute]) -> bool { attrs.iter().any(|attr| { get_meta_items(attr, SERDE) .into_iter() - .chain(get_meta_items(attr, TYPESHARE).into_iter()) + .chain(get_meta_items(attr, TYPESHARE)) .any(|arg| matches!(arg, Meta::Path(path) if path.is_ident("skip"))) }) } @@ -260,7 +260,7 @@ pub(crate) fn get_decorators(attrs: &[syn::Attribute]) -> HashMap = value.split(',').map(|s| s.trim().to_string()).collect(); // lastly, get the entry in the hashmap output and extend the value, or insert what we have already found - let decs = out.entry(SupportedLanguage::Swift).or_insert_with(Vec::new); + let decs = out.entry(SupportedLanguage::Swift).or_default(); decs.extend(decorators); // Sorting so all the added decorators will be after the normal ([`String`], `Codable`) in alphabetical order decs.sort_unstable(); diff --git a/core/src/parser.rs b/core/src/parser.rs index b785efa0..272a027a 100644 --- a/core/src/parser.rs +++ b/core/src/parser.rs @@ -416,340 +416,3 @@ fn parse_type_alias(t: &ItemType) -> Result { generic_types, }) } - -// Helpers - -/// Parses any comment out of the given slice of attributes -fn parse_comment_attrs(attrs: &[Attribute]) -> Vec { - const DOC_ATTR: &str = "doc"; - attrs - .iter() - .map(Attribute::parse_meta) - .filter_map(Result::ok) - .filter_map(|attr| match attr { - Meta::NameValue(name_value) => { - if let Some(ident) = name_value.path.get_ident() { - if *ident == DOC_ATTR { - Some(name_value.lit) - } else { - None - } - } else { - None - } - } - _ => None, - }) - .filter_map(literal_as_string) - .map(|string| string.trim().into()) - .collect() -} - -/// Checks the given attrs for `#[typeshare]` -fn has_typeshare_annotation(attrs: &[syn::Attribute]) -> bool { - let typeshare_ident = Ident::new("typeshare", Span::call_site()); - for a in attrs { - if let Some(segment) = a.path.segments.iter().next() { - if segment.ident == typeshare_ident { - return true; - } - } - } - - false -} - -fn get_ident( - ident: Option<&proc_macro2::Ident>, - attrs: &[syn::Attribute], - rename_all: &Option, -) -> Id { - let original = ident.map_or("???".to_string(), |id| id.to_string().replace("r#", "")); - - let mut renamed = rename_all_to_case(original.clone(), rename_all); - - if let Some(s) = serde_rename(attrs) { - renamed = s; - } - - Id { original, renamed } -} - -fn rename_all_to_case(original: String, case: &Option) -> String { - match case { - None => original, - Some(value) => match value.as_str() { - "lowercase" => original.to_lowercase(), - "UPPERCASE" => original.to_uppercase(), - "PascalCase" => original.to_pascal_case(), - "camelCase" => original.to_camel_case(), - "snake_case" => original.to_snake_case(), - "SCREAMING_SNAKE_CASE" => original.to_screaming_snake_case(), - "kebab-case" => original.to_kebab_case(), - "SCREAMING-KEBAB-CASE" => original.to_screaming_kebab_case(), - _ => original, - }, - } -} - -fn literal_as_string(lit: syn::Lit) -> Option { - match lit { - syn::Lit::Str(str) => Some(str.value()), - _ => None, - } -} - -fn get_typeshare_name_value_meta_items<'a>( - attrs: &'a [syn::Attribute], - name: &'a str, -) -> impl Iterator + 'a { - attrs.iter().flat_map(move |attr| { - get_typeshare_meta_items(attr) - .iter() - .filter_map(|arg| match arg { - NestedMeta::Meta(Meta::NameValue(name_value)) => { - if let Some(ident) = name_value.path.get_ident() { - if *ident == name { - Some(name_value.lit.clone()) - } else { - None - } - } else { - None - } - } - _ => None, - }) - .collect::>() - }) -} - -fn get_serde_name_value_meta_items<'a>( - attrs: &'a [syn::Attribute], - name: &'a str, -) -> impl Iterator + 'a { - attrs.iter().flat_map(move |attr| { - get_serde_meta_items(attr) - .iter() - .filter_map(|arg| match arg { - NestedMeta::Meta(Meta::NameValue(name_value)) => { - if let Some(ident) = name_value.path.get_ident() { - if *ident == name { - Some(name_value.lit.clone()) - } else { - None - } - } else { - None - } - } - _ => None, - }) - .collect::>() - }) -} - -fn get_serialized_as_type(attrs: &[syn::Attribute]) -> Option { - get_typeshare_name_value_meta_items(attrs, "serialized_as") - .next() - .and_then(literal_as_string) -} - -fn get_field_type_override(attrs: &[syn::Attribute]) -> Option { - get_typeshare_name_value_meta_items(attrs, "serialized_as") - .next() - .and_then(literal_as_string) -} - -/// Checks the struct or enum for decorators like `#[typeshare(typescript(readonly)]` -/// Takes a slice of `syn::Attribute`, returns a `HashMap>`, where `language` is `SupportedLanguage` -/// and `decorator` is `FieldDecorator`. Field decorators are ordered in a `BTreeSet` for consistent code generation. -fn get_field_decorators( - attrs: &[Attribute], -) -> HashMap> { - let languages: HashSet = SupportedLanguage::all_languages().collect(); - - attrs - .iter() - .flat_map(get_typeshare_meta_items) - .flat_map(|meta| { - if let NestedMeta::Meta(Meta::List(list)) = meta { - Some(list) - } else { - None - } - }) - .flat_map(|list| match list.path.get_ident() { - Some(ident) if languages.contains(&ident.try_into().unwrap()) => { - Some((ident.try_into().unwrap(), list.nested)) - } - _ => None, - }) - .map(|(language, list)| { - ( - language, - list.into_iter().filter_map(|nested| match nested { - NestedMeta::Meta(Meta::Path(path)) if path.segments.len() == 1 => { - Some(FieldDecorator::Word(path.get_ident()?.to_string())) - } - NestedMeta::Meta(Meta::NameValue(name_value)) => { - Some(FieldDecorator::NameValue( - name_value.path.get_ident()?.to_string(), - literal_as_string(name_value.lit)?, - )) - } - // TODO: this should throw a visible error since it suggests a malformed - // attribute. - _ => None, - }), - ) - }) - .fold(HashMap::new(), |mut acc, (language, decorators)| { - acc.entry(language).or_default().extend(decorators); - acc - }) -} - -/// Checks the struct or enum for decorators like `#[typeshare(swift = "Codable, Equatable")]` -/// Takes a slice of `syn::Attribute`, returns a `HashMap>`, where `language` is `SupportedLanguage` and `decoration_words` is `String` -fn get_decorators(attrs: &[syn::Attribute]) -> HashMap> { - // The resulting HashMap, Key is the language, and the value is a vector of decorators words that will be put onto structures - let mut out: HashMap> = HashMap::new(); - - for value in get_typeshare_name_value_meta_items(attrs, "swift").filter_map(literal_as_string) { - let decorators: Vec = value.split(',').map(|s| s.trim().to_string()).collect(); - - // lastly, get the entry in the hashmap output and extend the value, or insert what we have already found - let decs = out.entry(SupportedLanguage::Swift).or_default(); - decs.extend(decorators); - // Sorting so all the added decorators will be after the normal ([`String`], `Codable`) in alphabetical order - decs.sort_unstable(); - decs.dedup(); //removing any duplicates just in case - } - - //return our hashmap mapping of language -> Vec - out -} - -fn get_tag_key(attrs: &[syn::Attribute]) -> Option { - get_serde_name_value_meta_items(attrs, "tag") - .next() - .and_then(literal_as_string) -} - -fn get_content_key(attrs: &[syn::Attribute]) -> Option { - get_serde_name_value_meta_items(attrs, "content") - .next() - .and_then(literal_as_string) -} - -fn serde_rename(attrs: &[syn::Attribute]) -> Option { - get_serde_name_value_meta_items(attrs, "rename") - .next() - .and_then(literal_as_string) -} - -fn serde_rename_all(attrs: &[syn::Attribute]) -> Option { - get_serde_name_value_meta_items(attrs, "rename_all") - .next() - .and_then(literal_as_string) -} - -fn serde_attr(attrs: &[syn::Attribute], ident: &Ident) -> bool { - attrs.iter().any(|attr| { - get_serde_meta_items(attr).iter().any(|arg| match arg { - NestedMeta::Meta(Meta::Path(path)) => { - if let Some(this_ident) = path.get_ident() { - *this_ident == *ident - } else { - false - } - } - _ => false, - }) - }) -} - -fn serde_default(attrs: &[syn::Attribute]) -> bool { - serde_attr(attrs, &Ident::new("default", Span::call_site())) -} - -fn serde_flatten(attrs: &[syn::Attribute]) -> bool { - serde_attr(attrs, &Ident::new("flatten", Span::call_site())) -} - -// TODO: for now, this is a workaround until we can integrate serde_derive_internal -// into our parser. -/// Returns all arguments passed into `#[serde(...)]` attributes -pub fn get_serde_meta_items(attr: &syn::Attribute) -> Vec { - if attr.path.get_ident().is_none() || *attr.path.get_ident().unwrap() != SERDE { - return Vec::default(); - } - - match attr.parse_meta() { - Ok(Meta::List(meta)) => meta.nested.into_iter().collect(), - _ => Vec::new(), - } -} - -/// Returns all arguments passed into `#[typeshare(...)]` attributes -pub fn get_typeshare_meta_items(attr: &syn::Attribute) -> Vec { - if attr.path.get_ident().is_none() || *attr.path.get_ident().unwrap() != TYPESHARE { - return Vec::default(); - } - - match attr.parse_meta() { - Ok(Meta::List(meta)) => meta.nested.into_iter().collect(), - _ => Vec::new(), - } -} - -// `#[typeshare(skip)]` or `#[serde(skip)]` -fn is_skipped(attrs: &[syn::Attribute]) -> bool { - let skip = Ident::new("skip", Span::call_site()); - attrs.iter().any(|attr| { - get_serde_meta_items(attr) - .into_iter() - .chain(get_typeshare_meta_items(attr)) - .any(|arg| match arg { - NestedMeta::Meta(Meta::Path(path)) => { - if let Some(ident) = path.get_ident() { - *ident == skip - } else { - false - } - } - _ => false, - }) - }) -} - -#[test] -fn test_rename_all_to_case() { - let test_word = "test_case"; - - let tests = [ - ("lowercase", "test_case"), - ("UPPERCASE", "TEST_CASE"), - ("PascalCase", "TestCase"), - ("camelCase", "testCase"), - ("snake_case", "test_case"), - ("SCREAMING_SNAKE_CASE", "TEST_CASE"), - ("kebab-case", "test-case"), - ("SCREAMING-KEBAB-CASE", "TEST-CASE"), - ("invalid case", "test_case"), - ]; - - for test in tests { - assert_eq!( - rename_all_to_case(test_word.to_string(), &Some(test.0.to_string())), - test.1 - ); - } -} - -/// Removes `-` characters from identifiers -pub(crate) fn remove_dash_from_identifier(name: &str) -> String { - // Dashes are not valid in identifiers, so we map them to underscores - name.replace('-', "_") -} From 32f9a87a27a659db7f219268d36787573238477d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jan=20Czocha=C5=84ski?= Date: Thu, 1 Feb 2024 17:34:53 +0100 Subject: [PATCH 5/6] Revert helper extraction --- core/src/helpers.rs | 310 ---------------------------------- core/src/language/kotlin.rs | 2 +- core/src/language/scala.rs | 7 +- core/src/language/swift.rs | 2 +- core/src/lib.rs | 1 - core/src/parser.rs | 321 ++++++++++++++++++++++++++++++++++-- 6 files changed, 315 insertions(+), 328 deletions(-) delete mode 100644 core/src/helpers.rs diff --git a/core/src/helpers.rs b/core/src/helpers.rs deleted file mode 100644 index 6aadc040..00000000 --- a/core/src/helpers.rs +++ /dev/null @@ -1,310 +0,0 @@ -use std::collections::{BTreeSet, HashMap, HashSet}; - -use proc_macro2::Ident; -use syn::{ - ext::IdentExt, parse::ParseBuffer, punctuated::Punctuated, Attribute, Expr, ExprLit, LitStr, - Meta, MetaList, MetaNameValue, Token, -}; - -use crate::{ - language::SupportedLanguage, - rename::RenameExt, - rust_types::{FieldDecorator, Id}, -}; - -const SERDE: &str = "serde"; -const TYPESHARE: &str = "typeshare"; - -/// Checks the given attrs for `#[typeshare]` -pub(crate) fn has_typeshare_annotation(attrs: &[syn::Attribute]) -> bool { - attrs - .iter() - .flat_map(|attr| attr.path().segments.clone()) - .any(|segment| segment.ident == TYPESHARE) -} - -pub(crate) fn serde_rename_all(attrs: &[syn::Attribute]) -> Option { - get_name_value_meta_items(attrs, "rename_all", SERDE).next() -} - -pub(crate) fn get_serialized_as_type(attrs: &[syn::Attribute]) -> Option { - get_name_value_meta_items(attrs, "serialized_as", TYPESHARE).next() -} - -pub(crate) fn get_field_type_override(attrs: &[syn::Attribute]) -> Option { - get_name_value_meta_items(attrs, "serialized_as", TYPESHARE).next() -} - -pub(crate) fn get_name_value_meta_items<'a>( - attrs: &'a [syn::Attribute], - name: &'a str, - ident: &'static str, -) -> impl Iterator + 'a { - attrs.iter().flat_map(move |attr| { - get_meta_items(attr, ident) - .iter() - .filter_map(|arg| match arg { - Meta::NameValue(name_value) if name_value.path.is_ident(name) => { - expr_to_string(&name_value.value) - } - _ => None, - }) - .collect::>() - }) -} - -/// Returns all arguments passed into `#[{ident}(...)]` where `{ident}` can be `serde` or `typeshare` attributes -pub(crate) fn get_meta_items(attr: &syn::Attribute, ident: &str) -> Vec { - if attr.path().is_ident(ident) { - attr.parse_args_with(Punctuated::::parse_terminated) - .iter() - .flat_map(|meta| meta.iter()) - .cloned() - .collect() - } else { - Vec::default() - } -} - -pub(crate) fn get_ident( - ident: Option<&proc_macro2::Ident>, - attrs: &[syn::Attribute], - rename_all: &Option, -) -> Id { - let original = ident.map_or("???".to_string(), |id| id.to_string().replace("r#", "")); - - let mut renamed = rename_all_to_case(original.clone(), rename_all); - - if let Some(s) = serde_rename(attrs) { - renamed = s; - } - - Id { original, renamed } -} - -pub(crate) fn rename_all_to_case(original: String, case: &Option) -> String { - match case { - None => original, - Some(value) => match value.as_str() { - "lowercase" => original.to_lowercase(), - "UPPERCASE" => original.to_uppercase(), - "PascalCase" => original.to_pascal_case(), - "camelCase" => original.to_camel_case(), - "snake_case" => original.to_snake_case(), - "SCREAMING_SNAKE_CASE" => original.to_screaming_snake_case(), - "kebab-case" => original.to_kebab_case(), - "SCREAMING-KEBAB-CASE" => original.to_screaming_kebab_case(), - _ => original, - }, - } -} - -pub(crate) fn serde_rename(attrs: &[syn::Attribute]) -> Option { - get_name_value_meta_items(attrs, "rename", SERDE).next() -} - -/// Parses any comment out of the given slice of attributes -pub(crate) fn parse_comment_attrs(attrs: &[Attribute]) -> Vec { - attrs - .iter() - .map(|attr| attr.meta.clone()) - .filter_map(|meta| match meta { - Meta::NameValue(name_value) if name_value.path.is_ident("doc") => { - expr_to_string(&name_value.value) - } - _ => None, - }) - .collect() -} - -// `#[typeshare(skip)]` or `#[serde(skip)]` -pub(crate) fn is_skipped(attrs: &[syn::Attribute]) -> bool { - attrs.iter().any(|attr| { - get_meta_items(attr, SERDE) - .into_iter() - .chain(get_meta_items(attr, TYPESHARE)) - .any(|arg| matches!(arg, Meta::Path(path) if path.is_ident("skip"))) - }) -} - -fn serde_attr(attrs: &[syn::Attribute], ident: &str) -> bool { - attrs.iter().any(|attr| { - get_meta_items(attr, SERDE) - .iter() - .any(|arg| matches!(arg, Meta::Path(path) if path.is_ident(ident))) - }) -} - -pub(crate) fn serde_default(attrs: &[syn::Attribute]) -> bool { - serde_attr(attrs, "default") -} - -pub(crate) fn serde_flatten(attrs: &[syn::Attribute]) -> bool { - serde_attr(attrs, "flatten") -} - -/// Checks the struct or enum for decorators like `#[typeshare(typescript(readonly)]` -/// Takes a slice of `syn::Attribute`, returns a `HashMap>`, where `language` is `SupportedLanguage` -/// and `decorator` is `FieldDecorator`. Field decorators are ordered in a `BTreeSet` for consistent code generation. -pub(crate) fn get_field_decorators( - attrs: &[Attribute], -) -> HashMap> { - let languages: HashSet = SupportedLanguage::all_languages().collect(); - - attrs - .iter() - .flat_map(|attr| get_meta_items(attr, TYPESHARE)) - .flat_map(|meta| { - if let Meta::List(list) = meta { - Some(list) - } else { - None - } - }) - .flat_map(|list: MetaList| match list.path.get_ident() { - Some(ident) if languages.contains(&ident.try_into().unwrap()) => { - Some((ident.try_into().unwrap(), list)) - } - _ => None, - }) - .map(|(language, list): (SupportedLanguage, MetaList)| { - ( - language, - list.parse_args_with(|input: &ParseBuffer| { - let mut res: Vec = vec![]; - - loop { - if input.is_empty() { - break; - } - - let ident = input.call(Ident::parse_any)?; - - // Parse `readonly` or any other single ident optionally followed by a comma - if input.peek(Token![,]) || input.is_empty() { - input.parse::().unwrap_or_default(); - res.push(Meta::Path(ident.into())); - continue; - } - - if input.is_empty() { - break; - } - - // Parse `= "any | undefined"` or any other eq sign followed by a string literal - - let eq_token = input.parse::()?; - - let value: LitStr = input.parse()?; - res.push(Meta::NameValue(MetaNameValue { - path: ident.into(), - eq_token, - value: Expr::Lit(ExprLit { - attrs: Vec::new(), - lit: value.into(), - }), - })); - - if input.is_empty() { - break; - } - - input.parse::()?; - } - Ok(res) - }) - .iter() - .flatten() - .filter_map(|nested| match nested { - Meta::Path(path) if path.segments.len() == 1 => { - Some(FieldDecorator::Word(path.get_ident()?.to_string())) - } - Meta::NameValue(name_value) => Some(FieldDecorator::NameValue( - name_value.path.get_ident()?.to_string(), - expr_to_string(&name_value.value)?, - )), - // TODO: this should throw a visible error since it suggests a malformed - // attribute. - _ => None, - }) - .collect::>(), - ) - }) - .fold(HashMap::new(), |mut acc, (language, decorators)| { - acc.entry(language).or_default().extend(decorators); - acc - }) -} - -fn expr_to_string(expr: &Expr) -> Option { - match expr { - Expr::Lit(expr_lit) => literal_to_string(&expr_lit.lit), - _ => None, - } -} - -fn literal_to_string(lit: &syn::Lit) -> Option { - match lit { - syn::Lit::Str(str) => Some(str.value().trim().to_string()), - _ => None, - } -} - -/// Checks the struct or enum for decorators like `#[typeshare(swift = "Codable, Equatable")]` -/// Takes a slice of `syn::Attribute`, returns a `HashMap>`, where `language` is `SupportedLanguage` and `decoration_words` is `String` -pub(crate) fn get_decorators(attrs: &[syn::Attribute]) -> HashMap> { - // The resulting HashMap, Key is the language, and the value is a vector of decorators words that will be put onto structures - let mut out: HashMap> = HashMap::new(); - - for value in get_name_value_meta_items(attrs, "swift", TYPESHARE) { - let decorators: Vec = value.split(',').map(|s| s.trim().to_string()).collect(); - - // lastly, get the entry in the hashmap output and extend the value, or insert what we have already found - let decs = out.entry(SupportedLanguage::Swift).or_default(); - decs.extend(decorators); - // Sorting so all the added decorators will be after the normal ([`String`], `Codable`) in alphabetical order - decs.sort_unstable(); - decs.dedup(); //removing any duplicates just in case - } - - //return our hashmap mapping of language -> Vec - out -} - -pub(crate) fn get_tag_key(attrs: &[syn::Attribute]) -> Option { - get_name_value_meta_items(attrs, "tag", SERDE).next() -} - -pub(crate) fn get_content_key(attrs: &[syn::Attribute]) -> Option { - get_name_value_meta_items(attrs, "content", SERDE).next() -} - -/// Removes `-` characters from identifiers -pub(crate) fn remove_dash_from_identifier(name: &str) -> String { - // Dashes are not valid in identifiers, so we map them to underscores - name.replace('-', "_") -} - -#[test] -fn test_rename_all_to_case() { - let test_word = "test_case"; - - let tests = [ - ("lowercase", "test_case"), - ("UPPERCASE", "TEST_CASE"), - ("PascalCase", "TestCase"), - ("camelCase", "testCase"), - ("snake_case", "test_case"), - ("SCREAMING_SNAKE_CASE", "TEST_CASE"), - ("kebab-case", "test-case"), - ("SCREAMING-KEBAB-CASE", "TEST-CASE"), - ("invalid case", "test_case"), - ]; - - for test in tests { - assert_eq!( - rename_all_to_case(test_word.to_string(), &Some(test.0.to_string())), - test.1 - ); - } -} diff --git a/core/src/language/kotlin.rs b/core/src/language/kotlin.rs index f1583439..bc354e7e 100644 --- a/core/src/language/kotlin.rs +++ b/core/src/language/kotlin.rs @@ -1,8 +1,8 @@ use super::Language; use crate::language::SupportedLanguage; +use crate::parser::remove_dash_from_identifier; use crate::rust_types::{RustTypeFormatError, SpecialRustType}; use crate::{ - helpers::remove_dash_from_identifier, rename::RenameExt, rust_types::{RustEnum, RustEnumVariant, RustField, RustStruct, RustTypeAlias}, }; diff --git a/core/src/language/scala.rs b/core/src/language/scala.rs index 2fe764c0..abff8c14 100644 --- a/core/src/language/scala.rs +++ b/core/src/language/scala.rs @@ -1,11 +1,8 @@ use super::Language; use crate::language::SupportedLanguage; -use crate::parser::ParsedData; +use crate::parser::{remove_dash_from_identifier, ParsedData}; +use crate::rust_types::{RustEnum, RustEnumVariant, RustField, RustStruct, RustTypeAlias}; use crate::rust_types::{RustType, RustTypeFormatError, SpecialRustType}; -use crate::{ - helpers::remove_dash_from_identifier, - rust_types::{RustEnum, RustEnumVariant, RustField, RustStruct, RustTypeAlias}, -}; use itertools::Itertools; use joinery::JoinableIterator; use lazy_format::lazy_format; diff --git a/core/src/language/swift.rs b/core/src/language/swift.rs index 8e1da1e5..772d7096 100644 --- a/core/src/language/swift.rs +++ b/core/src/language/swift.rs @@ -1,6 +1,6 @@ +use crate::parser::remove_dash_from_identifier; use crate::rust_types::{RustTypeFormatError, SpecialRustType}; use crate::{ - helpers::remove_dash_from_identifier, language::{Language, SupportedLanguage}, rename::RenameExt, rust_types::{RustEnum, RustEnumVariant, RustStruct, RustTypeAlias}, diff --git a/core/src/lib.rs b/core/src/lib.rs index b8544afc..65a8c8c3 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -7,7 +7,6 @@ use thiserror::Error; mod rename; -mod helpers; /// Implementations for each language converter pub mod language; /// Parsing Rust code into a format the `language` modules can understand diff --git a/core/src/parser.rs b/core/src/parser.rs index 272a027a..18c558c3 100644 --- a/core/src/parser.rs +++ b/core/src/parser.rs @@ -1,21 +1,26 @@ -use crate::helpers::{ - get_content_key, get_decorators, get_field_decorators, get_field_type_override, get_ident, - get_serialized_as_type, get_tag_key, has_typeshare_annotation, is_skipped, parse_comment_attrs, - serde_default, serde_flatten, serde_rename_all, -}; - +use crate::language::SupportedLanguage; +use crate::rename::RenameExt; use crate::rust_types::{ - RustEnum, RustEnumShared, RustEnumVariant, RustEnumVariantShared, RustField, RustItem, - RustStruct, RustType, RustTypeAlias, RustTypeParseError, + FieldDecorator, Id, RustEnum, RustEnumShared, RustEnumVariant, RustEnumVariantShared, + RustField, RustItem, RustStruct, RustType, RustTypeAlias, RustTypeParseError, }; +use std::collections::{BTreeSet, HashMap, HashSet}; use std::convert::TryFrom; -use syn::GenericParam; -use syn::{Fields, Item, ItemEnum, ItemStruct, ItemType}; +use proc_macro2::Ident; +use syn::ext::IdentExt; +use syn::parse::ParseBuffer; +use syn::punctuated::Punctuated; +use syn::{ + Attribute, Expr, ExprLit, Fields, Item, ItemEnum, ItemStruct, ItemType, LitStr, MetaList, + MetaNameValue, Token, +}; +use syn::{GenericParam, Meta}; use thiserror::Error; const TYPESHARE: &str = "typeshare"; +const SERDE: &str = "serde"; /// Errors that can occur while parsing Rust source input. #[derive(Debug, Error)] @@ -416,3 +421,299 @@ fn parse_type_alias(t: &ItemType) -> Result { generic_types, }) } + +// Helpers + +/// Checks the given attrs for `#[typeshare]` +pub(crate) fn has_typeshare_annotation(attrs: &[syn::Attribute]) -> bool { + attrs + .iter() + .flat_map(|attr| attr.path().segments.clone()) + .any(|segment| segment.ident == TYPESHARE) +} + +pub(crate) fn serde_rename_all(attrs: &[syn::Attribute]) -> Option { + get_name_value_meta_items(attrs, "rename_all", SERDE).next() +} + +pub(crate) fn get_serialized_as_type(attrs: &[syn::Attribute]) -> Option { + get_name_value_meta_items(attrs, "serialized_as", TYPESHARE).next() +} + +pub(crate) fn get_field_type_override(attrs: &[syn::Attribute]) -> Option { + get_name_value_meta_items(attrs, "serialized_as", TYPESHARE).next() +} + +pub(crate) fn get_name_value_meta_items<'a>( + attrs: &'a [syn::Attribute], + name: &'a str, + ident: &'static str, +) -> impl Iterator + 'a { + attrs.iter().flat_map(move |attr| { + get_meta_items(attr, ident) + .iter() + .filter_map(|arg| match arg { + Meta::NameValue(name_value) if name_value.path.is_ident(name) => { + expr_to_string(&name_value.value) + } + _ => None, + }) + .collect::>() + }) +} + +/// Returns all arguments passed into `#[{ident}(...)]` where `{ident}` can be `serde` or `typeshare` attributes +pub(crate) fn get_meta_items(attr: &syn::Attribute, ident: &str) -> Vec { + if attr.path().is_ident(ident) { + attr.parse_args_with(Punctuated::::parse_terminated) + .iter() + .flat_map(|meta| meta.iter()) + .cloned() + .collect() + } else { + Vec::default() + } +} + +pub(crate) fn get_ident( + ident: Option<&proc_macro2::Ident>, + attrs: &[syn::Attribute], + rename_all: &Option, +) -> Id { + let original = ident.map_or("???".to_string(), |id| id.to_string().replace("r#", "")); + + let mut renamed = rename_all_to_case(original.clone(), rename_all); + + if let Some(s) = serde_rename(attrs) { + renamed = s; + } + + Id { original, renamed } +} + +pub(crate) fn rename_all_to_case(original: String, case: &Option) -> String { + match case { + None => original, + Some(value) => match value.as_str() { + "lowercase" => original.to_lowercase(), + "UPPERCASE" => original.to_uppercase(), + "PascalCase" => original.to_pascal_case(), + "camelCase" => original.to_camel_case(), + "snake_case" => original.to_snake_case(), + "SCREAMING_SNAKE_CASE" => original.to_screaming_snake_case(), + "kebab-case" => original.to_kebab_case(), + "SCREAMING-KEBAB-CASE" => original.to_screaming_kebab_case(), + _ => original, + }, + } +} + +pub(crate) fn serde_rename(attrs: &[syn::Attribute]) -> Option { + get_name_value_meta_items(attrs, "rename", SERDE).next() +} + +/// Parses any comment out of the given slice of attributes +pub(crate) fn parse_comment_attrs(attrs: &[Attribute]) -> Vec { + attrs + .iter() + .map(|attr| attr.meta.clone()) + .filter_map(|meta| match meta { + Meta::NameValue(name_value) if name_value.path.is_ident("doc") => { + expr_to_string(&name_value.value) + } + _ => None, + }) + .collect() +} + +// `#[typeshare(skip)]` or `#[serde(skip)]` +pub(crate) fn is_skipped(attrs: &[syn::Attribute]) -> bool { + attrs.iter().any(|attr| { + get_meta_items(attr, SERDE) + .into_iter() + .chain(get_meta_items(attr, TYPESHARE)) + .any(|arg| matches!(arg, Meta::Path(path) if path.is_ident("skip"))) + }) +} + +fn serde_attr(attrs: &[syn::Attribute], ident: &str) -> bool { + attrs.iter().any(|attr| { + get_meta_items(attr, SERDE) + .iter() + .any(|arg| matches!(arg, Meta::Path(path) if path.is_ident(ident))) + }) +} + +pub(crate) fn serde_default(attrs: &[syn::Attribute]) -> bool { + serde_attr(attrs, "default") +} + +pub(crate) fn serde_flatten(attrs: &[syn::Attribute]) -> bool { + serde_attr(attrs, "flatten") +} + +/// Checks the struct or enum for decorators like `#[typeshare(typescript(readonly)]` +/// Takes a slice of `syn::Attribute`, returns a `HashMap>`, where `language` is `SupportedLanguage` +/// and `decorator` is `FieldDecorator`. Field decorators are ordered in a `BTreeSet` for consistent code generation. +pub(crate) fn get_field_decorators( + attrs: &[Attribute], +) -> HashMap> { + let languages: HashSet = SupportedLanguage::all_languages().collect(); + + attrs + .iter() + .flat_map(|attr| get_meta_items(attr, TYPESHARE)) + .flat_map(|meta| { + if let Meta::List(list) = meta { + Some(list) + } else { + None + } + }) + .flat_map(|list: MetaList| match list.path.get_ident() { + Some(ident) if languages.contains(&ident.try_into().unwrap()) => { + Some((ident.try_into().unwrap(), list)) + } + _ => None, + }) + .map(|(language, list): (SupportedLanguage, MetaList)| { + ( + language, + list.parse_args_with(|input: &ParseBuffer| { + let mut res: Vec = vec![]; + + loop { + if input.is_empty() { + break; + } + + let ident = input.call(Ident::parse_any)?; + + // Parse `readonly` or any other single ident optionally followed by a comma + if input.peek(Token![,]) || input.is_empty() { + input.parse::().unwrap_or_default(); + res.push(Meta::Path(ident.into())); + continue; + } + + if input.is_empty() { + break; + } + + // Parse `= "any | undefined"` or any other eq sign followed by a string literal + + let eq_token = input.parse::()?; + + let value: LitStr = input.parse()?; + res.push(Meta::NameValue(MetaNameValue { + path: ident.into(), + eq_token, + value: Expr::Lit(ExprLit { + attrs: Vec::new(), + lit: value.into(), + }), + })); + + if input.is_empty() { + break; + } + + input.parse::()?; + } + Ok(res) + }) + .iter() + .flatten() + .filter_map(|nested| match nested { + Meta::Path(path) if path.segments.len() == 1 => { + Some(FieldDecorator::Word(path.get_ident()?.to_string())) + } + Meta::NameValue(name_value) => Some(FieldDecorator::NameValue( + name_value.path.get_ident()?.to_string(), + expr_to_string(&name_value.value)?, + )), + // TODO: this should throw a visible error since it suggests a malformed + // attribute. + _ => None, + }) + .collect::>(), + ) + }) + .fold(HashMap::new(), |mut acc, (language, decorators)| { + acc.entry(language).or_default().extend(decorators); + acc + }) +} + +fn expr_to_string(expr: &Expr) -> Option { + match expr { + Expr::Lit(expr_lit) => literal_to_string(&expr_lit.lit), + _ => None, + } +} + +fn literal_to_string(lit: &syn::Lit) -> Option { + match lit { + syn::Lit::Str(str) => Some(str.value().trim().to_string()), + _ => None, + } +} + +/// Checks the struct or enum for decorators like `#[typeshare(swift = "Codable, Equatable")]` +/// Takes a slice of `syn::Attribute`, returns a `HashMap>`, where `language` is `SupportedLanguage` and `decoration_words` is `String` +pub(crate) fn get_decorators(attrs: &[syn::Attribute]) -> HashMap> { + // The resulting HashMap, Key is the language, and the value is a vector of decorators words that will be put onto structures + let mut out: HashMap> = HashMap::new(); + + for value in get_name_value_meta_items(attrs, "swift", TYPESHARE) { + let decorators: Vec = value.split(',').map(|s| s.trim().to_string()).collect(); + + // lastly, get the entry in the hashmap output and extend the value, or insert what we have already found + let decs = out.entry(SupportedLanguage::Swift).or_default(); + decs.extend(decorators); + // Sorting so all the added decorators will be after the normal ([`String`], `Codable`) in alphabetical order + decs.sort_unstable(); + decs.dedup(); //removing any duplicates just in case + } + + //return our hashmap mapping of language -> Vec + out +} + +pub(crate) fn get_tag_key(attrs: &[syn::Attribute]) -> Option { + get_name_value_meta_items(attrs, "tag", SERDE).next() +} + +pub(crate) fn get_content_key(attrs: &[syn::Attribute]) -> Option { + get_name_value_meta_items(attrs, "content", SERDE).next() +} + +/// Removes `-` characters from identifiers +pub(crate) fn remove_dash_from_identifier(name: &str) -> String { + // Dashes are not valid in identifiers, so we map them to underscores + name.replace('-', "_") +} + +#[test] +fn test_rename_all_to_case() { + let test_word = "test_case"; + + let tests = [ + ("lowercase", "test_case"), + ("UPPERCASE", "TEST_CASE"), + ("PascalCase", "TestCase"), + ("camelCase", "testCase"), + ("snake_case", "test_case"), + ("SCREAMING_SNAKE_CASE", "TEST_CASE"), + ("kebab-case", "test-case"), + ("SCREAMING-KEBAB-CASE", "TEST-CASE"), + ("invalid case", "test_case"), + ]; + + for test in tests { + assert_eq!( + rename_all_to_case(test_word.to_string(), &Some(test.0.to_string())), + test.1 + ); + } +} From 558c1fba97e4be8e93ae2e14fd48b0d3bacde24e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Jan=20Czocha=C5=84ski?= Date: Wed, 7 Feb 2024 15:11:58 +0100 Subject: [PATCH 6/6] Make functions private --- core/src/parser.rs | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/core/src/parser.rs b/core/src/parser.rs index 18c558c3..5580f8d9 100644 --- a/core/src/parser.rs +++ b/core/src/parser.rs @@ -463,7 +463,7 @@ pub(crate) fn get_name_value_meta_items<'a>( } /// Returns all arguments passed into `#[{ident}(...)]` where `{ident}` can be `serde` or `typeshare` attributes -pub(crate) fn get_meta_items(attr: &syn::Attribute, ident: &str) -> Vec { +fn get_meta_items(attr: &syn::Attribute, ident: &str) -> Vec { if attr.path().is_ident(ident) { attr.parse_args_with(Punctuated::::parse_terminated) .iter() @@ -475,7 +475,7 @@ pub(crate) fn get_meta_items(attr: &syn::Attribute, ident: &str) -> Vec { } } -pub(crate) fn get_ident( +fn get_ident( ident: Option<&proc_macro2::Ident>, attrs: &[syn::Attribute], rename_all: &Option, @@ -491,7 +491,7 @@ pub(crate) fn get_ident( Id { original, renamed } } -pub(crate) fn rename_all_to_case(original: String, case: &Option) -> String { +fn rename_all_to_case(original: String, case: &Option) -> String { match case { None => original, Some(value) => match value.as_str() { @@ -508,12 +508,12 @@ pub(crate) fn rename_all_to_case(original: String, case: &Option) -> Str } } -pub(crate) fn serde_rename(attrs: &[syn::Attribute]) -> Option { +fn serde_rename(attrs: &[syn::Attribute]) -> Option { get_name_value_meta_items(attrs, "rename", SERDE).next() } /// Parses any comment out of the given slice of attributes -pub(crate) fn parse_comment_attrs(attrs: &[Attribute]) -> Vec { +fn parse_comment_attrs(attrs: &[Attribute]) -> Vec { attrs .iter() .map(|attr| attr.meta.clone()) @@ -527,7 +527,7 @@ pub(crate) fn parse_comment_attrs(attrs: &[Attribute]) -> Vec { } // `#[typeshare(skip)]` or `#[serde(skip)]` -pub(crate) fn is_skipped(attrs: &[syn::Attribute]) -> bool { +fn is_skipped(attrs: &[syn::Attribute]) -> bool { attrs.iter().any(|attr| { get_meta_items(attr, SERDE) .into_iter() @@ -544,18 +544,18 @@ fn serde_attr(attrs: &[syn::Attribute], ident: &str) -> bool { }) } -pub(crate) fn serde_default(attrs: &[syn::Attribute]) -> bool { +fn serde_default(attrs: &[syn::Attribute]) -> bool { serde_attr(attrs, "default") } -pub(crate) fn serde_flatten(attrs: &[syn::Attribute]) -> bool { +fn serde_flatten(attrs: &[syn::Attribute]) -> bool { serde_attr(attrs, "flatten") } /// Checks the struct or enum for decorators like `#[typeshare(typescript(readonly)]` /// Takes a slice of `syn::Attribute`, returns a `HashMap>`, where `language` is `SupportedLanguage` /// and `decorator` is `FieldDecorator`. Field decorators are ordered in a `BTreeSet` for consistent code generation. -pub(crate) fn get_field_decorators( +fn get_field_decorators( attrs: &[Attribute], ) -> HashMap> { let languages: HashSet = SupportedLanguage::all_languages().collect(); @@ -661,7 +661,7 @@ fn literal_to_string(lit: &syn::Lit) -> Option { /// Checks the struct or enum for decorators like `#[typeshare(swift = "Codable, Equatable")]` /// Takes a slice of `syn::Attribute`, returns a `HashMap>`, where `language` is `SupportedLanguage` and `decoration_words` is `String` -pub(crate) fn get_decorators(attrs: &[syn::Attribute]) -> HashMap> { +fn get_decorators(attrs: &[syn::Attribute]) -> HashMap> { // The resulting HashMap, Key is the language, and the value is a vector of decorators words that will be put onto structures let mut out: HashMap> = HashMap::new(); @@ -680,11 +680,11 @@ pub(crate) fn get_decorators(attrs: &[syn::Attribute]) -> HashMap Option { +fn get_tag_key(attrs: &[syn::Attribute]) -> Option { get_name_value_meta_items(attrs, "tag", SERDE).next() } -pub(crate) fn get_content_key(attrs: &[syn::Attribute]) -> Option { +fn get_content_key(attrs: &[syn::Attribute]) -> Option { get_name_value_meta_items(attrs, "content", SERDE).next() }