rustyline_derive/
lib.rs

1use proc_macro::TokenStream;
2use proc_macro2::TokenStream as TokenStream2;
3use quote::quote;
4use syn::{parse_macro_input, Data, DeriveInput, Field, Index, Path};
5
6fn get_field_by_attr<'a>(data: &'a Data, ident: &str) -> Option<(usize, &'a Field)> {
7    if let Data::Struct(struct_data) = &data {
8        let mut fields = struct_data.fields.iter().enumerate().filter(|(_, field)| {
9            field.attrs.iter().any(|attr| {
10                attr.path().is_ident("rustyline")
11                    && attr
12                        .parse_args::<Path>()
13                        .map_or(false, |arg| arg.is_ident(ident))
14            })
15        });
16
17        let field = fields.next();
18
19        if fields.next().is_some() {
20            panic!("Only one {:} field is allowed.", ident);
21        }
22
23        field
24    } else {
25        None
26    }
27}
28
29fn field_name_or_index_token(index: usize, field: &Field) -> TokenStream2 {
30    if let Some(ident) = field.ident.as_ref() {
31        quote!(#ident)
32    } else {
33        let index = Index::from(index);
34        quote!(#index)
35    }
36}
37
38#[proc_macro_derive(Completer, attributes(rustyline))]
39pub fn completer_macro_derive(input: TokenStream) -> TokenStream {
40    let input = parse_macro_input!(input as DeriveInput);
41    let name = &input.ident;
42    let generics = input.generics;
43    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
44    let expanded = if let Some((index, field)) = get_field_by_attr(&input.data, "Completer") {
45        let field_name_or_index = field_name_or_index_token(index, field);
46        let field_type = &field.ty;
47
48        quote! {
49            #[automatically_derived]
50            impl #impl_generics ::rustyline::completion::Completer for #name #ty_generics #where_clause {
51                type Candidate = <#field_type as ::rustyline::completion::Completer>::Candidate;
52
53                fn complete(
54                    &self,
55                    line: &str,
56                    pos: usize,
57                    ctx: &::rustyline::Context<'_>,
58                ) -> ::rustyline::Result<(usize, ::std::vec::Vec<Self::Candidate>)> {
59                    ::rustyline::completion::Completer::complete(&self.#field_name_or_index, line, pos, ctx)
60                }
61
62                fn update(&self, line: &mut ::rustyline::line_buffer::LineBuffer, start: usize, elected: &str, cl: &mut ::rustyline::Changeset) {
63                    ::rustyline::completion::Completer::update(&self.#field_name_or_index, line, start, elected, cl)
64                }
65            }
66        }
67    } else {
68        quote! {
69            #[automatically_derived]
70            impl #impl_generics ::rustyline::completion::Completer for #name #ty_generics #where_clause {
71                type Candidate = ::std::string::String;
72            }
73        }
74    };
75
76    TokenStream::from(expanded)
77}
78
79#[proc_macro_derive(Helper)]
80pub fn helper_macro_derive(input: TokenStream) -> TokenStream {
81    let input = parse_macro_input!(input as DeriveInput);
82    let name = &input.ident;
83    let generics = input.generics;
84    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
85    let expanded = quote! {
86        #[automatically_derived]
87        impl #impl_generics ::rustyline::Helper for #name #ty_generics #where_clause {
88        }
89    };
90    TokenStream::from(expanded)
91}
92
93#[proc_macro_derive(Highlighter, attributes(rustyline))]
94pub fn highlighter_macro_derive(input: TokenStream) -> TokenStream {
95    let input = parse_macro_input!(input as DeriveInput);
96    let name = &input.ident;
97    let generics = input.generics;
98    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
99    let expanded = if let Some((index, field)) = get_field_by_attr(&input.data, "Highlighter") {
100        let field_name_or_index = field_name_or_index_token(index, field);
101
102        quote! {
103            #[automatically_derived]
104            impl #impl_generics ::rustyline::highlight::Highlighter for #name #ty_generics #where_clause {
105                fn highlight<'l>(&self, line: &'l str, pos: usize) -> ::std::borrow::Cow<'l, str> {
106                    ::rustyline::highlight::Highlighter::highlight(&self.#field_name_or_index, line, pos)
107                }
108
109                fn highlight_prompt<'b, 's: 'b, 'p: 'b>(
110                    &'s self,
111                    prompt: &'p str,
112                    default: bool,
113                ) -> ::std::borrow::Cow<'b, str> {
114                    ::rustyline::highlight::Highlighter::highlight_prompt(&self.#field_name_or_index, prompt, default)
115                }
116
117                fn highlight_hint<'h>(&self, hint: &'h str) -> ::std::borrow::Cow<'h, str> {
118                    ::rustyline::highlight::Highlighter::highlight_hint(&self.#field_name_or_index, hint)
119                }
120
121                fn highlight_candidate<'c>(
122                    &self,
123                    candidate: &'c str,
124                    completion: ::rustyline::config::CompletionType,
125                ) -> ::std::borrow::Cow<'c, str> {
126                    ::rustyline::highlight::Highlighter::highlight_candidate(&self.#field_name_or_index, candidate, completion)
127                }
128
129                fn highlight_char(&self, line: &str, pos: usize, kind: ::rustyline::highlight::CmdKind) -> bool {
130                    ::rustyline::highlight::Highlighter::highlight_char(&self.#field_name_or_index, line, pos, kind)
131                }
132            }
133        }
134    } else {
135        quote! {
136            #[automatically_derived]
137            impl #impl_generics ::rustyline::highlight::Highlighter for #name #ty_generics #where_clause {
138            }
139        }
140    };
141    TokenStream::from(expanded)
142}
143
144#[proc_macro_derive(Hinter, attributes(rustyline))]
145pub fn hinter_macro_derive(input: TokenStream) -> TokenStream {
146    let input = parse_macro_input!(input as DeriveInput);
147    let name = &input.ident;
148    let generics = input.generics;
149    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
150    let expanded = if let Some((index, field)) = get_field_by_attr(&input.data, "Hinter") {
151        let field_name_or_index = field_name_or_index_token(index, field);
152        let field_type = &field.ty;
153
154        quote! {
155            #[automatically_derived]
156            impl #impl_generics ::rustyline::hint::Hinter for #name #ty_generics #where_clause {
157                type Hint = <#field_type as ::rustyline::hint::Hinter>::Hint;
158
159                fn hint(&self, line: &str, pos: usize, ctx: &::rustyline::Context<'_>) -> ::std::option::Option<Self::Hint> {
160                    ::rustyline::hint::Hinter::hint(&self.#field_name_or_index, line, pos, ctx)
161                }
162            }
163        }
164    } else {
165        quote! {
166            #[automatically_derived]
167            impl #impl_generics ::rustyline::hint::Hinter for #name #ty_generics #where_clause {
168                type Hint = ::std::string::String;
169            }
170        }
171    };
172    TokenStream::from(expanded)
173}
174
175#[proc_macro_derive(Validator, attributes(rustyline))]
176pub fn validator_macro_derive(input: TokenStream) -> TokenStream {
177    let input = parse_macro_input!(input as DeriveInput);
178    let name = &input.ident;
179    let generics = input.generics;
180    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
181    let expanded = if let Some((index, field)) = get_field_by_attr(&input.data, "Validator") {
182        let field_name_or_index = field_name_or_index_token(index, field);
183
184        quote! {
185            #[automatically_derived]
186            impl #impl_generics ::rustyline::validate::Validator for #name #ty_generics #where_clause {
187                fn validate(
188                    &self,
189                    ctx: &mut ::rustyline::validate::ValidationContext,
190                ) -> ::rustyline::Result<::rustyline::validate::ValidationResult> {
191                    ::rustyline::validate::Validator::validate(&self.#field_name_or_index, ctx)
192                }
193
194                fn validate_while_typing(&self) -> bool {
195                    ::rustyline::validate::Validator::validate_while_typing(&self.#field_name_or_index)
196                }
197            }
198        }
199    } else {
200        quote! {
201            #[automatically_derived]
202            impl #impl_generics ::rustyline::validate::Validator for #name #ty_generics #where_clause {
203            }
204        }
205    };
206    TokenStream::from(expanded)
207}