hyprshell_config_lib/io/
save.rs1use anyhow::{Context, bail};
2use ron::Options;
3use ron::extensions::Extensions;
4use ron::ser::PrettyConfig;
5use std::ffi::OsStr;
6use std::fs::{File, create_dir_all};
7use std::io::Write;
8use std::path::Path;
9use tracing::{debug, info, instrument};
10
11const CONFIG_EXPLANATION: &str = "Edit with `hyprshell config edit`";
12
13#[instrument(skip(config, config_file), level = "debug", name = "write_config", fields(config_file = %config_file.display()))]
14pub fn write_io_config(
15 config_file: &Path,
16 config: &crate::io::Config,
17 override_file: bool,
18) -> anyhow::Result<()> {
19 let config_file_display = config_file.display();
20 if config_file.exists() && !override_file {
21 bail!(
22 "Config file at {config_file_display} already exists, delete it before generating a new one or use -f to override"
23 );
24 }
25 if let Some(parent) = config_file.parent() {
26 create_dir_all(parent)
27 .with_context(|| format!("Failed to create config dir at ({})", parent.display()))?;
28 }
29 let str = match config_file.extension().and_then(OsStr::to_str) {
30 None | Some("ron") => Options::default()
31 .with_default_extension(Extensions::IMPLICIT_SOME)
32 .with_default_extension(Extensions::UNWRAP_NEWTYPES)
33 .with_default_extension(Extensions::UNWRAP_VARIANT_NEWTYPES)
34 .to_string_pretty(&config, PrettyConfig::default())
35 .with_context(|| format!("Failed to write RON config to ({config_file_display})")),
36 Some("json5" | "json") => {
37 serde_json::to_string_pretty(&config).context("Failed to generate JSON config")
38 }
39 Some("toml") => toml::to_string_pretty(&config).context("Failed to generate TOML config"),
40 Some(ext) => bail!(
41 "Invalid config file extension: {ext} (run with -vv and check `FEATURES: ` debug log to see enabled extensions)"
42 ),
43 }?;
44 #[allow(clippy::match_same_arms)]
45 let header = match config_file.extension().and_then(OsStr::to_str) {
46 None | Some("ron") => format!("// {CONFIG_EXPLANATION}"),
47 Some("json5") => format!("// {CONFIG_EXPLANATION}"),
48 Some("toml") => format!("# {CONFIG_EXPLANATION}"),
49 _ => String::new(),
50 };
51 let content = format!("{header}\n{str}");
52 let mut file = File::create(config_file)
53 .with_context(|| format!("Failed to create config file at ({config_file_display})"))?;
54 file.write_all(content.as_bytes())
55 .with_context(|| format!("Failed to write to config file at ({config_file_display})"))
56 .inspect_err(|_| {
57 info!("New config contents: {config:?}");
58 })?;
59
60 debug!("Config file written successfully at {config_file_display}");
61 Ok(())
62}
63
64pub fn write_config(
65 config_file: &Path,
66 config: &crate::Config,
67 override_file: bool,
68) -> anyhow::Result<()> {
69 let config = crate::io::Config::from(config.clone());
70 write_io_config(config_file, &config, override_file)
71}