Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions App/Sources/App/ContentViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ final class ContentViewModel {
let jsonVariable = ConfigVariable(
key: "complexConfig",
defaultValue: ComplexConfiguration(field1: "a", field2: 1),
content: .json(representation: .data)
content: .json(representation: .string())
).metadata(\.displayName, "Complex Config")

let intBackedVariable = ConfigVariable(key: "favoriteCardSuit", defaultValue: CardSuit.spades, isSecret: true)
Expand Down Expand Up @@ -96,15 +96,15 @@ struct ComplexConfiguration: Codable, Hashable, Sendable {
}


enum Beatle: String, Codable, Hashable, Sendable {
enum Beatle: String, CaseIterable, Codable, Hashable, Sendable {
case john = "John"
case paul = "Paul"
case george = "George"
case ringo = "Ringo"
}


enum CardSuit: Int, Codable, Hashable, Sendable {
enum CardSuit: Int, CaseIterable, Codable, Hashable, Sendable {
case spades
case hearts
case clubs
Expand Down
34 changes: 34 additions & 0 deletions Sources/DevConfiguration/Core/CodableValueRepresentation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,16 @@ public struct CodableValueRepresentation: Sendable {
CodableValueRepresentation(kind: .data)
}

/// Whether this representation supports text-based editing in the editor UI.
///
/// String-backed representations can be edited as text, while data-backed representations cannot.
var supportsTextEditing: Bool {
switch kind {
case .string: true
case .data: false
}
}


/// Reads raw data synchronously from the reader based on this representation.
///
Expand Down Expand Up @@ -129,6 +139,30 @@ public struct CodableValueRepresentation: Sendable {
}


/// Extracts raw `Data` from a ``ConfigContent`` based on this representation.
///
/// This is the reverse of ``encodeToContent(_:)``. For string-backed representations, this extracts the string and
/// converts it to `Data` using the representation's encoding. For data-backed representations, this extracts the
/// byte array and wraps it in `Data`.
///
/// - Parameter content: The content to extract data from.
/// - Returns: The raw data, or `nil` if the content doesn't match this representation's expected case.
func data(from content: ConfigContent) -> Data? {
switch kind {
case .string(let encoding):
guard case .string(let string) = content else {
return nil
}
return string.data(using: encoding)
case .data:
guard case .bytes(let bytes) = content else {
return nil
}
return Data(bytes)
}
}


/// Watches for raw data changes from the reader based on this representation.
///
/// Each time the underlying configuration value changes, `onUpdate` is called with the new raw data (or `nil` if the
Expand Down
39 changes: 39 additions & 0 deletions Sources/DevConfiguration/Core/ConfigVariable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,28 @@ extension ConfigVariable {
}


extension ConfigVariable {
/// Creates a `RawRepresentable<String> & CaseIterable` configuration variable.
///
/// Content is set to ``ConfigVariableContent/rawRepresentableCaseIterableString()`` automatically. Uses a picker
/// control populated with all cases instead of a free-text field.
///
/// - Parameters:
/// - key: The configuration key.
/// - defaultValue: The default value to use when variable resolution fails.
/// - isSecret: Whether this variable's value should be treated as secret. Defaults to `false`.
public init(key: ConfigKey, defaultValue: Value, isSecret: Bool = false)
where Value: RawRepresentable & CaseIterable & Sendable, Value.RawValue == String {
self.init(
key: key,
defaultValue: defaultValue,
content: .rawRepresentableCaseIterableString(),
isSecret: isSecret
)
}
}


extension ConfigVariable {
/// Creates a `[RawRepresentable<String>]` configuration variable.
///
Expand Down Expand Up @@ -348,6 +370,23 @@ extension ConfigVariable {
}


extension ConfigVariable {
/// Creates a `RawRepresentable<Int> & CaseIterable` configuration variable.
///
/// Content is set to ``ConfigVariableContent/rawRepresentableCaseIterableInt()`` automatically. Uses a picker
/// control populated with all cases instead of a free-text number field.
///
/// - Parameters:
/// - key: The configuration key.
/// - defaultValue: The default value to use when variable resolution fails.
/// - isSecret: Whether this variable's value should be treated as secret. Defaults to `false`.
public init(key: ConfigKey, defaultValue: Value, isSecret: Bool = false)
where Value: RawRepresentable & CaseIterable & Sendable, Value.RawValue == Int {
self.init(key: key, defaultValue: defaultValue, content: .rawRepresentableCaseIterableInt(), isSecret: isSecret)
}
}


extension ConfigVariable {
/// Creates a `[RawRepresentable<Int>]` configuration variable.
///
Expand Down
Loading
Loading