1use crate::preset::{AccentsColor, ApplyBuilder, Mode, Preset};
2use crate::utils::{get_gnome_shell_version, run_command, ShellVersion};
3use grass::from_path;
4use walkdir::WalkDir;
5
6pub struct Shell {
7 pub version: ShellVersion,
8 pub source_dir: String,
9 pub preset: Preset,
10}
11
12pub enum ThemeName {
13 Default,
14 Custom { name: String },
15}
16
17impl Shell {
18 pub fn new(source_dir: String, preset: Preset) -> Shell {
19 let version = get_gnome_shell_version();
20 Shell {
21 version,
22 source_dir,
23 preset,
24 }
25 }
26
27 fn apply_gtk(&self, mode: Mode, accent: AccentsColor, theme_dir: String) {
28 if !std::path::Path::new(&format!("{}/gtk-4.0", theme_dir)).exists() {
29 std::fs::create_dir_all(&format!("{}/gtk-4.0", theme_dir)).unwrap();
30 }
31 if !std::path::Path::new(&format!("{}/gtk-3.0", theme_dir)).exists() {
32 std::fs::create_dir_all(&format!("{}/gtk-3.0", theme_dir)).unwrap();
33 }
34
35 ApplyBuilder::new(self.preset.clone())
36 .accent(accent)
37 .mode(mode)
38 .gtk3_path(format!("{}/gtk-3.0/gtk.css", theme_dir).as_str())
39 .gtk4_path(format!("{}/gtk-4.0/gtk.css", theme_dir).as_str())
40 .apply();
41 }
42
43 pub fn apply(
44 &self,
45 target_dir: String,
46 themes_dir: String,
47 mode: Mode,
48 accent: AccentsColor,
49 theme_name: ThemeName,
50 ) -> Result<(), std::io::Error> {
51 let version = match self.version {
52 ShellVersion::G46 => "46",
53 ShellVersion::Unsupported => {
54 return Err(std::io::Error::new(
55 std::io::ErrorKind::Other,
56 "Unsupported shell version",
57 ))
58 }
59 };
60
61 let source_path = format!("{}/{}", self.source_dir, version);
62 let target_path = match theme_name {
63 ThemeName::Default => format!(
64 "{}/{}-{:?}-{:?}",
65 target_dir, self.preset.name, mode, accent
66 ),
67 ThemeName::Custom { ref name } => name.to_string(),
68 };
69 let theme_dir = match theme_name {
70 ThemeName::Default => format!(
71 "{}/{}-{:?}-{:?}",
72 themes_dir, self.preset.name, mode, accent
73 ),
74 ThemeName::Custom { ref name } => name.to_string(),
75 };
76
77 if !std::path::Path::new(&target_path).exists() {
78 std::fs::create_dir_all(&target_path).unwrap();
79 }
80
81 if !std::path::Path::new(&theme_dir).exists() {
82 std::fs::create_dir_all(&theme_dir).unwrap();
83 }
84
85 std::fs::create_dir_all(&format!("{}/gnome-shell", theme_dir)).unwrap();
86
87 let output = run_command(&format!("cp -r {}/* {}", source_path, target_path));
88
89 if !output.status.success() {
90 return Err(std::io::Error::new(
91 std::io::ErrorKind::Other,
92 "Failed to copy files",
93 ));
94 }
95
96 for entry in WalkDir::new(&target_path) {
97 let entry = entry?;
98 if entry.path().extension().unwrap_or_default() == "template" {
99 let template = std::fs::read_to_string(entry.path())?;
100 let rendered = self.preset.render_template(template, mode, accent);
101 std::fs::write(entry.path().with_extension("scss"), rendered)?;
102 std::fs::remove_file(entry.path().with_extension("template"))?;
103 }
104 }
105
106 let css = from_path(
107 &format!("{}/gnome-shell.scss", target_path),
108 &grass::Options::default(),
109 )
110 .unwrap();
111 std::fs::write(format!("{}/gnome-shell/gnome-shell.css", theme_dir), css)?;
112 self.apply_gtk(mode, accent, theme_dir);
113
114 return Ok(());
115 }
116}