1#![doc(
9 html_logo_url = "https://github.com/tauri-apps/tauri/raw/dev/.github/icon.png",
10 html_favicon_url = "https://github.com/tauri-apps/tauri/raw/dev/.github/icon.png"
11)]
12
13pub use self::context::{ContextData, context_codegen};
14use crate::embedded_assets::{EmbeddedAssetsError, ensure_out_dir};
15use proc_macro2::TokenStream;
16use quote::{ToTokens, TokenStreamExt, quote};
17use std::{
18 borrow::Cow,
19 fmt::{self, Write},
20 path::{Path, PathBuf},
21};
22pub use tauri_utils::config::{Config, parse::ConfigError};
23use tauri_utils::platform::Target;
24use tauri_utils::write_if_changed;
25
26mod context;
27pub mod embedded_assets;
28pub mod image;
29#[doc(hidden)]
30pub mod vendor;
31
32#[derive(Debug, thiserror::Error)]
34#[non_exhaustive]
35pub enum CodegenConfigError {
36 #[error("unable to access current working directory: {0}")]
37 CurrentDir(std::io::Error),
38
39 #[error(
41 "Tauri config file has no parent, this shouldn't be possible. file an issue on https://github.com/tauri-apps/tauri - target {0}"
42 )]
43 Parent(PathBuf),
44
45 #[error("unable to parse inline JSON TAURI_CONFIG env var: {0}")]
46 FormatInline(serde_json::Error),
47
48 #[error(transparent)]
49 Json(#[from] serde_json::Error),
50
51 #[error("{0}")]
52 ConfigError(#[from] ConfigError),
53}
54
55pub fn get_config(path: &Path) -> Result<(Config, PathBuf), CodegenConfigError> {
60 let path = if path.is_relative() {
61 let cwd = std::env::current_dir().map_err(CodegenConfigError::CurrentDir)?;
62 Cow::Owned(cwd.join(path))
63 } else {
64 Cow::Borrowed(path)
65 };
66
67 let parent = path
69 .parent()
70 .map(ToOwned::to_owned)
71 .ok_or_else(|| CodegenConfigError::Parent(path.into_owned()))?;
72
73 let target = std::env::var("TAURI_ENV_TARGET_TRIPLE")
74 .as_deref()
75 .map(Target::from_triple)
76 .unwrap_or_else(|_| Target::current());
77
78 let mut config =
83 serde_json::from_value(tauri_utils::config::parse::read_from(target, &parent)?.0)?;
84
85 if let Ok(env) = std::env::var("TAURI_CONFIG") {
86 let merge_config: serde_json::Value =
87 serde_json::from_str(&env).map_err(CodegenConfigError::FormatInline)?;
88 json_patch::merge(&mut config, &merge_config);
89 }
90
91 let old_cwd = std::env::current_dir().map_err(CodegenConfigError::CurrentDir)?;
93 std::env::set_current_dir(parent.clone()).map_err(CodegenConfigError::CurrentDir)?;
94
95 let config = serde_json::from_value(config)?;
96
97 std::env::set_current_dir(old_cwd).map_err(CodegenConfigError::CurrentDir)?;
99
100 Ok((config, parent))
101}
102
103fn checksum(bytes: &[u8]) -> Result<String, fmt::Error> {
105 let mut hasher = vendor::blake3_reference::Hasher::default();
106 hasher.update(bytes);
107
108 let mut bytes = [0u8; 32];
109 hasher.finalize(&mut bytes);
110
111 let mut hex = String::with_capacity(2 * bytes.len());
112 for b in bytes {
113 write!(hex, "{b:02x}")?;
114 }
115 Ok(hex)
116}
117
118struct Cached {
123 checksum: String,
124}
125
126impl TryFrom<String> for Cached {
127 type Error = EmbeddedAssetsError;
128
129 fn try_from(value: String) -> Result<Self, Self::Error> {
130 Self::try_from(Vec::from(value))
131 }
132}
133
134impl TryFrom<Vec<u8>> for Cached {
135 type Error = EmbeddedAssetsError;
136
137 fn try_from(content: Vec<u8>) -> Result<Self, Self::Error> {
138 let checksum = checksum(content.as_ref()).map_err(EmbeddedAssetsError::Hex)?;
139 let path = ensure_out_dir()?.join(&checksum);
140
141 write_if_changed(&path, &content)
142 .map(|_| Self { checksum })
143 .map_err(|error| EmbeddedAssetsError::AssetWrite { path, error })
144 }
145}
146
147impl ToTokens for Cached {
148 fn to_tokens(&self, tokens: &mut TokenStream) {
149 let path = &self.checksum;
150 tokens.append_all(quote!(::std::concat!(::std::env!("OUT_DIR"), "/", #path)))
151 }
152}