Skip to content

Commit

Permalink
Rebase fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
czocher committed Jan 30, 2024
1 parent cf44a31 commit 4f2a564
Showing 1 changed file with 0 additions and 337 deletions.
337 changes: 0 additions & 337 deletions core/src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -416,340 +416,3 @@ fn parse_type_alias(t: &ItemType) -> Result<RustTypeAlias, ParseError> {
generic_types,
})
}

// Helpers

/// Parses any comment out of the given slice of attributes
fn parse_comment_attrs(attrs: &[Attribute]) -> Vec<String> {
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<String>,
) -> 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>) -> 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<String> {
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<Item = syn::Lit> + '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::<Vec<_>>()
})
}

fn get_serde_name_value_meta_items<'a>(
attrs: &'a [syn::Attribute],
name: &'a str,
) -> impl Iterator<Item = syn::Lit> + '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::<Vec<_>>()
})
}

fn get_serialized_as_type(attrs: &[syn::Attribute]) -> Option<String> {
get_typeshare_name_value_meta_items(attrs, "serialized_as")
.next()
.and_then(literal_as_string)
}

fn get_field_type_override(attrs: &[syn::Attribute]) -> Option<String> {
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<language, BTreeSet<decorator>>`, 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<SupportedLanguage, BTreeSet<FieldDecorator>> {
let languages: HashSet<SupportedLanguage> = 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<language, Vec<decoration_words>>`, where `language` is `SupportedLanguage` and `decoration_words` is `String`
fn get_decorators(attrs: &[syn::Attribute]) -> HashMap<SupportedLanguage, Vec<String>> {
// 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<SupportedLanguage, Vec<String>> = HashMap::new();

for value in get_typeshare_name_value_meta_items(attrs, "swift").filter_map(literal_as_string) {
let decorators: Vec<String> = 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<decorators>
out
}

fn get_tag_key(attrs: &[syn::Attribute]) -> Option<String> {
get_serde_name_value_meta_items(attrs, "tag")
.next()
.and_then(literal_as_string)
}

fn get_content_key(attrs: &[syn::Attribute]) -> Option<String> {
get_serde_name_value_meta_items(attrs, "content")
.next()
.and_then(literal_as_string)
}

fn serde_rename(attrs: &[syn::Attribute]) -> Option<String> {
get_serde_name_value_meta_items(attrs, "rename")
.next()
.and_then(literal_as_string)
}

fn serde_rename_all(attrs: &[syn::Attribute]) -> Option<String> {
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<NestedMeta> {
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<NestedMeta> {
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('-', "_")
}

0 comments on commit 4f2a564

Please sign in to comment.