1#![recursion_limit="128"]
29
30#[macro_use]
31extern crate lazy_static;
32extern crate proc_macro;
33extern crate proc_macro2;
34#[macro_use]
35extern crate quote;
36#[macro_use]
37extern crate syn;
38
39mod adder;
40mod gen;
41mod parser;
42mod transformer;
43mod walker;
44
45use std::collections::{HashMap, HashSet};
46
47use proc_macro2::{Span, TokenStream};
48use quote::ToTokens;
49use syn::{
50 ArgCaptured,
51 Generics,
52 Ident,
53 ImplItem,
54 ImplItemMethod,
55 ItemImpl,
56 Macro,
57 MethodSig,
58 Path,
59 ReturnType,
60 TypePath,
61 parse,
62};
63use syn::FnArg::{self, Captured};
64use syn::fold::Fold;
65use syn::ImplItem::{Const, Existential, Method, Verbatim};
66use syn::Item::{self, Impl};
67use syn::parse::Result;
68use syn::spanned::Spanned;
69use syn::Type;
70use syn::visit::Visit;
71
72use adder::{Adder, Message, Property};
73use gen::gen;
74pub use gen::gen_where_clause;
75use parser::EitherWidget::{Gtk, Relm};
76use parser::{Widget, parse_widget};
77use walker::ModelVariableVisitor;
78
79const MODEL_IDENT: &str = "__relm_model";
80
81type MsgModelMap = HashMap<Ident, HashSet<Message>>;
82type PropertyModelMap = HashMap<Ident, HashSet<Property>>;
83
84#[derive(Debug)]
85pub struct Driver {
86 data_method: Option<ImplItem>,
87 generic_types: Option<Generics>,
88 model_type: Option<ImplItem>,
89 model_param_type: Option<ImplItem>,
90 msg_model_map: Option<MsgModelMap>,
91 msg_type: Option<ImplItem>,
92 other_methods: Vec<ImplItem>,
93 properties_model_map: Option<PropertyModelMap>,
94 root_method: Option<ImplItem>,
95 root_type: Option<ImplItem>,
96 root_widget: Option<Ident>,
97 root_widget_expr: Option<TokenStream>,
98 root_widget_type: Option<TokenStream>,
99 update_method: Option<ImplItem>,
100 view_macro: Option<Macro>,
101 widget_model_type: Option<Type>,
102 widget_msg_type: Option<Type>,
103 widget_parent_id: Option<String>,
104 widgets: HashMap<Ident, TokenStream>, }
106
107struct View {
108 container_impl: TokenStream,
109 item: ImplItem,
110 msg_model_map: MsgModelMap,
111 properties_model_map: PropertyModelMap,
112 relm_widgets: HashMap<Ident, Path>,
113 widget: Widget,
114}
115
116impl Driver {
117 fn new() -> Self {
118 Driver {
119 data_method: None,
120 generic_types: None,
121 model_type: None,
122 model_param_type: None,
123 msg_model_map: None,
124 msg_type: None,
125 other_methods: vec![],
126 properties_model_map: None,
127 root_method: None,
128 root_type: None,
129 root_widget: None,
130 root_widget_expr: None,
131 root_widget_type: None,
132 update_method: None,
133 view_macro: None,
134 widget_model_type: None,
135 widget_msg_type: None,
136 widget_parent_id: None,
137 widgets: HashMap::new(),
138 }
139 }
140
141 fn add_set_property_to_method(&self, func: &mut ImplItem) {
142 if let Method(ImplItemMethod { ref mut block, .. }) = *func {
143 let msg_map = self.msg_model_map.as_ref().expect("update method");
144 let property_map = self.properties_model_map.as_ref().expect("update method");
145 let mut adder = Adder::new(property_map, msg_map);
146 *block = adder.fold_block(block.clone());
147 }
148 }
149
150 fn add_widgets(&mut self, widget: &Widget, map: &PropertyModelMap) {
151 let mut to_add = false;
153 for values in map.values() {
154 for value in values {
155 if value.widget_name == widget.name {
156 to_add = true;
157 }
158 }
159 }
160 if to_add {
161 let widget_type = &widget.typ;
162 let typ = quote! {
163 #widget_type
164 };
165 self.widgets.insert(widget.name.clone(), typ);
166 }
167 for child in &widget.children {
168 self.add_widgets(child, map);
169 }
170 }
171
172 fn create_struct(&self, typ: &Type, relm_widgets: &HashMap<Ident, Path>, generics: &Generics) -> TokenStream {
173 let where_clause = gen_where_clause(generics);
174 let widgets = self.widgets.iter().filter(|&(ident, _)| !relm_widgets.contains_key(ident))
175 .map(|(ident, tokens)| (ident.clone(), tokens));
176 let (idents, types): (Vec<Ident>, Vec<_>) = widgets.unzip();
177 let idents = &idents;
178 let types = &types;
179 let relm_idents = relm_widgets.keys();
180 let relm_types = relm_widgets.values();
181 let widget_model_type = self.widget_model_type.as_ref().expect("missing model method");
182 let widgets = {
183 let relm_idents = relm_widgets.keys();
184 let relm_types = relm_widgets.values();
185 let name = Ident::new(&format!("__{}Widgets", get_name(&typ)), Span::call_site());
186 quote! {
187 pub struct #name {
188 #(pub #relm_idents: #relm_types,)*
189 }
190 }
191 };
192 quote_spanned! { typ.span() =>
193 #[allow(dead_code, missing_docs)]
194 pub struct #typ #where_clause {
195 #(#idents: #types,)*
196 #(#relm_idents: #relm_types,)*
197 model: #widget_model_type,
198 }
199
200 #widgets
201 }
202 }
203
204 fn gen_widget(&mut self, input: TokenStream) -> TokenStream {
205 let mut ast: Item = parse(input.into()).expect("parse_item() in gen_widget()");
206 if let Impl(ItemImpl { attrs, defaultness, unsafety, impl_token, generics, trait_, self_ty, items, brace_token }
207 ) = ast
208 {
209 self.generic_types = Some(generics.clone());
210 let name = get_name(&self_ty);
211 let mut new_items = vec![];
212 let mut update_items = vec![];
213 for item in items {
214 let mut i = item.clone();
215 match item {
216 Const(..) => panic!("Unexpected const item"),
217 Existential(..) => panic!("Unexpected existential item"),
218 ImplItem::Macro(mac) => self.view_macro = Some(mac.mac),
219 Method(ImplItemMethod { sig, .. }) => {
220 match sig.ident.to_string().as_ref() {
221 "parent_id" => self.data_method = Some(i),
222 "root" => self.root_method = Some(i),
223 "model" => {
224 self.widget_model_type = Some(get_return_type(sig));
225 add_model_param(&mut i, &mut self.model_param_type);
226 update_items.push(i);
227 },
228 "subscriptions" => update_items.push(i),
229 "init_view" | "on_add" => new_items.push(i),
230 "update" => {
231 self.widget_msg_type = Some(get_second_param_type(&sig));
232 self.update_method = Some(i)
233 },
234 _ => self.other_methods.push(i),
235 }
236 },
237 ImplItem::Type(typ) => {
238 match typ.ident.to_string().as_ref() {
239 "Root" => self.root_type = Some(i),
240 "Model" => self.model_type = Some(i),
241 "ModelParam" => self.model_param_type = Some(i),
242 "Msg" => self.msg_type = Some(i),
243 _ => panic!("Unexpected type item {:?}", typ.ident),
244 }
245 },
246 Verbatim(..) => panic!("Unexpected verbatim item"),
247 }
248 }
249 let view =
250 match self.get_view(&name, &self_ty) {
251 Ok(view) => view,
252 Err(error) => return error.to_compile_error(),
253 };
254 if let Some(on_add) = gen_set_child_prop_calls(&view.widget) {
255 new_items.push(on_add);
256 }
257 self.msg_model_map = Some(view.msg_model_map);
258 self.properties_model_map = Some(view.properties_model_map);
259 new_items.push(view.item);
260 self.widgets.insert(self.root_widget.clone().expect("root widget"),
261 self.root_widget_type.clone().expect("root widget type"));
262 let widget_struct = self.create_struct(&self_ty, &view.relm_widgets, &generics);
263 new_items.push(self.get_root_type());
264 if let Some(data_method) = self.get_data_method() {
265 new_items.push(data_method);
266 }
267 new_items.push(self.get_root());
268 let other_methods = self.get_other_methods(&self_ty, &generics);
269 let update_impl = self.update_impl(&self_ty, &generics, update_items);
270 let widget_test_impl = self.widget_test_impl(&self_ty, &generics, &view.relm_widgets);
271 let item = Impl(ItemImpl { attrs, defaultness, unsafety, generics, impl_token, trait_, self_ty, brace_token,
272 items: new_items });
273 ast = item;
274 let container_impl = view.container_impl;
275 quote! {
276 #widget_struct
277 #ast
278 #container_impl
279 #update_impl
280 #widget_test_impl
281
282 #other_methods
283 }
284 }
285 else {
286 panic!("Expected impl");
287 }
288 }
289
290 fn get_data_method(&mut self) -> Option<ImplItem> {
291 self.data_method.take().or_else(|| {
292 if let Some(ref parent_id) = self.widget_parent_id {
293 Some(block_to_impl_item(quote! {
294 fn parent_id() -> Option<&'static str> {
295 Some(#parent_id)
296 }
297 }))
298 }
299 else {
300 None
301 }
302 })
303 }
304
305 fn get_model_param_type(&mut self) -> ImplItem {
306 self.model_param_type.take().unwrap_or_else(|| {
307 block_to_impl_item(quote! {
308 type ModelParam = ();
309 })
310 })
311 }
312
313 fn get_model_type(&mut self) -> ImplItem {
314 self.model_type.take().unwrap_or_else(|| {
315 let widget_model_type = self.widget_model_type.take().expect("missing model method");
316 block_to_impl_item(quote! {
317 type Model = #widget_model_type;
318 })
319 })
320 }
321
322 fn get_msg_type(&mut self) -> ImplItem {
323 self.msg_type.take().unwrap_or_else(|| {
324 let widget_msg_type = self.widget_msg_type.take().expect("missing update method");
325 block_to_impl_item(quote! {
326 type Msg = #widget_msg_type;
327 })
328 })
329 }
330
331 fn get_other_methods(&mut self, typ: &Type, generics: &Generics) -> TokenStream {
332 let mut other_methods: Vec<_> = self.other_methods.drain(..).collect();
333 let where_clause = gen_where_clause(generics);
334 for method in &mut other_methods {
335 self.add_set_property_to_method(method);
336 }
337 quote! {
338 impl #generics #typ #where_clause {
339 #(#other_methods)*
340 }
341 }
342 }
343
344 fn get_root(&mut self) -> ImplItem {
345 self.root_method.take().unwrap_or_else(|| {
346 let root_widget_expr = self.root_widget_expr.take().expect("root widget expr");
347 block_to_impl_item(quote! {
348 fn root(&self) -> Self::Root {
349 self.#root_widget_expr.clone()
350 }
351 })
352 })
353 }
354
355 fn get_root_type(&mut self) -> ImplItem {
356 self.root_type.take().unwrap_or_else(|| {
357 let root_widget_type = self.root_widget_type.take().expect("root widget type");
358 block_to_impl_item(quote! {
359 type Root = #root_widget_type;
360 })
361 })
362 }
363
364 fn get_update(&mut self) -> ImplItem {
369 let mut func = self.update_method.take().expect("update method");
370 self.add_set_property_to_method(&mut func);
371 func
373 }
374
375 fn get_view(&mut self, name: &Ident, typ: &Type) -> Result<View> {
376 self.view_validation_before_impl();
379 self.impl_view(name, typ)
380 }
381
382 fn view_validation_before_impl(&mut self) {
383 }
423
424 fn impl_view(&mut self, name: &Ident, typ: &Type) -> Result<View> {
425 let tts = self.view_macro.take().expect("view_macro in impl_view()").tts;
426 let mut widget = parse_widget(tts)?;
427 if let Gtk(ref mut widget) = widget.widget {
428 widget.relm_name = Some(typ.clone());
429 }
430 self.widget_parent_id = widget.parent_id.clone();
431 let mut msg_model_map = HashMap::new();
432 let mut properties_model_map = HashMap::new();
433 get_properties_model_map(&widget, &mut properties_model_map);
434 get_msg_model_map(&widget, &mut msg_model_map);
435 self.add_widgets(&widget, &properties_model_map);
436 let (view, relm_widgets, container_impl) = gen(name, &widget, self);
437 let model_ident = Ident::new(MODEL_IDENT, Span::call_site()); let code = quote_spanned! { name.span() =>
439 #[allow(unused_variables)] fn view(relm: &::relm::Relm<Self>, #model_ident: Self::Model) -> Self {
441 #view
442 }
443 };
444 let item = block_to_impl_item(code);
445 Ok(View {
446 container_impl,
447 item,
448 msg_model_map,
449 properties_model_map,
450 relm_widgets,
451 widget,
452 })
453 }
454
455 fn update_impl(&mut self, typ: &Type, generics: &Generics, items: Vec<ImplItem>) -> TokenStream {
456 let where_clause = gen_where_clause(generics);
457
458 let msg = self.get_msg_type();
459 let model_param = self.get_model_param_type();
460 let update = self.get_update();
461 let model = self.get_model_type();
462 quote_spanned! { typ.span() =>
463 impl #generics ::relm::Update for #typ #where_clause {
464 #msg
465 #model
466 #model_param
467 #update
468 #(#items)*
469 }
470 }
471 }
472
473 fn widget_test_impl(&self, typ: &Type, generics: &Generics, relm_widgets: &HashMap<Ident, Path>) -> TokenStream {
474 let name = Ident::new(&format!("__{}Widgets", get_name(&typ)), Span::call_site());
475 let where_clause = gen_where_clause(generics);
476 let mut relm_idents = quote! { };
477 for token in relm_widgets.keys().map(|ident| ident.clone().into_token_stream()) {
478 relm_idents = quote_spanned! { typ.span() =>
479 #relm_idents
480 #token: self.#token.clone(),
481 };
482 }
483 quote_spanned! { typ.span() =>
484 impl #generics ::relm::WidgetTest for #typ #where_clause {
485 type Widgets = #name;
486
487 fn get_widgets(&self) -> #name {
488 #name {
489 #relm_idents
490 }
491 }
492 }
493 }
494 }
495}
496
497pub fn gen_widget(input: TokenStream) -> TokenStream {
498 let mut driver = Driver::new();
499 driver.gen_widget(input)
500}
501
502fn add_model_param(model_fn: &mut ImplItem, model_param_type: &mut Option<ImplItem>) {
503 let span = model_fn.span();
504 if let Method(ImplItemMethod { ref mut sig, .. }) = *model_fn {
505 let len = sig.decl.inputs.len();
506 if len == 0 || len == 1 {
507 let type_tokens = quote_spanned! { span =>
508 &::relm::Relm<Self>
509 };
510 let ty: Type = parse(type_tokens.into()).expect("Relm type");
511 let input: FnArg = parse(quote! { _: #ty }.into()).expect("wild arg");
512 sig.decl.inputs.insert(0, input);
513 if len == 0 {
514 let input: FnArg = parse(quote! { _: () }.into()).expect("wild arg");
515 sig.decl.inputs.push(input);
516 }
517 }
518 if let Some(&Captured(ArgCaptured { ref ty, .. })) = sig.decl.inputs.iter().nth(1) {
519 *model_param_type = Some(block_to_impl_item(quote! {
520 type ModelParam = #ty;
521 }));
522 }
523 }
524}
525
526fn block_to_impl_item(tokens: TokenStream) -> ImplItem {
527 let implementation = quote! {
528 impl Test {
529 #tokens
530 }
531 };
532 let implementation: Item = parse(implementation.into()).expect("parse_item in block_to_impl_item");
533 match implementation {
534 Impl(ItemImpl { items, .. }) => items[0].clone(),
535 _ => unreachable!(),
536 }
537}
538
539fn get_name(typ: &Type) -> Ident {
540 if let Type::Path(TypePath { ref path, .. }) = *typ {
541 let mut parts = vec![];
542 for segment in &path.segments {
543 parts.push(segment.ident.to_string());
544 }
545 Ident::new(&parts.join("::"), typ.span())
546 }
547 else {
548 panic!("Expected Path")
549 }
550}
551
552macro_rules! get_map {
553 ($widget:expr, $map:expr, $is_relm:expr) => {{
554 for (name, expr) in &$widget.properties {
555 let mut visitor = ModelVariableVisitor::new();
556 visitor.visit_expr(&expr);
557 let model_variables = visitor.idents;
558 for var in model_variables {
559 let set = $map.entry(var).or_insert_with(HashSet::new);
560 set.insert(Property {
561 expr: expr.clone(),
562 is_relm_widget: $is_relm,
563 name: name.clone(),
564 widget_name: $widget.name.clone(),
565 });
566 }
567 }
568 for child in &$widget.children {
569 get_properties_model_map(child, $map);
570 }
571 }};
572}
573
574fn get_msg_model_map(widget: &Widget, map: &mut MsgModelMap) {
575 match widget.widget {
576 Gtk(_) => {
577 for child in &widget.children {
578 get_msg_model_map(child, map);
579 }
580 },
581 Relm(ref relm_widget) => {
582 for (name, expr) in &relm_widget.messages {
583 let mut visitor = ModelVariableVisitor::new();
584 visitor.visit_expr(&expr);
585 let model_variables = visitor.idents;
586 for var in model_variables {
587 let set = map.entry(var).or_insert_with(HashSet::new);
588 set.insert(Message {
589 expr: expr.clone(),
590 name: name.clone(),
591 widget_name: widget.name.clone(),
592 });
593 }
594 }
595 for child in &widget.children {
596 get_msg_model_map(child, map);
597 }
598 },
599 }
600}
601
602fn get_properties_model_map(widget: &Widget, map: &mut PropertyModelMap) {
606 match widget.widget {
607 Gtk(_) => get_map!(widget, map, false),
608 Relm(_) => get_map!(widget, map, true),
609 }
610}
611
612fn get_return_type(sig: MethodSig) -> Type {
613 if let ReturnType::Type(_, ty) = sig.decl.output {
614 *ty
615 }
616 else {
617 Type::Tuple(syn::TypeTuple {
618 paren_token: syn::token::Paren::default(),
619 elems: syn::punctuated::Punctuated::new()
620 })
621 }
622}
623
624fn get_second_param_type(sig: &MethodSig) -> Type {
625 if let Captured(ArgCaptured { ref ty, .. }) = sig.decl.inputs[1] {
626 ty.clone()
627 }
628 else {
629 panic!("Unexpected `(unknown)`, expecting Captured Type"); }
631}
632
633fn gen_set_child_prop_calls(widget: &Widget) -> Option<ImplItem> {
634 let mut tokens = quote! {};
635 let widget_name = &widget.name;
636 for (&(ref ident, ref key), value) in &widget.child_properties {
637 let property_func = Ident::new(&format!("set_{}_{}", ident, key), key.span());
638 tokens = quote_spanned! { widget_name.span() =>
639 #tokens
640 parent.#property_func(&self.#widget_name, #value);
641 };
642 }
643 if !widget.child_properties.is_empty() {
644 Some(block_to_impl_item(quote_spanned! { widget_name.span() =>
645 fn on_add<W: ::gtk::IsA<::gtk::Widget> + ::gtk::IsA<::gtk::Object>>(&self, parent: W) {
646 let parent: gtk::Box = ::gtk::Cast::downcast(::gtk::Cast::upcast::<::gtk::Widget>(parent))
647 .expect("the parent of a widget with child properties must be a gtk::Box");
648 #tokens
649 }
650 }))
651 }
652 else {
653 None
654 }
655}
656
657#[cfg(test)]
658mod tests {
659 use super::*;
660 use syn::parse_expr;
661 use syn::{ExprKind};
662
663 #[test]
664 #[should_panic(expected = "Expected `view!` macro, found `foo` instead")]
665 fn incorrect_view_macro_name() {
666 let macro_text = "foo! {
667 gtk::Window {}
668 }";
669 let parsed_expr: ExprKind = parse_expr(macro_text)
670 .expect("incorrect_view_macro_name > parse_expr failed").node;
671 let mac = match parsed_expr {
672 ExprKind::Mac(mac) => mac,
673 _ => panic!("Expected ExprKind::Mac(mac), found {:#?}", parsed_expr),
674 };
675 let mut driver = Driver::new();
676 driver.view_macro = Some(mac);
677 driver.view_validation_before_impl();
678 }
679
680 #[test]
681 #[should_panic(expected = "`view!` macro is empty, must contain one top-level item")]
682 fn empty_view_macro() {
683 let macro_text = "view! {
684 }";
685 let parsed_expr: ExprKind = parse_expr(macro_text)
686 .expect("empty_view_macro > parse_expr failed").node;
687 let mac = match parsed_expr {
688 ExprKind::Mac(mac) => mac,
689 _ => panic!("Expected ExprKind::Mac(mac), found {:#?}", parsed_expr),
690 };
691 let mut driver = Driver::new();
692 driver.view_macro = Some(mac);
693 driver.view_validation_before_impl();
694 }
695
696 #[test]
697 #[should_panic(expected = "There may only be one top-level item in `view!`")]
698 fn multiple_top_level_items() {
699 let macro_text = "view! {
700 gtk::Window {},
701 gtk::Window {}
702 }";
703 let parsed_expr: ExprKind = parse_expr(macro_text)
704 .expect("multiple_top_level_items > parse_expr failed").node;
705 let mac = match parsed_expr {
706 ExprKind::Mac(mac) => mac,
707 _ => panic!("Expected ExprKind::Mac(mac), found {:#?}", parsed_expr),
708 };
709 let mut driver = Driver::new();
710 driver.view_macro = Some(mac);
711 driver.view_validation_before_impl();
712 }
713}