1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use crate::util::kw;
use proc_macro2::TokenStream;
use quote::{quote, ToTokens};
use syn::{
    bracketed, parenthesized,
    parse::{Nothing, Parse, ParseStream},
    token::{Bracket, Paren},
    Lit,
};

/// Default value
#[derive(Clone)]
pub enum DefaultValue {
    Literal(Lit),
    None(kw::None),
    Some {
        some: kw::Some,
        paren: Paren,
        inner: Box<DefaultValue>,
    },
    EmptySeq(Bracket),
}

impl ToTokens for DefaultValue {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        match self {
            DefaultValue::Literal(lit) => lit.to_tokens(tokens),
            DefaultValue::None(kw) => kw.to_tokens(tokens),
            DefaultValue::Some { inner, .. } => tokens.extend(quote! { Some(#inner) }),
            DefaultValue::EmptySeq(_) => tokens.extend(quote! { [] }),
        }
    }
}

impl Parse for DefaultValue {
    fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
        let lookahead = input.lookahead1();
        if lookahead.peek(kw::None) {
            let none_kw: kw::None = input.parse()?;
            Ok(Self::None(none_kw))
        } else if lookahead.peek(kw::Some) {
            let some: kw::Some = input.parse()?;
            let content;
            let paren = parenthesized!(content in input);
            Ok(Self::Some {
                some,
                paren,
                inner: content.parse()?,
            })
        } else if lookahead.peek(Bracket) {
            let content;
            let bracket = bracketed!(content in input);
            content.parse::<Nothing>()?;
            Ok(Self::EmptySeq(bracket))
        } else {
            Ok(Self::Literal(input.parse()?))
        }
    }
}

impl DefaultValue {
    fn metadata_calls(&self) -> syn::Result<TokenStream> {
        match self {
            DefaultValue::Literal(Lit::Int(i)) if !i.suffix().is_empty() => Err(
                syn::Error::new_spanned(i, "integer literals with suffix not supported here"),
            ),
            DefaultValue::Literal(Lit::Float(f)) if !f.suffix().is_empty() => Err(
                syn::Error::new_spanned(f, "float literals with suffix not supported here"),
            ),

            DefaultValue::Literal(Lit::Str(s)) => Ok(quote! {
                .concat_value(::uniffi::metadata::codes::LIT_STR)
                .concat_str(#s)
            }),
            DefaultValue::Literal(Lit::Int(i)) => {
                let digits = i.base10_digits();
                Ok(quote! {
                    .concat_value(::uniffi::metadata::codes::LIT_INT)
                    .concat_str(#digits)
                })
            }
            DefaultValue::Literal(Lit::Float(f)) => {
                let digits = f.base10_digits();
                Ok(quote! {
                    .concat_value(::uniffi::metadata::codes::LIT_FLOAT)
                    .concat_str(#digits)
                })
            }
            DefaultValue::Literal(Lit::Bool(b)) => Ok(quote! {
                .concat_value(::uniffi::metadata::codes::LIT_BOOL)
                .concat_bool(#b)
            }),

            DefaultValue::Literal(_) => Err(syn::Error::new_spanned(
                self,
                "this type of literal is not currently supported as a default",
            )),

            DefaultValue::EmptySeq(_) => Ok(quote! {
                .concat_value(::uniffi::metadata::codes::LIT_EMPTY_SEQ)
            }),

            DefaultValue::None(_) => Ok(quote! {
                .concat_value(::uniffi::metadata::codes::LIT_NONE)
            }),

            DefaultValue::Some { inner, .. } => {
                let inner_calls = inner.metadata_calls()?;
                Ok(quote! {
                    .concat_value(::uniffi::metadata::codes::LIT_SOME)
                    #inner_calls
                })
            }
        }
    }
}

pub fn default_value_metadata_calls(default: &Option<DefaultValue>) -> syn::Result<TokenStream> {
    Ok(match default {
        Some(default) => {
            let metadata_calls = default.metadata_calls()?;
            quote! {
                .concat_bool(true)
                #metadata_calls
            }
        }
        None => quote! { .concat_bool(false) },
    })
}