119625d8cSopenharmony_ci// Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>,
219625d8cSopenharmony_ci// Kevin Knapp (@kbknapp) <kbknapp@gmail.com>, and
319625d8cSopenharmony_ci// Ana Hobden (@hoverbear) <operator@hoverbear.org>
419625d8cSopenharmony_ci//
519625d8cSopenharmony_ci// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
619625d8cSopenharmony_ci// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
719625d8cSopenharmony_ci// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
819625d8cSopenharmony_ci// option. This file may not be copied, modified, or distributed
919625d8cSopenharmony_ci// except according to those terms.
1019625d8cSopenharmony_ci//
1119625d8cSopenharmony_ci// This work was derived from Structopt (https://github.com/TeXitoi/structopt)
1219625d8cSopenharmony_ci// commit#ea76fa1b1b273e65e3b0b1046643715b49bec51f which is licensed under the
1319625d8cSopenharmony_ci// MIT/Apache 2.0 license.
1419625d8cSopenharmony_ci
1519625d8cSopenharmony_ciuse proc_macro2::TokenStream;
1619625d8cSopenharmony_ciuse quote::quote;
1719625d8cSopenharmony_ciuse syn::Ident;
1819625d8cSopenharmony_ciuse syn::Variant;
1919625d8cSopenharmony_ciuse syn::{
2019625d8cSopenharmony_ci    self, punctuated::Punctuated, token::Comma, Data, DataStruct, DeriveInput, Field, Fields,
2119625d8cSopenharmony_ci    Generics,
2219625d8cSopenharmony_ci};
2319625d8cSopenharmony_ci
2419625d8cSopenharmony_ciuse crate::derives::{args, into_app, subcommand};
2519625d8cSopenharmony_ciuse crate::item::Item;
2619625d8cSopenharmony_ciuse crate::item::Name;
2719625d8cSopenharmony_ci
2819625d8cSopenharmony_cipub fn derive_parser(input: &DeriveInput) -> Result<TokenStream, syn::Error> {
2919625d8cSopenharmony_ci    let ident = &input.ident;
3019625d8cSopenharmony_ci    let pkg_name = std::env::var("CARGO_PKG_NAME").ok().unwrap_or_default();
3119625d8cSopenharmony_ci
3219625d8cSopenharmony_ci    match input.data {
3319625d8cSopenharmony_ci        Data::Struct(DataStruct {
3419625d8cSopenharmony_ci            fields: Fields::Named(ref fields),
3519625d8cSopenharmony_ci            ..
3619625d8cSopenharmony_ci        }) => {
3719625d8cSopenharmony_ci            let name = Name::Assigned(quote!(#pkg_name));
3819625d8cSopenharmony_ci            let item = Item::from_args_struct(input, name)?;
3919625d8cSopenharmony_ci            let fields = fields
4019625d8cSopenharmony_ci                .named
4119625d8cSopenharmony_ci                .iter()
4219625d8cSopenharmony_ci                .map(|field| {
4319625d8cSopenharmony_ci                    let item = Item::from_args_field(field, item.casing(), item.env_casing())?;
4419625d8cSopenharmony_ci                    Ok((field, item))
4519625d8cSopenharmony_ci                })
4619625d8cSopenharmony_ci                .collect::<Result<Vec<_>, syn::Error>>()?;
4719625d8cSopenharmony_ci            gen_for_struct(&item, ident, &input.generics, &fields)
4819625d8cSopenharmony_ci        }
4919625d8cSopenharmony_ci        Data::Struct(DataStruct {
5019625d8cSopenharmony_ci            fields: Fields::Unit,
5119625d8cSopenharmony_ci            ..
5219625d8cSopenharmony_ci        }) => {
5319625d8cSopenharmony_ci            let name = Name::Assigned(quote!(#pkg_name));
5419625d8cSopenharmony_ci            let item = Item::from_args_struct(input, name)?;
5519625d8cSopenharmony_ci            let fields = Punctuated::<Field, Comma>::new();
5619625d8cSopenharmony_ci            let fields = fields
5719625d8cSopenharmony_ci                .iter()
5819625d8cSopenharmony_ci                .map(|field| {
5919625d8cSopenharmony_ci                    let item = Item::from_args_field(field, item.casing(), item.env_casing())?;
6019625d8cSopenharmony_ci                    Ok((field, item))
6119625d8cSopenharmony_ci                })
6219625d8cSopenharmony_ci                .collect::<Result<Vec<_>, syn::Error>>()?;
6319625d8cSopenharmony_ci            gen_for_struct(&item, ident, &input.generics, &fields)
6419625d8cSopenharmony_ci        }
6519625d8cSopenharmony_ci        Data::Enum(ref e) => {
6619625d8cSopenharmony_ci            let name = Name::Assigned(quote!(#pkg_name));
6719625d8cSopenharmony_ci            let item = Item::from_subcommand_enum(input, name)?;
6819625d8cSopenharmony_ci            let variants = e
6919625d8cSopenharmony_ci                .variants
7019625d8cSopenharmony_ci                .iter()
7119625d8cSopenharmony_ci                .map(|variant| {
7219625d8cSopenharmony_ci                    let item =
7319625d8cSopenharmony_ci                        Item::from_subcommand_variant(variant, item.casing(), item.env_casing())?;
7419625d8cSopenharmony_ci                    Ok((variant, item))
7519625d8cSopenharmony_ci                })
7619625d8cSopenharmony_ci                .collect::<Result<Vec<_>, syn::Error>>()?;
7719625d8cSopenharmony_ci            gen_for_enum(&item, ident, &input.generics, &variants)
7819625d8cSopenharmony_ci        }
7919625d8cSopenharmony_ci        _ => abort_call_site!("`#[derive(Parser)]` only supports non-tuple structs and enums"),
8019625d8cSopenharmony_ci    }
8119625d8cSopenharmony_ci}
8219625d8cSopenharmony_ci
8319625d8cSopenharmony_cifn gen_for_struct(
8419625d8cSopenharmony_ci    item: &Item,
8519625d8cSopenharmony_ci    item_name: &Ident,
8619625d8cSopenharmony_ci    generics: &Generics,
8719625d8cSopenharmony_ci    fields: &[(&Field, Item)],
8819625d8cSopenharmony_ci) -> Result<TokenStream, syn::Error> {
8919625d8cSopenharmony_ci    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
9019625d8cSopenharmony_ci
9119625d8cSopenharmony_ci    let into_app = into_app::gen_for_struct(item, item_name, generics)?;
9219625d8cSopenharmony_ci    let args = args::gen_for_struct(item, item_name, generics, fields)?;
9319625d8cSopenharmony_ci
9419625d8cSopenharmony_ci    Ok(quote! {
9519625d8cSopenharmony_ci        impl #impl_generics clap::Parser for #item_name #ty_generics #where_clause {}
9619625d8cSopenharmony_ci
9719625d8cSopenharmony_ci        #into_app
9819625d8cSopenharmony_ci        #args
9919625d8cSopenharmony_ci    })
10019625d8cSopenharmony_ci}
10119625d8cSopenharmony_ci
10219625d8cSopenharmony_cifn gen_for_enum(
10319625d8cSopenharmony_ci    item: &Item,
10419625d8cSopenharmony_ci    item_name: &Ident,
10519625d8cSopenharmony_ci    generics: &Generics,
10619625d8cSopenharmony_ci    variants: &[(&Variant, Item)],
10719625d8cSopenharmony_ci) -> Result<TokenStream, syn::Error> {
10819625d8cSopenharmony_ci    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
10919625d8cSopenharmony_ci
11019625d8cSopenharmony_ci    let into_app = into_app::gen_for_enum(item, item_name, generics)?;
11119625d8cSopenharmony_ci    let subcommand = subcommand::gen_for_enum(item, item_name, generics, variants)?;
11219625d8cSopenharmony_ci
11319625d8cSopenharmony_ci    Ok(quote! {
11419625d8cSopenharmony_ci        impl #impl_generics clap::Parser for #item_name #ty_generics #where_clause {}
11519625d8cSopenharmony_ci
11619625d8cSopenharmony_ci        #into_app
11719625d8cSopenharmony_ci        #subcommand
11819625d8cSopenharmony_ci    })
11919625d8cSopenharmony_ci}
120