@@ -11,15 +11,75 @@ use std::path::{Path, PathBuf};
1111use thiserror:: Error ;
1212
1313/// All extensions that are possibly supported, but perhaps not enabled.
14- pub const EXTENSIONS_SUPPORTED : & [ & str ] = & [ "json" , "json5" ] ;
14+ pub const EXTENSIONS_SUPPORTED : & [ & str ] = & [ "json" , "json5" , "toml" ] ;
1515
16- /// All extensions that are currently enabled.
17- pub const EXTENSIONS_ENABLED : & [ & str ] = & [
18- "json" ,
16+ /// All configuration formats that are possibly supported, but perhaps not enabled.
17+ pub const SUPPORTED_FORMATS : & [ ConfigFormat ] =
18+ & [ ConfigFormat :: Json , ConfigFormat :: Json5 , ConfigFormat :: Toml ] ;
19+
20+ /// All configuration formats that are currently enabled.
21+ pub const ENABLED_FORMATS : & [ ConfigFormat ] = & [
22+ ConfigFormat :: Json ,
1923 #[ cfg( feature = "config-json5" ) ]
20- "json5" ,
24+ ConfigFormat :: Json5 ,
25+ #[ cfg( feature = "config-toml" ) ]
26+ ConfigFormat :: Toml ,
2127] ;
2228
29+ /// The available configuration formats.
30+ #[ derive( Debug , Copy , Clone ) ]
31+ pub enum ConfigFormat {
32+ /// The default JSON (tauri.conf.json) format.
33+ Json ,
34+ /// The JSON5 (tauri.conf.json5) format.
35+ Json5 ,
36+ /// The TOML (Tauri.toml file) format.
37+ Toml ,
38+ }
39+
40+ impl ConfigFormat {
41+ /// Maps the config format to its file name.
42+ pub fn into_file_name ( self ) -> & ' static str {
43+ match self {
44+ Self :: Json => "tauri.conf.json" ,
45+ Self :: Json5 => "tauri.conf.json5" ,
46+ Self :: Toml => "Tauri.toml" ,
47+ }
48+ }
49+
50+ fn into_platform_file_name ( self ) -> & ' static str {
51+ match self {
52+ Self :: Json => {
53+ if cfg ! ( target_os = "macos" ) {
54+ "tauri.macos.conf.json"
55+ } else if cfg ! ( windows) {
56+ "tauri.windows.conf.json"
57+ } else {
58+ "tauri.linux.conf.json"
59+ }
60+ }
61+ Self :: Json5 => {
62+ if cfg ! ( target_os = "macos" ) {
63+ "tauri.macos.conf.json5"
64+ } else if cfg ! ( windows) {
65+ "tauri.windows.conf.json5"
66+ } else {
67+ "tauri.linux.conf.json5"
68+ }
69+ }
70+ Self :: Toml => {
71+ if cfg ! ( target_os = "macos" ) {
72+ "Tauri.macos.toml"
73+ } else if cfg ! ( windows) {
74+ "Tauri.windows.toml"
75+ } else {
76+ "Tauri.linux.toml"
77+ }
78+ }
79+ }
80+ }
81+ }
82+
2383/// Represents all the errors that can happen while reading the config.
2484#[ derive( Debug , Error ) ]
2585#[ non_exhaustive]
@@ -45,7 +105,18 @@ pub enum ConfigError {
45105 error : :: json5:: Error ,
46106 } ,
47107
48- /// Unknown file extension encountered.
108+ /// Failed to parse from TOML.
109+ #[ cfg( feature = "config-toml" ) ]
110+ #[ error( "unable to parse toml Tauri config file at {path} because {error}" ) ]
111+ FormatToml {
112+ /// The path that failed to parse into TOML.
113+ path : PathBuf ,
114+
115+ /// The parsing [`toml::Error`].
116+ error : :: toml:: de:: Error ,
117+ } ,
118+
119+ /// Unknown config file name encountered.
49120 #[ error( "unsupported format encountered {0}" ) ]
50121 UnsupportedFormat ( String ) ,
51122
@@ -81,32 +152,21 @@ pub enum ConfigError {
81152///
82153/// [JSON Merge Patch (RFC 7396)]: https://datatracker.ietf.org/doc/html/rfc7396.
83154pub fn read_from ( root_dir : PathBuf ) -> Result < Value , ConfigError > {
84- let mut config: Value = parse_value ( root_dir. join ( "tauri.conf.json" ) ) ?;
85- if let Some ( platform_config) = read_platform ( root_dir) ? {
155+ let mut config: Value = parse_value ( root_dir. join ( "tauri.conf.json" ) ) ?. 0 ;
156+ if let Some ( ( platform_config, _ ) ) = read_platform ( root_dir) ? {
86157 merge ( & mut config, & platform_config) ;
87158 }
88159 Ok ( config)
89160}
90161
91- /// Gets the platform configuration file name.
92- pub fn get_platform_config_filename ( ) -> & ' static str {
93- if cfg ! ( target_os = "macos" ) {
94- "tauri.macos.conf.json"
95- } else if cfg ! ( windows) {
96- "tauri.windows.conf.json"
97- } else {
98- "tauri.linux.conf.json"
99- }
100- }
101-
102162/// Reads the platform-specific configuration file from the given root directory if it exists.
103163///
104164/// Check [`read_from`] for more information.
105- pub fn read_platform ( root_dir : PathBuf ) -> Result < Option < Value > , ConfigError > {
106- let platform_config_path = root_dir. join ( get_platform_config_filename ( ) ) ;
107- if does_supported_extension_exist ( & platform_config_path) {
108- let platform_config: Value = parse_value ( platform_config_path) ?;
109- Ok ( Some ( platform_config) )
165+ pub fn read_platform ( root_dir : PathBuf ) -> Result < Option < ( Value , PathBuf ) > , ConfigError > {
166+ let platform_config_path = root_dir. join ( ConfigFormat :: Json . into_platform_file_name ( ) ) ;
167+ if does_supported_file_name_exist ( & platform_config_path) {
168+ let ( platform_config, path ) : ( Value , PathBuf ) = parse_value ( platform_config_path) ?;
169+ Ok ( Some ( ( platform_config, path ) ) )
110170 } else {
111171 Ok ( None )
112172 }
@@ -116,11 +176,21 @@ pub fn read_platform(root_dir: PathBuf) -> Result<Option<Value>, ConfigError> {
116176///
117177/// The passed path is expected to be the path to the "default" configuration format, in this case
118178/// JSON with `.json`.
119- pub fn does_supported_extension_exist ( path : impl Into < PathBuf > ) -> bool {
179+ pub fn does_supported_file_name_exist ( path : impl Into < PathBuf > ) -> bool {
120180 let path = path. into ( ) ;
121- EXTENSIONS_ENABLED
181+ let source_file_name = path. file_name ( ) . unwrap ( ) . to_str ( ) . unwrap ( ) ;
182+ let lookup_platform_config = ENABLED_FORMATS
122183 . iter ( )
123- . any ( |ext| path. with_extension ( ext) . exists ( ) )
184+ . any ( |format| source_file_name == format. into_platform_file_name ( ) ) ;
185+ ENABLED_FORMATS . iter ( ) . any ( |format| {
186+ path
187+ . with_file_name ( if lookup_platform_config {
188+ format. into_platform_file_name ( )
189+ } else {
190+ format. into_file_name ( )
191+ } )
192+ . exists ( )
193+ } )
124194}
125195
126196/// Parse the config from path, including alternative formats.
@@ -133,18 +203,39 @@ pub fn does_supported_extension_exist(path: impl Into<PathBuf>) -> bool {
133203/// 2. Check if `tauri.conf.json5` exists
134204/// a. Parse it with `json5`
135205/// b. Return error if all above steps failed
136- /// 3. Return error if all above steps failed
137- pub fn parse ( path : impl Into < PathBuf > ) -> Result < Config , ConfigError > {
206+ /// 3. Check if `Tauri.json` exists
207+ /// a. Parse it with `toml`
208+ /// b. Return error if all above steps failed
209+ /// 4. Return error if all above steps failed
210+ pub fn parse ( path : impl Into < PathBuf > ) -> Result < ( Config , PathBuf ) , ConfigError > {
138211 do_parse ( path. into ( ) )
139212}
140213
141214/// See [`parse`] for specifics, returns a JSON [`Value`] instead of [`Config`].
142- pub fn parse_value ( path : impl Into < PathBuf > ) -> Result < Value , ConfigError > {
215+ pub fn parse_value ( path : impl Into < PathBuf > ) -> Result < ( Value , PathBuf ) , ConfigError > {
143216 do_parse ( path. into ( ) )
144217}
145218
146- fn do_parse < D : DeserializeOwned > ( path : PathBuf ) -> Result < D , ConfigError > {
147- let json5 = path. with_extension ( "json5" ) ;
219+ fn do_parse < D : DeserializeOwned > ( path : PathBuf ) -> Result < ( D , PathBuf ) , ConfigError > {
220+ let file_name = path
221+ . file_name ( )
222+ . map ( OsStr :: to_string_lossy)
223+ . unwrap_or_default ( ) ;
224+ let lookup_platform_config = ENABLED_FORMATS
225+ . iter ( )
226+ . any ( |format| file_name == format. into_platform_file_name ( ) ) ;
227+
228+ let json5 = path. with_file_name ( if lookup_platform_config {
229+ ConfigFormat :: Json5 . into_platform_file_name ( )
230+ } else {
231+ ConfigFormat :: Json5 . into_file_name ( )
232+ } ) ;
233+ let toml = path. with_file_name ( if lookup_platform_config {
234+ ConfigFormat :: Toml . into_platform_file_name ( )
235+ } else {
236+ ConfigFormat :: Toml . into_file_name ( )
237+ } ) ;
238+
148239 let path_ext = path
149240 . extension ( )
150241 . map ( OsStr :: to_string_lossy)
@@ -171,19 +262,31 @@ fn do_parse<D: DeserializeOwned>(path: PathBuf) -> Result<D, ConfigError> {
171262 }
172263 } ;
173264
174- json
265+ json. map ( |j| ( j , path ) )
175266 } else if json5. exists ( ) {
176267 #[ cfg( feature = "config-json5" ) ]
177268 {
178269 let raw = read_to_string ( & json5) ?;
179- do_parse_json5 ( & raw , & path)
270+ do_parse_json5 ( & raw , & path) . map ( |config| ( config , json5 ) )
180271 }
181272
182273 #[ cfg( not( feature = "config-json5" ) ) ]
183274 Err ( ConfigError :: DisabledFormat {
184275 extension : ".json5" . into ( ) ,
185276 feature : "config-json5" . into ( ) ,
186277 } )
278+ } else if toml. exists ( ) {
279+ #[ cfg( feature = "config-toml" ) ]
280+ {
281+ let raw = read_to_string ( & toml) ?;
282+ do_parse_toml ( & raw , & path) . map ( |config| ( config, toml) )
283+ }
284+
285+ #[ cfg( not( feature = "config-toml" ) ) ]
286+ Err ( ConfigError :: DisabledFormat {
287+ extension : ".toml" . into ( ) ,
288+ feature : "config-toml" . into ( ) ,
289+ } )
187290 } else if !EXTENSIONS_SUPPORTED . contains ( & path_ext. as_ref ( ) ) {
188291 Err ( ConfigError :: UnsupportedFormat ( path_ext. to_string ( ) ) )
189292 } else {
@@ -241,6 +344,14 @@ fn do_parse_json5<D: DeserializeOwned>(raw: &str, path: &Path) -> Result<D, Conf
241344 } )
242345}
243346
347+ #[ cfg( feature = "config-toml" ) ]
348+ fn do_parse_toml < D : DeserializeOwned > ( raw : & str , path : & Path ) -> Result < D , ConfigError > {
349+ :: toml:: from_str ( raw) . map_err ( |error| ConfigError :: FormatToml {
350+ path : path. into ( ) ,
351+ error,
352+ } )
353+ }
354+
244355/// Helper function to wrap IO errors from [`std::fs::read_to_string`] into a [`ConfigError`].
245356fn read_to_string ( path : & Path ) -> Result < String , ConfigError > {
246357 std:: fs:: read_to_string ( path) . map_err ( |error| ConfigError :: Io {
0 commit comments