bob/
init.rs

1/*
2 * Copyright (c) 2025 Jonathan Perkin <jonathan@perkin.org.uk>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17use anyhow::bail;
18use rust_embed::RustEmbed;
19use std::env;
20use std::fs;
21#[cfg(unix)]
22use std::os::unix::fs::PermissionsExt;
23use std::path::PathBuf;
24
25#[derive(Debug, RustEmbed)]
26#[folder = "scripts/"]
27struct Scripts;
28
29pub struct Init {}
30
31impl Init {
32    pub fn create(dir: &PathBuf) -> anyhow::Result<()> {
33        let initdir = if dir.is_absolute() {
34            dir.to_path_buf()
35        } else {
36            env::current_dir()?.join(dir)
37        };
38        if initdir.exists() {
39            bail!("{} already exists", initdir.display());
40        }
41
42        let Some(initdir_str) = initdir.to_str() else {
43            bail!("Sorry, configuration directory must be valid UTF-8");
44        };
45
46        println!("Initialising new configuration directory {}:", initdir_str);
47
48        let confstr = match env::consts::OS {
49            "illumos" => include_str!("../config/illumos.lua"),
50            "linux" => include_str!("../config/linux.lua"),
51            "macos" => include_str!("../config/macos.lua"),
52            "netbsd" => include_str!("../config/netbsd.lua"),
53            "solaris" => include_str!("../config/illumos.lua"),
54            os => {
55                eprintln!(
56                    "WARNING: OS '{}' not explicitly supported, using generic config",
57                    os
58                );
59                include_str!("../config/generic.lua")
60            }
61        };
62
63        let confstr = confstr.replace("@INITDIR@", initdir_str);
64        let conffile = initdir.join("config.lua");
65        fs::create_dir_all(conffile.parent().unwrap())?;
66        fs::write(&conffile, confstr)?;
67        println!("\t{}", conffile.display());
68
69        for script in Scripts::iter() {
70            if let Some(content) = Scripts::get(&script) {
71                let fp = initdir.join("scripts").join(&*script);
72                fs::create_dir_all(fp.parent().unwrap())?;
73                fs::write(&fp, content.data)?;
74                #[cfg(unix)]
75                {
76                    let mut perms = fs::metadata(&fp)?.permissions();
77                    perms.set_mode(0o755);
78                    fs::set_permissions(&fp, perms)?;
79                }
80                println!("\t{}", fp.display());
81            }
82        }
83
84        Ok(())
85    }
86}