Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature] - Adding and Removing Values #10

Merged
merged 4 commits into from
Feb 6, 2022
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
4 changes: 2 additions & 2 deletions Package.resolved
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
"repositoryURL": "https://github.com/apple/swift-collections.git",
"state": {
"branch": null,
"revision": "2d33a0ea89c961dcb2b3da2157963d9c0370347e",
"version": "1.0.1"
"revision": "48254824bb4248676bf7ce56014ff57b142b77eb",
"version": "1.0.2"
}
}
]
Expand Down
1 change: 1 addition & 0 deletions Sources/Dotenv.swift
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ public struct Dotenv {
}

/// Save an environment object to a file.
/// - Important: This method does not serialize process values to disk.
/// - Parameters:
/// - environment: Environment to serialize.
/// - path: Path to save the environment to.
Expand Down
64 changes: 59 additions & 5 deletions Sources/Environment.swift
Original file line number Diff line number Diff line change
Expand Up @@ -121,10 +121,10 @@ public struct Environment {
/// Environment fallback strategy.
public static var fallbackStrategy: FallbackStrategy = .init(query: .configuration, fallback: .process)

// MARK: - Private
// MARK: - Storage

/// Backing environment values.
private let values: OrderedDictionary<String, Value>
public private(set) var values: OrderedDictionary<String, Value>

/// Process info instance.
private let processInfo: ProcessInfo
Expand Down Expand Up @@ -163,6 +163,12 @@ public struct Environment {
}
self.processInfo = processInfo
}

/// Create an empty environment.
/// - Parameter processInfo: Process info to seed the environment with.
public init(processInfo: ProcessInfo = ProcessInfo.processInfo) throws {
try self.init(values: [:] as OrderedDictionary<String, Value>, processInfo: processInfo)
}

/// Create an environment from the contents of a file.
/// - Parameter contents: File contents.
Expand All @@ -188,10 +194,46 @@ public struct Environment {
self.values = values
self.processInfo = processInfo
}

// MARK: - Modifying the Environment

/// Set a new value in the environment for the given key, optionally specifiying if the value should overwrite an existing value, if any exists.
/// - Parameters:
/// - value: Value to set in the environment.
/// - key: Key to set value for.
/// - force: Flag that indicates if the value should be forced if a value with the same key exists, defaults to `false`.
/// - Important: If the environment is set to read from the process, this method is a no-op as the process' environment is read-only.
public mutating func setValue(_ value: Value?, forKey key: String, force: Bool = false) {
guard values[key] == nil || force else {
return
}
values[key] = value
}

/// Set a new value in the environment for the given key, optionally specifiying if the value should overwrite an existing value, if any exists.
/// - Parameters:
/// - value: Value to set in the environment.
/// - key: Key to set value for.
/// - force: Flag that indicates if the value should be forced if a value with the same key exists, defaults to `false`.
/// - Important: If the environment is set to read from the process, this method is a no-op as the process' environment is read-only.
public mutating func setValue(_ value: String, forKey key: String, force: Bool = false) {
setValue(Value(value), forKey: key, force: force)
}

/// Remove a value for the given key, returning the old value, if any exsts.
/// - Parameter key: Key to remove.
/// - Returns: Old value that was removed, if any.
@discardableResult
public mutating func removeValue(forKey key: String) -> Value? {
let oldValue = queryValue(forKey: key)
setValue(nil, forKey: key, force: true)
return oldValue
}

// MARK: - Serialization

/// Transform the environment into a string representation that can be written to disk.
/// - Important: If the environment is backed by the current process, this method will **not** serialize those values to disk, only those manually set via `setValue(_:forKey:force:)`.
/// - Returns: File contents.
func serialize() throws -> String {
values.enumerated().reduce(into: "") { accumulated, current in
Expand All @@ -202,17 +244,29 @@ public struct Environment {
// MARK: - Subscript

public subscript(key: String) -> Value? {
queryValue(forKey: key)
get {
queryValue(forKey: key)
} set {
setValue(newValue, forKey: key)
}
}

public subscript(key: String, default defaultValue: @autoclosure () -> Value) -> Value {
queryValue(forKey: key) ?? defaultValue()
get {
queryValue(forKey: key) ?? defaultValue()
} set {
setValue(newValue, forKey: key)
}
}

// MARK: Dynamic Member Lookup

public subscript(dynamicMember member: String) -> Value? {
queryValue(forKey: member)
get {
queryValue(forKey: member)
} set {
setValue(newValue, forKey: member)
}
}

// MARK: - Helpers
Expand Down
60 changes: 60 additions & 0 deletions Tests/EnvironmentTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,64 @@ final class EnvironmentTests: XCTestCase {
XCTAssertEqual(env.networkRetries, .integer(3))
XCTAssertEqual(env.networkTimeout, .double(10.5))
}

// MARK: - Adding and Removing Values

func testAddingValueForNonexistantKey() throws {
var environment = try Environment()

XCTAssertNil(environment.key)

environment.setValue(.integer(1), forKey: "key")

XCTAssertEqual(environment.key, .integer(1))
}

func testAddingValueForExistingKeyWithoutForcing() throws {
var environment = try Environment(values: [
"key": .integer(1)
])

XCTAssertEqual(environment.key, .integer(1))

environment.setValue(.integer(2), forKey: "key")

XCTAssertEqual(environment.key, .integer(1))
}

func testAddingValueForExistingValueWithForcing() throws {
var environment = try Environment(values: [
"key": .integer(1)
])

XCTAssertEqual(environment.key, .integer(1))

environment.setValue(.integer(2), forKey: "key", force: true)

XCTAssertEqual(environment.key, .integer(2))
}

func testRemovingNonexistantValue() throws {
var environment = try Environment()

XCTAssertNil(environment.key)

let oldValue = environment.removeValue(forKey: "key")

XCTAssertNil(oldValue)
}


func testRemovingValue() throws {
var environment = try Environment(values: [
"key": .integer(1)
])

XCTAssertNotNil(environment.key)

let oldValue = environment.removeValue(forKey: "key")

XCTAssertEqual(oldValue, .integer(1))
XCTAssertNil(environment.key)
}
}