gradience-lib 0.1.0

Change the look of GNOME, with ease
Documentation
use crate::preset::{AccentsColor, ApplyBuilder, Mode, Preset};
use crate::utils::{get_gnome_shell_version, run_command, ShellVersion};
use grass::from_path;
use walkdir::WalkDir;

pub struct Shell {
    pub version: ShellVersion,
    pub source_dir: String,
    pub preset: Preset,
}

pub enum ThemeName {
    Default,
    Custom { name: String },
}

impl Shell {
    pub fn new(source_dir: String, preset: Preset) -> Shell {
        let version = get_gnome_shell_version();
        Shell {
            version,
            source_dir,
            preset,
        }
    }

    fn apply_gtk(&self, mode: Mode, accent: AccentsColor, theme_dir: String) {
        if !std::path::Path::new(&format!("{}/gtk-4.0", theme_dir)).exists() {
            std::fs::create_dir_all(&format!("{}/gtk-4.0", theme_dir)).unwrap();
        }
        if !std::path::Path::new(&format!("{}/gtk-3.0", theme_dir)).exists() {
            std::fs::create_dir_all(&format!("{}/gtk-3.0", theme_dir)).unwrap();
        }

        ApplyBuilder::new(self.preset.clone())
            .accent(accent)
            .mode(mode)
            .gtk3_path(format!("{}/gtk-3.0/gtk.css", theme_dir).as_str())
            .gtk4_path(format!("{}/gtk-4.0/gtk.css", theme_dir).as_str())
            .apply();
    }

    pub fn apply(
        &self,
        target_dir: String,
        themes_dir: String,
        mode: Mode,
        accent: AccentsColor,
        theme_name: ThemeName,
    ) -> Result<(), std::io::Error> {
        let version = match self.version {
            ShellVersion::G46 => "46",
            ShellVersion::Unsupported => {
                return Err(std::io::Error::new(
                    std::io::ErrorKind::Other,
                    "Unsupported shell version",
                ))
            }
        };

        let source_path = format!("{}/{}", self.source_dir, version);
        let target_path = match theme_name {
            ThemeName::Default => format!(
                "{}/{}-{:?}-{:?}",
                target_dir, self.preset.name, mode, accent
            ),
            ThemeName::Custom { ref name } => name.to_string(),
        };
        let theme_dir = match theme_name {
            ThemeName::Default => format!(
                "{}/{}-{:?}-{:?}",
                themes_dir, self.preset.name, mode, accent
            ),
            ThemeName::Custom { ref name } => name.to_string(),
        };

        if !std::path::Path::new(&target_path).exists() {
            std::fs::create_dir_all(&target_path).unwrap();
        }

        if !std::path::Path::new(&theme_dir).exists() {
            std::fs::create_dir_all(&theme_dir).unwrap();
        }

        std::fs::create_dir_all(&format!("{}/gnome-shell", theme_dir)).unwrap();

        let output = run_command(&format!("cp -r {}/* {}", source_path, target_path));

        if !output.status.success() {
            return Err(std::io::Error::new(
                std::io::ErrorKind::Other,
                "Failed to copy files",
            ));
        }

        for entry in WalkDir::new(&target_path) {
            let entry = entry?;
            if entry.path().extension().unwrap_or_default() == "template" {
                let template = std::fs::read_to_string(entry.path())?;
                let rendered = self.preset.render_template(template, mode, accent);
                std::fs::write(entry.path().with_extension("scss"), rendered)?;
                std::fs::remove_file(entry.path().with_extension("template"))?;
            }
        }

        let css = from_path(
            &format!("{}/gnome-shell.scss", target_path),
            &grass::Options::default(),
        )
        .unwrap();
        std::fs::write(format!("{}/gnome-shell/gnome-shell.css", theme_dir), css)?;
        self.apply_gtk(mode, accent, theme_dir);

        return Ok(());
    }
}