<commit_before>//! Functions to aid the `Api::to_tokens` method.

use proc_macro2::{Span, TokenStream};
use quote::quote;
use std::collections::BTreeSet;
use syn::{
    AngleBracketedGenericArguments, GenericArgument, Ident, Lifetime,
    ParenthesizedGenericArguments, PathArguments, Type, TypePath, TypeReference,
};

use crate::api::{metadata::Metadata, request::Request};

pub fn collect_lifetime_ident(lifetimes: &mut BTreeSet<Lifetime>, ty: &Type) {
    match ty {
        Type::Path(TypePath { path, .. }) => {
            for seg in &path.segments {
                match &seg.arguments {
                    PathArguments::AngleBracketed(AngleBracketedGenericArguments {
                        args, ..
                    }) => {
                        for gen in args {
                            if let GenericArgument::Type(ty) = gen {
                                collect_lifetime_ident(lifetimes, &ty)
                            } else if let GenericArgument::Lifetime(lt) = gen {
                                lifetimes.insert(lt.clone());
                            }
                        }
                    }
                    PathArguments::Parenthesized(ParenthesizedGenericArguments {
                        inputs, ..
                    }) => {
                        for ty in inputs {
                            collect_lifetime_ident(lifetimes, ty)
                        }
                    }
                    _ => {}
                }
            }
        }
        Type::Reference(TypeReference { elem, lifetime, .. }) => {
            collect_lifetime_ident(lifetimes, &*elem);
            if let Some(lt) = lifetime {
                lifetimes.insert(lt.clone());
            }
        }
        _ => {}
    }
}

/// Generates a `TokenStream` of lifetime identifiers `<'lifetime>`.
pub fn generics_to_tokens<'a, I: Iterator<Item = &'a Lifetime>>(lifetimes: I) -> TokenStream {
    let lifetimes = lifetimes.collect::<Vec<_>>();
    if lifetimes.is_empty() {
        TokenStream::new()
    } else {
        let lifetimes = quote! { #( #lifetimes ),* };
        quote! { < #lifetimes > }
    }
}

/// The first item in the tuple generates code for the request path from
/// the `Metadata` and `Request` structs. The second item in the returned tuple
/// is the code to generate a Request struct field created from any segments
/// of the path that start with ":".
///
/// The first `TokenStream` returned is the constructed url path. The second `TokenStream` is<commit_msg>Handle tuple types in collect_lifetime_idents<commit_after>//! Functions to aid the `Api::to_tokens` method.

use proc_macro2::{Span, TokenStream};
use quote::quote;
use std::collections::BTreeSet;
use syn::{
    AngleBracketedGenericArguments, GenericArgument, Ident, Lifetime,
    ParenthesizedGenericArguments, PathArguments, Type, TypePath, TypeReference,
};

use crate::api::{metadata::Metadata, request::Request};

pub fn collect_lifetime_ident(lifetimes: &mut BTreeSet<Lifetime>, ty: &Type) {
    match ty {
        Type::Path(TypePath { path, .. }) => {
            for seg in &path.segments {
                match &seg.arguments {
                    PathArguments::AngleBracketed(AngleBracketedGenericArguments {
                        args, ..
                    }) => {
                        for gen in args {
                            if let GenericArgument::Type(ty) = gen {
                                collect_lifetime_ident(lifetimes, &ty)
                            } else if let GenericArgument::Lifetime(lt) = gen {
                                lifetimes.insert(lt.clone());
                            }
                        }
                    }
                    PathArguments::Parenthesized(ParenthesizedGenericArguments {
                        inputs, ..
                    }) => {
                        for ty in inputs {
                            collect_lifetime_ident(lifetimes, ty)
                        }
                    }
                    _ => {}
                }
            }
        }
        Type::Reference(TypeReference { elem, lifetime, .. }) => {
            collect_lifetime_ident(lifetimes, &*elem);
            if let Some(lt) = lifetime {
                lifetimes.insert(lt.clone());
            }
        }
        Type::Tuple(syn::TypeTuple { elems, .. }) => {
            for ty in elems {
                collect_lifetime_ident(lifetimes, ty)
            }
        }
        _ => {}
    }
}

/// Generates a `TokenStream` of lifetime identifiers `<'lifetime>`.
pub fn generics_to_tokens<'a, I: Iterator<Item = &'a Lifetime>>(lifetimes: I) -> TokenStream {
    let lifetimes = lifetimes.collect::<Vec<_>>();
    if lifetimes.is_empty() {
        TokenStream::new()
    } else {
        let lifetimes = quote! { #( #lifetimes ),* };
        quote! { < #lifetimes > }
    }
}

/// The first item in the tuple generates code for the request path from
/// the `Metadata` and `Request` structs. The second item in the returned tuple
/// is the code to generate a Request struct field created from any segments
/// of the path that start with ":".
///
/// The first `TokenStream` returned is the constructed url path. The second `TokenStream` is
{'fraction_non_alphanumeric': 0.11113225499524262, 'fraction_numerical': 0.00038058991436726926, 'mean_word_length': 1.6345864661654135, 'pattern_counts': {'":': 2, '<': 17, '<?xml version=': 0, '>': 32, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3921807', 'n_tokens_mistral': 1396, 'n_tokens_neox': 1327, 'n_words': 345}