hyprshell_config_lib/
load.rs1use crate::Config;
2use crate::migrate::check_migration_needed;
3use anyhow::{Context, bail};
4use ron::Options;
5use ron::extensions::Extensions;
6use serde::de::DeserializeOwned;
7use std::ffi::OsStr;
8use std::path::Path;
9use tracing::{debug, debug_span, info, trace, warn};
10
11pub fn load_and_migrate_config(config_path: &Path, allow_migrate: bool) -> anyhow::Result<Config> {
12 let _span = debug_span!("load_config", path =? config_path).entered();
13 if !config_path.exists() {
14 bail!("Config file does not exist, create it using `hyprshell config generate`");
15 }
16
17 if check_migration_needed(config_path)
18 .inspect_err(|e| warn!("Failed to check if migration is needed: {e:?}"))
19 .unwrap_or(false)
20 {
21 info!("Config needs migration");
22 if !allow_migrate {
23 bail!("Config file needs migration, but migration is not allowed.");
24 }
25 let migrated = crate::migrate::migrate(config_path);
26 match migrated {
27 Ok(config) => {
28 info!("Config migrated successfully");
29 crate::check(&config)?;
30 return Ok(config);
31 }
32 Err(err) => {
33 warn!("Migration failed: \n{err:?}");
34 }
35 }
36 } else {
37 trace!("No migration needed");
38 }
39
40 let config: Config = load_config_file(config_path).with_context(|| {
41 format!(
42 "Failed to load config from file ({})",
43 config_path.display()
44 )
45 })?;
46 debug!("Loaded config");
47
48 crate::check(&config)?;
49
50 Ok(config)
51}
52
53pub fn load_config_file<T: DeserializeOwned>(config_path: &Path) -> anyhow::Result<T> {
54 let config_path_display = config_path.display();
55 match config_path.extension().and_then(OsStr::to_str) {
56 None | Some("ron") => {
57 let options = Options::default()
58 .with_default_extension(Extensions::IMPLICIT_SOME)
59 .with_default_extension(Extensions::UNWRAP_NEWTYPES)
60 .with_default_extension(Extensions::UNWRAP_VARIANT_NEWTYPES);
61 let file = std::fs::File::open(config_path)
62 .with_context(|| format!("Failed to open RON config at ({config_path_display})"))?;
63 options
64 .from_reader(file)
65 .with_context(|| format!("Failed to read RON config at ({config_path_display})"))
66 }
67 #[cfg(not(feature = "json5_config"))]
68 Some("json") => {
69 let file = std::fs::File::open(config_path).with_context(|| {
70 format!("Failed to open JSON5 config at ({config_path_display})")
71 })?;
72 serde_json::from_reader(file)
73 .with_context(|| format!("Failed to read JSON5 config at ({config_path_display})"))
74 }
75 #[cfg(feature = "json5_config")]
76 Some("json5" | "json") => {
77 let file = std::fs::File::open(config_path).with_context(|| {
78 format!("Failed to open JSON5 config at ({config_path_display})")
79 })?;
80 serde_json5::from_reader(file)
81 .with_context(|| format!("Failed to read JSON5 config at ({config_path_display})"))
82 }
83 Some("toml") => {
84 use std::io::Read;
85 let mut file = std::fs::File::open(config_path).with_context(|| {
86 format!("Failed to open TOML config at ({config_path_display})")
87 })?;
88 let mut content = String::new();
89 file.read_to_string(&mut content).with_context(|| {
90 format!("Failed to read TOML config at ({config_path_display})")
91 })?;
92 toml::from_str(&content).context("Failed to parse TOML config")
93 }
94 Some(ext) => bail!(
95 "Invalid config file extension: {} (run with -vv and check `FEATURES: ` debug log to see enabled extensions)",
96 ext
97 ),
98 }
99}