cleanup Block
This commit is contained in:
@@ -0,0 +1,52 @@
|
||||
import Foundation
|
||||
import TOML
|
||||
|
||||
extension PHBlock {
|
||||
private struct Wrapper: Decodable {
|
||||
let blocks: [PHBlock]
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case blocks = "block"
|
||||
}
|
||||
}
|
||||
|
||||
nonisolated static var configFile: URL {
|
||||
FileManager.default.homeDirectoryForCurrentUser.appending(path: ".config/phbar/blocks.toml")
|
||||
}
|
||||
|
||||
/// Load blocks from the default path (~/.config/phbar/blocks.toml).
|
||||
/// If the file doesn't exist or fails to parse, defaults are used (non-fatal).
|
||||
static func load() throws -> [PHBlock] {
|
||||
if FileManager.default.fileExists(atPath: Self.configFile.relativePath) {
|
||||
return try load(from: Self.configFile)
|
||||
} else {
|
||||
throw phbar.Error("Failed to load blocks file")
|
||||
}
|
||||
}
|
||||
|
||||
/// Load theme from a file at URL.
|
||||
/// If the file doesn't exist or fails to parse, the execution is interrupted.
|
||||
static func load(from url: URL) throws -> [PHBlock] {
|
||||
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 blocks file", underlyingError: error)
|
||||
}
|
||||
}
|
||||
|
||||
/// Load theme from a TOML string.
|
||||
/// If the content fails to parse, the execution is interrupted.
|
||||
static func load(from contents: String) throws -> [PHBlock] {
|
||||
do {
|
||||
let decoder = TOMLDecoder()
|
||||
let wrapper = try decoder.decode(PHBlock.Wrapper.self, from: contents)
|
||||
return wrapper.blocks
|
||||
} catch {
|
||||
throw phbar.Error("Failed to parse blocks file", underlyingError: error)
|
||||
}
|
||||
}
|
||||
}
|
||||
-117
@@ -1,69 +1,4 @@
|
||||
import ArgumentParser
|
||||
import Foundation
|
||||
import SwiftUI
|
||||
import TOML
|
||||
|
||||
@MainActor
|
||||
final class PHBlock: ObservableObject, Decodable, Identifiable {
|
||||
let id = UUID()
|
||||
let command: String
|
||||
let name: String?
|
||||
let styleName: String?
|
||||
@Published var style: PHThemeStyle = .default
|
||||
let refresh: Double?
|
||||
let centered: Bool?
|
||||
var debug: Bool = false
|
||||
|
||||
var visible: Bool {
|
||||
label != nil && !label!.isEmpty
|
||||
}
|
||||
|
||||
/// System events that should trigger a refresh (e.g. `["volume", "network"]`).
|
||||
/// `nil` when omitted from config.
|
||||
let events: [PHEvent]?
|
||||
|
||||
/// Event source registry used to subscribe to system events. Defaults to the
|
||||
/// shared instance; inject a custom one for testing.
|
||||
var registry: PHEventRegistry = .shared
|
||||
|
||||
@Published private(set) var label: String?
|
||||
|
||||
/// The repeating interval task, if any.
|
||||
private var intervalTask: Task<Void, Never>?
|
||||
|
||||
/// Active event subscriptions, torn down in `stopAutoRefresh`.
|
||||
private var subscriptions: [PHEventSubscription] = []
|
||||
|
||||
/// Guards against stacking concurrent updates during rapid event bursts
|
||||
/// (e.g. dragging the volume slider fires many events in quick succession).
|
||||
private var updateScheduled = false
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case command, name
|
||||
case styleName = "style"
|
||||
case refresh, events, centered
|
||||
}
|
||||
|
||||
deinit {
|
||||
intervalTask?.cancel()
|
||||
subscriptions.forEach { $0.cancel() }
|
||||
}
|
||||
}
|
||||
|
||||
// Kind
|
||||
|
||||
extension PHBlock {
|
||||
enum Kind: String {
|
||||
case text, space
|
||||
}
|
||||
|
||||
var kind: Kind {
|
||||
if command.starts(with: "_space") { return .space }
|
||||
return .text
|
||||
}
|
||||
}
|
||||
|
||||
// Refresh
|
||||
|
||||
extension PHBlock {
|
||||
/// Start keeping the label up to date.
|
||||
@@ -187,55 +122,3 @@ extension PHBlock {
|
||||
return lines.last?.description
|
||||
}
|
||||
}
|
||||
|
||||
// Loading
|
||||
|
||||
extension PHBlock {
|
||||
private struct Wrapper: Decodable {
|
||||
let blocks: [PHBlock]
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case blocks = "block"
|
||||
}
|
||||
}
|
||||
|
||||
private nonisolated static var configFile: URL {
|
||||
FileManager.default.homeDirectoryForCurrentUser.appending(path: ".config/phbar/blocks.toml")
|
||||
}
|
||||
|
||||
/// Load blocks from the default path (~/.config/phbar/blocks.toml).
|
||||
/// If the file doesn't exist or fails to parse, defaults are used (non-fatal).
|
||||
static func load() throws -> [PHBlock] {
|
||||
if FileManager.default.fileExists(atPath: Self.configFile.relativePath) {
|
||||
return try load(from: Self.configFile)
|
||||
} else {
|
||||
throw phbar.Error("Failed to load blocks file")
|
||||
}
|
||||
}
|
||||
|
||||
/// Load theme from a file at URL.
|
||||
/// If the file doesn't exist or fails to parse, the execution is interrupted.
|
||||
static func load(from url: URL) throws -> [PHBlock] {
|
||||
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 blocks file", underlyingError: error)
|
||||
}
|
||||
}
|
||||
|
||||
/// Load theme from a TOML string.
|
||||
/// If the content fails to parse, the execution is interrupted.
|
||||
static func load(from contents: String) throws -> [PHBlock] {
|
||||
do {
|
||||
let decoder = TOMLDecoder()
|
||||
let wrapper = try decoder.decode(PHBlock.Wrapper.self, from: contents)
|
||||
return wrapper.blocks
|
||||
} catch {
|
||||
throw phbar.Error("Failed to parse blocks file", underlyingError: error)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
import Foundation
|
||||
|
||||
@MainActor
|
||||
final class PHBlock: ObservableObject, Decodable, Identifiable {
|
||||
let id = UUID()
|
||||
|
||||
let command: String
|
||||
let name: String?
|
||||
let styleName: String?
|
||||
@Published var style: PHThemeStyle = .default
|
||||
let refresh: Double?
|
||||
let centered: Bool?
|
||||
var debug: Bool = false
|
||||
|
||||
var visible: Bool {
|
||||
label != nil && !label!.isEmpty
|
||||
}
|
||||
|
||||
/// System events that should trigger a refresh (e.g. `["volume", "network"]`).
|
||||
/// `nil` when omitted from config.
|
||||
let events: [PHEvent]?
|
||||
|
||||
/// Event source registry used to subscribe to system events. Defaults to the
|
||||
/// shared instance; inject a custom one for testing.
|
||||
var registry: PHEventRegistry = .shared
|
||||
|
||||
@Published var label: String?
|
||||
|
||||
/// The repeating interval task, if any.
|
||||
var intervalTask: Task<Void, Never>?
|
||||
|
||||
/// Active event subscriptions, torn down in `stopAutoRefresh`.
|
||||
var subscriptions: [PHEventSubscription] = []
|
||||
|
||||
/// Guards against stacking concurrent updates during rapid event bursts
|
||||
/// (e.g. dragging the volume slider fires many events in quick succession).
|
||||
var updateScheduled = false
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case command, name
|
||||
case styleName = "style"
|
||||
case refresh, events, centered
|
||||
}
|
||||
|
||||
deinit {
|
||||
intervalTask?.cancel()
|
||||
subscriptions.forEach { $0.cancel() }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
enum PHBlockKind: String {
|
||||
case text, space
|
||||
}
|
||||
|
||||
extension PHBlock {
|
||||
var kind: PHBlockKind {
|
||||
if command.starts(with: "_space") { return .space }
|
||||
return .text
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user