diff --git a/Sources/DotEnvy/Documentation.docc/Documentation.md b/Sources/DotEnvy/Documentation.docc/Documentation.md index fc04839..e70278d 100644 --- a/Sources/DotEnvy/Documentation.docc/Documentation.md +++ b/Sources/DotEnvy/Documentation.docc/Documentation.md @@ -80,6 +80,17 @@ func env() -> [String: String] { } ``` +If you want to override the process environment, you can use ``DotEnvironment/export(overwrite:)``: + +```swift +import DotEnvy + +func overrideEnv() throws { + let environment = try DotEnvironment.make() + environment.export() +} +``` + ## Topics ### Group diff --git a/Sources/DotEnvy/Load.swift b/Sources/DotEnvy/Load.swift index 9915843..e73df33 100644 --- a/Sources/DotEnvy/Load.swift +++ b/Sources/DotEnvy/Load.swift @@ -46,6 +46,16 @@ extension DotEnvironment.Override { } extension DotEnvironment { + /// Export the `DotEnvironment` values into the process environment. + /// + /// - Parameter overwrite: Indicates if the calculated value should overwrite any existing value + /// in the process environment. + public func export(overwrite: Bool = true) { + for value in self.merge() { + setenv(value.key, value.value, overwrite ? 1 : 0) + } + } + /// Load the contents of a dotenv file into a dictionary. /// /// Defaults to loading `.env` from the current working directory. If the file does not exist, an @@ -77,6 +87,21 @@ extension DotEnvironment { } } + /// Create a `DotEnvironment` from `source` and `overrides`. + /// + /// - Parameter source: A string in dotenv format. + public static func make( + source: String, + overrides: DotEnvironment.Override = .process + ) throws -> DotEnvironment { + do { + let values = try self.parse(string: source) + return DotEnvironment(environment: values, overrides: overrides) + } catch let error as ParseErrorWithLocation { + throw LoadError.parseError(error, source) + } + } + /// Create a `DotEnvironment` from `url` and `overrides`. /// /// Defaults to loading `.env` from the current working directory and using the process environment diff --git a/Tests/DotEnvyTests/ExportTests.swift b/Tests/DotEnvyTests/ExportTests.swift new file mode 100644 index 0000000..3d1d126 --- /dev/null +++ b/Tests/DotEnvyTests/ExportTests.swift @@ -0,0 +1,32 @@ +@testable import DotEnvy +import XCTest + +final class ExportTests: XCTestCase { + func testOverwrite() throws { + setenv("KEY1", "ENVVALUE1", 1) + let dotenv = try DotEnvironment.make( + source: """ + KEY1=DOTENVVALUE1 + KEY2=DOTENVVALUE2 + """, + overrides: .none + ) + dotenv.export(overwrite: true) + XCTAssertEqual(ProcessInfo.processInfo.environment["KEY1"], "DOTENVVALUE1") + XCTAssertEqual(ProcessInfo.processInfo.environment["KEY2"], "DOTENVVALUE2") + } + + func testNoOverwrite() throws { + setenv("KEY1", "ENVVALUE1", 1) + let dotenv = try DotEnvironment.make( + source: """ + KEY1=DOTENVVALUE1 + KEY2=DOTENVVALUE2 + """, + overrides: .none + ) + dotenv.export(overwrite: false) + XCTAssertEqual(ProcessInfo.processInfo.environment["KEY1"], "ENVVALUE1") + XCTAssertEqual(ProcessInfo.processInfo.environment["KEY2"], "DOTENVVALUE2") + } +} diff --git a/Tests/DotEnvyTests/LoadTests.swift b/Tests/DotEnvyTests/LoadTests.swift index cab060e..4eb0b84 100644 --- a/Tests/DotEnvyTests/LoadTests.swift +++ b/Tests/DotEnvyTests/LoadTests.swift @@ -59,6 +59,25 @@ final class LoadTests: XCTestCase { } } + func testLoadErrorFromMakeSource() throws { + let source = """ + KEY1=VALUE1 + KEY2-VALUE2 + KEY3=VALUE3 + """ + XCTAssertThrowsError(try DotEnvironment.make(source: source)) { error in + guard let error = error as? LoadError else { + XCTFail("Unexpected error: \(error)") + return + } + guard case let .parseError(p, _) = error else { + XCTFail("Unexpected LoadError: \(error)") + return + } + XCTAssertEqual(p.error, .missingEquals) + } + } + func testMakeDotEnvironmentWithDefaultFile() throws { try inTemporaryDirectory { tempDir in try Data("""