context.rs 4.5 KB

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