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}