expand.rs 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. use crate::{error::Error, include_dir::IncludeDir, DEFAULT_CONFIG_FILE};
  2. use proc_macro2::TokenStream;
  3. use quote::quote;
  4. use std::{
  5. collections::HashSet,
  6. env::var,
  7. fs::File,
  8. io::BufReader,
  9. path::{Path, PathBuf},
  10. };
  11. use syn::{DeriveInput, Lit::Str, Meta::NameValue, MetaNameValue};
  12. use tauri_utils::{assets::AssetCompression, config::Config};
  13. pub(crate) fn load_context(input: DeriveInput) -> Result<TokenStream, Error> {
  14. let name = input.ident;
  15. // quick way of parsing #[config_path = "path_goes_here"]
  16. let mut config_file_path = DEFAULT_CONFIG_FILE.into();
  17. let config_path_attr = input
  18. .attrs
  19. .iter()
  20. .find(|attr| attr.path.is_ident("config_path"));
  21. if let Some(attr) = config_path_attr {
  22. if let Ok(NameValue(MetaNameValue { lit: Str(path), .. })) = attr.parse_meta() {
  23. config_file_path = path.value()
  24. }
  25. }
  26. // grab the manifest of the application the macro is in
  27. let manifest = var("CARGO_MANIFEST_DIR")
  28. .map(PathBuf::from)
  29. .map_err(|_| Error::EnvCargoManifestDir)?;
  30. let full_config_path = Path::new(&manifest).join(config_file_path);
  31. let config = get_config(&full_config_path)?;
  32. let config_dir = full_config_path.parent().ok_or(Error::ConfigDir)?;
  33. let dist_dir = config_dir.join(config.build.dist_dir);
  34. // generate the assets into a perfect hash function
  35. let assets = generate_asset_map(&dist_dir)?;
  36. let tauri_script_path = dist_dir.join("__tauri.js");
  37. // format paths into a string to use them in quote!
  38. let tauri_config_path = full_config_path.display().to_string();
  39. let tauri_script_path = tauri_script_path.display().to_string();
  40. Ok(quote! {
  41. impl ::tauri::api::private::AsTauriContext for #name {
  42. fn config_path() -> &'static std::path::Path {
  43. std::path::Path::new(#tauri_config_path)
  44. }
  45. /// Make the file a dependency for the compiler
  46. fn raw_config() -> &'static str {
  47. include_str!(#tauri_config_path)
  48. }
  49. fn assets() -> &'static ::tauri::api::assets::Assets {
  50. use ::tauri::api::assets::{Assets, AssetCompression, phf, phf::phf_map};
  51. static ASSETS: Assets = Assets::new(#assets);
  52. &ASSETS
  53. }
  54. /// Make the __tauri.js a dependency for the compiler
  55. fn raw_tauri_script() -> &'static str {
  56. include_str!(#tauri_script_path)
  57. }
  58. }
  59. })
  60. }
  61. fn get_config(path: &Path) -> Result<Config, Error> {
  62. match var("TAURI_CONFIG") {
  63. Ok(custom_config) => {
  64. serde_json::from_str(&custom_config).map_err(|e| Error::Serde("TAURI_CONFIG".into(), e))
  65. }
  66. Err(_) => {
  67. let file = File::open(&path).map_err(|e| Error::Io(path.into(), e))?;
  68. let reader = BufReader::new(file);
  69. serde_json::from_reader(reader).map_err(|e| Error::Serde(path.into(), e))
  70. }
  71. }
  72. }
  73. /// Generates a perfect hash function from `phf` of the assets in dist directory
  74. ///
  75. /// The `TokenStream` produced by this function expects to have `phf` and
  76. /// `phf_map` paths available. Make sure to `use` these so the macro has access to them.
  77. /// It also expects `AssetCompression` to be in path.
  78. fn generate_asset_map(dist: &Path) -> Result<TokenStream, Error> {
  79. let mut inline_assets = HashSet::new();
  80. if let Ok(assets) = std::env::var("TAURI_INLINED_ASSETS") {
  81. assets
  82. .split('|')
  83. .filter(|&s| !s.trim().is_empty())
  84. .map(PathBuf::from)
  85. .for_each(|path| {
  86. inline_assets.insert(path);
  87. })
  88. }
  89. IncludeDir::new(&dist)
  90. .dir(&dist, AssetCompression::Gzip)?
  91. .set_filter(inline_assets)?
  92. .build()
  93. }