menu.rs 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. // Copyright 2019-2024 Tauri Programme within The Commons Conservancy
  2. // SPDX-License-Identifier: Apache-2.0
  3. // SPDX-License-Identifier: MIT
  4. use proc_macro2::{Ident, Span, TokenStream};
  5. use quote::quote;
  6. use syn::{
  7. parse::{Parse, ParseStream},
  8. punctuated::Punctuated,
  9. Expr, Token,
  10. };
  11. pub struct DoMenuItemInput {
  12. resources_table: Ident,
  13. rid: Ident,
  14. kind: Ident,
  15. var: Ident,
  16. expr: Expr,
  17. kinds: Vec<NegatedIdent>,
  18. }
  19. #[derive(Clone)]
  20. struct NegatedIdent {
  21. negated: bool,
  22. ident: Ident,
  23. }
  24. impl NegatedIdent {
  25. fn new(ident: &str) -> Self {
  26. Self {
  27. negated: false,
  28. ident: Ident::new(ident, Span::call_site()),
  29. }
  30. }
  31. fn is_negated(&self) -> bool {
  32. self.negated
  33. }
  34. }
  35. impl Parse for NegatedIdent {
  36. fn parse(input: ParseStream) -> syn::Result<Self> {
  37. let negated_token = input.parse::<Token![!]>();
  38. let ident: Ident = input.parse()?;
  39. Ok(NegatedIdent {
  40. negated: negated_token.is_ok(),
  41. ident,
  42. })
  43. }
  44. }
  45. impl Parse for DoMenuItemInput {
  46. fn parse(input: ParseStream) -> syn::Result<Self> {
  47. let resources_table: Ident = input.parse()?;
  48. let _: Token![,] = input.parse()?;
  49. let rid: Ident = input.parse()?;
  50. let _: Token![,] = input.parse()?;
  51. let kind: Ident = input.parse()?;
  52. let _: Token![,] = input.parse()?;
  53. let _: Token![|] = input.parse()?;
  54. let var: Ident = input.parse()?;
  55. let _: Token![|] = input.parse()?;
  56. let expr: Expr = input.parse()?;
  57. let _: syn::Result<Token![,]> = input.parse();
  58. let kinds = Punctuated::<NegatedIdent, Token![|]>::parse_terminated(input)?;
  59. Ok(Self {
  60. resources_table,
  61. rid,
  62. kind,
  63. var,
  64. expr,
  65. kinds: kinds.into_iter().collect(),
  66. })
  67. }
  68. }
  69. pub fn do_menu_item(input: DoMenuItemInput) -> TokenStream {
  70. let DoMenuItemInput {
  71. rid,
  72. resources_table,
  73. kind,
  74. expr,
  75. var,
  76. mut kinds,
  77. } = input;
  78. let defaults = vec![
  79. NegatedIdent::new("Submenu"),
  80. NegatedIdent::new("MenuItem"),
  81. NegatedIdent::new("Predefined"),
  82. NegatedIdent::new("Check"),
  83. NegatedIdent::new("Icon"),
  84. ];
  85. if kinds.is_empty() {
  86. kinds.extend(defaults.clone());
  87. }
  88. let has_negated = kinds.iter().any(|n| n.is_negated());
  89. if has_negated {
  90. kinds.extend(defaults);
  91. kinds.sort_by(|a, b| a.ident.cmp(&b.ident));
  92. kinds.dedup_by(|a, b| a.ident == b.ident);
  93. }
  94. let (kinds, types): (Vec<Ident>, Vec<Ident>) = kinds
  95. .into_iter()
  96. .filter_map(|nident| {
  97. if nident.is_negated() {
  98. None
  99. } else {
  100. match nident.ident {
  101. i if i == "MenuItem" => Some((i, Ident::new("MenuItem", Span::call_site()))),
  102. i if i == "Submenu" => Some((i, Ident::new("Submenu", Span::call_site()))),
  103. i if i == "Predefined" => Some((i, Ident::new("PredefinedMenuItem", Span::call_site()))),
  104. i if i == "Check" => Some((i, Ident::new("CheckMenuItem", Span::call_site()))),
  105. i if i == "Icon" => Some((i, Ident::new("IconMenuItem", Span::call_site()))),
  106. _ => None,
  107. }
  108. }
  109. })
  110. .unzip();
  111. quote! {
  112. match #kind {
  113. #(
  114. ItemKind::#kinds => {
  115. let #var = #resources_table.get::<#types<R>>(#rid)?;
  116. #expr
  117. }
  118. )*
  119. _ => unreachable!(),
  120. }
  121. }
  122. }