context.rs 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  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, ToTokens};
  6. use std::path::PathBuf;
  7. use syn::{
  8. parse::{Parse, ParseBuffer},
  9. punctuated::Punctuated,
  10. Expr, ExprLit, Lit, LitBool, LitStr, Meta, PathArguments, PathSegment, Token,
  11. };
  12. use tauri_codegen::{context_codegen, get_config, ContextData};
  13. use tauri_utils::{config::parse::does_supported_file_name_exist, platform::Target};
  14. pub(crate) struct ContextItems {
  15. config_file: PathBuf,
  16. root: syn::Path,
  17. capabilities: Option<Vec<PathBuf>>,
  18. assets: Option<Expr>,
  19. test: bool,
  20. }
  21. impl Parse for ContextItems {
  22. fn parse(input: &ParseBuffer<'_>) -> syn::parse::Result<Self> {
  23. let target = std::env::var("TARGET")
  24. .or_else(|_| std::env::var("TAURI_ENV_TARGET_TRIPLE"))
  25. .as_deref()
  26. .map(Target::from_triple)
  27. .unwrap_or_else(|_| Target::current());
  28. let mut root = None;
  29. let mut capabilities = None;
  30. let mut assets = None;
  31. let mut test = false;
  32. let config_file = input.parse::<LitStr>().ok().map(|raw| {
  33. let _ = input.parse::<Token![,]>();
  34. let path = PathBuf::from(raw.value());
  35. if path.is_relative() {
  36. std::env::var("CARGO_MANIFEST_DIR")
  37. .map(|m| PathBuf::from(m).join(path))
  38. .map_err(|e| e.to_string())
  39. } else {
  40. Ok(path)
  41. }
  42. .and_then(|path| {
  43. if does_supported_file_name_exist(target, &path) {
  44. Ok(path)
  45. } else {
  46. Err(format!(
  47. "no file at path {} exists, expected tauri config file",
  48. path.display()
  49. ))
  50. }
  51. })
  52. });
  53. while let Ok(meta) = input.parse::<Meta>() {
  54. match meta {
  55. Meta::Path(p) => {
  56. root.replace(p);
  57. }
  58. Meta::NameValue(v) => {
  59. let ident = v.path.require_ident()?;
  60. match ident.to_string().as_str() {
  61. "capabilities" => {
  62. if let Expr::Array(array) = v.value {
  63. capabilities.replace(
  64. array
  65. .elems
  66. .into_iter()
  67. .map(|e| {
  68. if let Expr::Lit(ExprLit {
  69. attrs: _,
  70. lit: Lit::Str(s),
  71. }) = e
  72. {
  73. Ok(s.value().into())
  74. } else {
  75. Err(syn::Error::new(
  76. input.span(),
  77. "unexpected expression for capability",
  78. ))
  79. }
  80. })
  81. .collect::<Result<Vec<_>, syn::Error>>()?,
  82. );
  83. } else {
  84. return Err(syn::Error::new(
  85. input.span(),
  86. "unexpected value for capabilities",
  87. ));
  88. }
  89. }
  90. "assets" => {
  91. assets.replace(v.value);
  92. }
  93. "test" => {
  94. if let Expr::Lit(ExprLit {
  95. lit: Lit::Bool(LitBool { value, .. }),
  96. ..
  97. }) = v.value
  98. {
  99. test = value;
  100. } else {
  101. return Err(syn::Error::new(input.span(), "unexpected value for test"));
  102. }
  103. }
  104. name => {
  105. return Err(syn::Error::new(
  106. input.span(),
  107. format!("unknown attribute {name}"),
  108. ));
  109. }
  110. }
  111. }
  112. Meta::List(_) => {
  113. return Err(syn::Error::new(input.span(), "unexpected list input"));
  114. }
  115. }
  116. let _ = input.parse::<Token![,]>();
  117. }
  118. Ok(Self {
  119. config_file: config_file
  120. .unwrap_or_else(|| {
  121. std::env::var("CARGO_MANIFEST_DIR")
  122. .map(|m| PathBuf::from(m).join("tauri.conf.json"))
  123. .map_err(|e| e.to_string())
  124. })
  125. .map_err(|e| input.error(e))?,
  126. root: root.unwrap_or_else(|| {
  127. let mut segments = Punctuated::new();
  128. segments.push(PathSegment {
  129. ident: Ident::new("tauri", Span::call_site()),
  130. arguments: PathArguments::None,
  131. });
  132. syn::Path {
  133. leading_colon: Some(Token![::](Span::call_site())),
  134. segments,
  135. }
  136. }),
  137. capabilities,
  138. assets,
  139. test,
  140. })
  141. }
  142. }
  143. pub(crate) fn generate_context(context: ContextItems) -> TokenStream {
  144. let context = get_config(&context.config_file)
  145. .map_err(|e| e.to_string())
  146. .map(|(config, config_parent)| ContextData {
  147. dev: cfg!(not(feature = "custom-protocol")),
  148. config,
  149. config_parent,
  150. root: context.root.to_token_stream(),
  151. capabilities: context.capabilities,
  152. assets: context.assets,
  153. test: context.test,
  154. })
  155. .and_then(|data| context_codegen(data).map_err(|e| e.to_string()));
  156. match context {
  157. Ok(code) => code,
  158. Err(error) => quote!(compile_error!(#error)),
  159. }
  160. }