diff --git a/Sources/phbar/Models/Theme/Extensions/PHTheme+Loading.swift b/Sources/phbar/Models/Theme/Extensions/PHTheme+Loading.swift new file mode 100644 index 0000000..8e517e7 --- /dev/null +++ b/Sources/phbar/Models/Theme/Extensions/PHTheme+Loading.swift @@ -0,0 +1,59 @@ +import Foundation +import TOML + +extension PHTheme { + /// Load theme from the config directory (~/.config/phbar/themes/). + /// If the file doesn't exist or fails to parse, defaults are used (non-fatal). + static func load(_ theme: String?) throws -> PHTheme { + guard let theme else { return try loadFromBundle() } + + let url = FileManager.default.homeDirectoryForCurrentUser.appending( + path: ".config/phbar/themes/\(theme).toml" + ) + + if FileManager.default.fileExists(atPath: url.relativePath) { + return try load(from: url) + } else { + return try loadFromBundle() + } + } + + /// Load the bundled theme. + /// If the file doesn't exist or fails to parse, the execution is interrupted. + static private func loadFromBundle() throws -> PHTheme { + guard + let defaultConfig = Bundle.module.url( + forResource: "theme", + withExtension: "toml" + )?.resolvingSymlinksInPath() + else { + throw phbar.Error("Failed to load theme file") + } + return try load(from: defaultConfig) + } + + /// Load theme from a file at URL. + /// If the file doesn't exist or fails to parse, the execution is interrupted. + static private func load(from url: URL) throws -> PHTheme { + do { + let data = try Data(contentsOf: url) + guard let contents = String(data: data, encoding: .utf8), !contents.isEmpty else { + throw phbar.Error("the file is empty") + } + return try load(from: contents) + } catch { + throw phbar.Error("Failed to load theme file", underlyingError: error) + } + } + + /// Load theme from a TOML string. + /// If the content fails to parse, the execution is interrupted. + static private func load(from contents: String) throws -> PHTheme { + do { + let decoder = TOMLDecoder() + return try decoder.decode(PHTheme.self, from: contents) + } catch { + throw phbar.Error("Failed to parse theme file", underlyingError: error) + } + } +} diff --git a/Sources/phbar/Models/Theme/PHTheme.swift b/Sources/phbar/Models/Theme/PHTheme.swift index dfba179..f7f939f 100644 --- a/Sources/phbar/Models/Theme/PHTheme.swift +++ b/Sources/phbar/Models/Theme/PHTheme.swift @@ -1,71 +1,9 @@ -import Foundation -import TOML - struct PHTheme: Decodable { let window: PHThemeWindow let styles: [PHThemeStyle] - enum CodingKeys: String, CodingKey { + private enum CodingKeys: String, CodingKey { case window case styles = "style" } } - -// Loading - -extension PHTheme { - /// Load theme from the config directory (~/.config/phbar/themes/). - /// If the file doesn't exist or fails to parse, defaults are used (non-fatal). - static func load(_ theme: String?) throws -> PHTheme { - guard let theme else { return try loadFromBundle() } - - let url = FileManager.default.homeDirectoryForCurrentUser.appending( - path: ".config/phbar/themes/\(theme).toml" - ) - - if FileManager.default.fileExists(atPath: url.relativePath) { - return try load(from: url) - } else { - return try loadFromBundle() - } - } - - /// Load the bundled theme. - /// If the file doesn't exist or fails to parse, the execution is interrupted. - static private func loadFromBundle() throws -> PHTheme { - guard - let defaultConfig = Bundle.module.url( - forResource: "theme", - withExtension: "toml" - )?.resolvingSymlinksInPath() - else { - throw phbar.Error("Failed to load theme file") - } - return try load(from: defaultConfig) - } - - /// Load theme from a file at URL. - /// If the file doesn't exist or fails to parse, the execution is interrupted. - static private func load(from url: URL) throws -> PHTheme { - do { - let data = try Data(contentsOf: url) - guard let contents = String(data: data, encoding: .utf8), !contents.isEmpty else { - throw phbar.Error("the file is empty") - } - return try load(from: contents) - } catch { - throw phbar.Error("Failed to load theme file", underlyingError: error) - } - } - - /// Load theme from a TOML string. - /// If the content fails to parse, the execution is interrupted. - static private func load(from contents: String) throws -> PHTheme { - do { - let decoder = TOMLDecoder() - return try decoder.decode(PHTheme.self, from: contents) - } catch { - throw phbar.Error("Failed to parse theme file", underlyingError: error) - } - } -} diff --git a/Sources/phbar/Models/Theme/PHThemeWindow.swift b/Sources/phbar/Models/Theme/PHThemeWindow.swift index 83080f9..80362e5 100644 --- a/Sources/phbar/Models/Theme/PHThemeWindow.swift +++ b/Sources/phbar/Models/Theme/PHThemeWindow.swift @@ -2,7 +2,7 @@ struct PHThemeWindow: Decodable { let hasShadow: Bool let blur: Double - enum CodingKeys: String, CodingKey { + private enum CodingKeys: String, CodingKey { case hasShadow = "shadow" case blur }