From 020a0892fb67d19884ab35a8afa878879d90c982 Mon Sep 17 00:00:00 2001 From: Ayodeji O <97124713+aosasona@users.noreply.github.com> Date: Thu, 18 Jul 2024 20:27:35 +0100 Subject: [PATCH 1/2] Added get_string and get_string_or variants --- src/dot_env/env.gleam | 28 ++++++++- test/dot_env_test.gleam | 122 ++++++++++++++++++++-------------------- 2 files changed, 87 insertions(+), 63 deletions(-) diff --git a/src/dot_env/env.gleam b/src/dot_env/env.gleam index 632f902..498b575 100644 --- a/src/dot_env/env.gleam +++ b/src/dot_env/env.gleam @@ -27,13 +27,37 @@ pub fn set(key: String, value: String) -> Result(Nil, String) /// |> result.unwrap("NOT SET") /// |> io.println /// ``` +@deprecated("Use `get_string` instead, this will be removed in the next release") @external(erlang, "dot_env_ffi", "get_env") @external(javascript, "../dot_env_ffi.mjs", "get_env") pub fn get(key: String) -> Result(String, String) +/// Get an environment variable (supports both Erlang and JavaScript targets) +/// +/// Example: +/// ```gleam +/// import dot_env/env +/// import gleam/io +/// import gleam/result +/// +/// env.get_string("FOO") +/// |> result.unwrap("NOT SET") +/// |> io.println +/// ``` +@external(erlang, "dot_env_ffi", "get_env") +@external(javascript, "../dot_env_ffi.mjs", "get_env") +pub fn get_string(key: String) -> Result(String, String) + /// Get an environment variable or return a default value if it is not set +@deprecated("Use `get_string_or` instead, this will be removed in the next release") pub fn get_or(key: String, default: String) -> String { - get(key) + get_string(key) + |> result.unwrap(default) +} + +/// Get an environment variable or return a default value if it is not set +pub fn get_string_or(key: String, default: String) -> String { + get_string(key) |> result.unwrap(default) } @@ -42,7 +66,7 @@ pub fn get_then( key: String, f: fn(String) -> Result(t, String), ) -> Result(t, String) { - case get(key) { + case get_string(key) { Ok(value) -> f(value) Error(err) -> Error(err) } diff --git a/test/dot_env_test.gleam b/test/dot_env_test.gleam index e70e6d7..9622c1f 100644 --- a/test/dot_env_test.gleam +++ b/test/dot_env_test.gleam @@ -10,13 +10,13 @@ pub fn main() { pub fn get_test() { dot_env.load_default() - env.get("DEFINITELY_NOT_A_REAL_KEY") + env.get_string("DEFINITELY_NOT_A_REAL_KEY") |> should.be_error - env.get("PORT") + env.get_string("PORT") |> should.equal(Ok("9000")) - env.get_or("UNDEFINED_KEY", "default") + env.get_string_or("UNDEFINED_KEY", "default") |> should.equal("default") env.get_int("PORT") @@ -49,26 +49,26 @@ pub fn load_missing_env_file_test() { ignore_missing_file: True, )) - env.get("PORT") + env.get_string("PORT") |> should.equal(Ok("9000")) } pub fn load_default_test() { dot_env.load_default() - env.get("PORT") + env.get_string("PORT") |> should.equal(Ok("9000")) - env.get("APP_NAME") + env.get_string("APP_NAME") |> should.equal(Ok("app")) - env.get("APP_ENV") + env.get_string("APP_ENV") |> should.equal(Ok("local")) - env.get("APP_KEY") + env.get_string("APP_KEY") |> should.equal(Ok("base-64:0")) - env.get("APP_DEBUG") + env.get_string("APP_DEBUG") |> should.equal(Ok("true")) } @@ -80,105 +80,105 @@ pub fn load_normal_test() { ignore_missing_file: False, )) - env.get("BASIC") + env.get_string("BASIC") |> should.equal(Ok("basic")) - env.get("AFTER_LINE") + env.get_string("AFTER_LINE") |> should.equal(Ok("after_line")) - env.get("EMPTY") + env.get_string("EMPTY") |> should.equal(Ok("")) - env.get("EMPTY_SINGLE_QUOTES") + env.get_string("EMPTY_SINGLE_QUOTES") |> should.equal(Ok("")) - env.get("EMPTY_DOUBLE_QUOTES") + env.get_string("EMPTY_DOUBLE_QUOTES") |> should.equal(Ok("")) - env.get("SINGLE_QUOTES") + env.get_string("SINGLE_QUOTES") |> should.equal(Ok("single_quotes")) - env.get("SINGLE_QUOTES_SPACED") + env.get_string("SINGLE_QUOTES_SPACED") |> should.equal(Ok(" single quotes ")) - env.get("DOUBLE_QUOTES_INSIDE_SINGLE") + env.get_string("DOUBLE_QUOTES_INSIDE_SINGLE") |> should.equal(Ok("double \"quotes\" work inside single quotes")) - env.get("DOUBLE_QUOTES_WITH_NO_SPACE_BRACKET") + env.get_string("DOUBLE_QUOTES_WITH_NO_SPACE_BRACKET") |> should.equal(Ok("{ port: $MONGOLAB_PORT}")) - env.get("SINGLE_QUOTES_INSIDE_DOUBLE") + env.get_string("SINGLE_QUOTES_INSIDE_DOUBLE") |> should.equal(Ok("single 'quotes' work inside double quotes")) - env.get("BACKTICKS_INSIDE_SINGLE") + env.get_string("BACKTICKS_INSIDE_SINGLE") |> should.equal(Ok("`backticks` work inside single quotes")) - env.get("BACKTICKS_INSIDE_DOUBLE") + env.get_string("BACKTICKS_INSIDE_DOUBLE") |> should.equal(Ok("`backticks` work inside double quotes")) - env.get("BACKTICKS") + env.get_string("BACKTICKS") |> should.equal(Ok("backticks")) - env.get("BACKTICKS_SPACED") + env.get_string("BACKTICKS_SPACED") |> should.equal(Ok(" backticks ")) - env.get("DOUBLE_QUOTES_INSIDE_BACKTICKS") + env.get_string("DOUBLE_QUOTES_INSIDE_BACKTICKS") |> should.equal(Ok("double \"quotes\" work inside backticks")) - env.get("SINGLE_QUOTES_INSIDE_BACKTICKS") + env.get_string("SINGLE_QUOTES_INSIDE_BACKTICKS") |> should.equal(Ok("single 'quotes' work inside backticks")) - env.get("DOUBLE_AND_SINGLE_QUOTES_INSIDE_BACKTICKS") + env.get_string("DOUBLE_AND_SINGLE_QUOTES_INSIDE_BACKTICKS") |> should.equal(Ok( "double \"quotes\" and single 'quotes' work inside backticks", )) - env.get("EXPAND_NEWLINES") + env.get_string("EXPAND_NEWLINES") |> should.equal(Ok("expand\nnew\nlines")) - env.get("DONT_EXPAND_UNQUOTED") + env.get_string("DONT_EXPAND_UNQUOTED") |> should.equal(Ok("dontexpand\\nnewlines")) - env.get("DONT_EXPAND_SQUOTED") + env.get_string("DONT_EXPAND_SQUOTED") |> should.equal(Ok("dontexpand\\nnewlines")) - env.get("INLINE_COMMENTS") + env.get_string("INLINE_COMMENTS") |> should.equal(Ok("inline comments")) - env.get("INLINE_COMMENTS_SINGLE_QUOTES") + env.get_string("INLINE_COMMENTS_SINGLE_QUOTES") |> should.equal(Ok("inline comments outside of #singlequotes")) - env.get("INLINE_COMMENTS_DOUBLE_QUOTES") + env.get_string("INLINE_COMMENTS_DOUBLE_QUOTES") |> should.equal(Ok("inline comments outside of #doublequotes")) - env.get("INLINE_COMMENTS_BACKTICKS") + env.get_string("INLINE_COMMENTS_BACKTICKS") |> should.equal(Ok("inline comments outside of #backticks")) - env.get("INLINE_COMMENTS_SPACE") + env.get_string("INLINE_COMMENTS_SPACE") |> should.equal(Ok("inline comments start with a")) - env.get("EQUAL_SIGNS") + env.get_string("EQUAL_SIGNS") |> should.equal(Ok("equals==")) - env.get("RETAIN_INNER_QUOTES") + env.get_string("RETAIN_INNER_QUOTES") |> should.equal(Ok("{\"foo\": \"bar\"}")) - env.get("RETAIN_INNER_QUOTES_AS_STRING") + env.get_string("RETAIN_INNER_QUOTES_AS_STRING") |> should.equal(Ok("{\"foo\": \"bar\"}")) - env.get("RETAIN_INNER_QUOTES_AS_BACKTICKS") + env.get_string("RETAIN_INNER_QUOTES_AS_BACKTICKS") |> should.equal(Ok("{\"foo\": \"bar's\"}")) - env.get("TRIM_SPACE_FROM_UNQUOTED") + env.get_string("TRIM_SPACE_FROM_UNQUOTED") |> should.equal(Ok("some spaced out string")) - env.get("USERNAME") + env.get_string("USERNAME") |> should.equal(Ok("therealnerdybeast@example.tld")) - env.get("SPACED_KEY") + env.get_string("SPACED_KEY") |> should.equal(Ok("parsed")) - env.get("DOESNT_EXIST") + env.get_string("DOESNT_EXIST") |> should.equal(Error("key DOESNT_EXIST is not set")) } @@ -190,60 +190,60 @@ pub fn load_multiline_test() { ignore_missing_file: False, )) - env.get("BASIC") + env.get_string("BASIC") |> should.equal(Ok("basic")) - env.get("AFTER_LINE") + env.get_string("AFTER_LINE") |> should.equal(Ok("after_line")) - env.get("EMPTY") + env.get_string("EMPTY") |> should.equal(Ok("")) - env.get("SINGLE_QUOTES") + env.get_string("SINGLE_QUOTES") |> should.equal(Ok("single_quotes")) - env.get("SINGLE_QUOTES_SPACED") + env.get_string("SINGLE_QUOTES_SPACED") |> should.equal(Ok(" single quotes ")) - env.get("DOUBLE_QUOTES") + env.get_string("DOUBLE_QUOTES") |> should.equal(Ok("double_quotes")) - env.get("DOUBLE_QUOTES_SPACED") + env.get_string("DOUBLE_QUOTES_SPACED") |> should.equal(Ok(" double quotes ")) - env.get("EXPAND_NEWLINES") + env.get_string("EXPAND_NEWLINES") |> should.equal(Ok("expand\nnew\nlines")) - env.get("DONT_EXPAND_UNQUOTED") + env.get_string("DONT_EXPAND_UNQUOTED") |> should.equal(Ok("dontexpand\\nnewlines")) - env.get("DONT_EXPAND_SQUOTED") + env.get_string("DONT_EXPAND_SQUOTED") |> should.equal(Ok("dontexpand\\nnewlines")) - env.get("EQUAL_SIGNS") + env.get_string("EQUAL_SIGNS") |> should.equal(Ok("equals==")) - env.get("RETAIN_INNER_QUOTES") + env.get_string("RETAIN_INNER_QUOTES") |> should.equal(Ok("{\"foo\": \"bar\"}")) - env.get("RETAIN_INNER_QUOTES_AS_STRING") + env.get_string("RETAIN_INNER_QUOTES_AS_STRING") |> should.equal(Ok("{\"foo\": \"bar\"}")) - env.get("TRIM_SPACE_FROM_UNQUOTED") + env.get_string("TRIM_SPACE_FROM_UNQUOTED") |> should.equal(Ok("some spaced out string")) - env.get("USERNAME") + env.get_string("USERNAME") |> should.equal(Ok("therealnerdybeast@example.tld")) - env.get("SPACED_KEY") + env.get_string("SPACED_KEY") |> should.equal(Ok("parsed")) - env.get("MULTI_DOUBLE_QUOTED") + env.get_string("MULTI_DOUBLE_QUOTED") |> should.equal(Ok("THIS\nIS\nA\nMULTILINE\nSTRING")) - env.get("MULTI_SINGLE_QUOTED") + env.get_string("MULTI_SINGLE_QUOTED") |> should.equal(Ok("THIS\nIS\nA\nMULTILINE\nSTRING")) - env.get("MULTI_BACKTICKED") + env.get_string("MULTI_BACKTICKED") |> should.equal(Ok("THIS\nIS\nA\n\"MULTILINE'S\"\nSTRING")) } From c95a1c51e7158c7f2370b05b1c43a84e7ddb81af Mon Sep 17 00:00:00 2001 From: Ayodeji O <97124713+aosasona@users.noreply.github.com> Date: Thu, 18 Jul 2024 20:53:10 +0100 Subject: [PATCH 2/2] More documentation for `env` functions --- src/dot_env/env.gleam | 111 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 101 insertions(+), 10 deletions(-) diff --git a/src/dot_env/env.gleam b/src/dot_env/env.gleam index 498b575..0f9e045 100644 --- a/src/dot_env/env.gleam +++ b/src/dot_env/env.gleam @@ -4,11 +4,16 @@ import gleam/string /// Set an environment variable (supports both Erlang and JavaScript targets) /// -/// Example: +/// ## Usage +/// /// ```gleam /// import dot_env/env /// -/// env.set("FOO", "my value") +/// fn main() { +/// env.set("APP_NAME", "app") +/// +/// Nil +/// } /// ``` /// @external(erlang, "dot_env_ffi", "set_env") @@ -34,16 +39,20 @@ pub fn get(key: String) -> Result(String, String) /// Get an environment variable (supports both Erlang and JavaScript targets) /// -/// Example: +/// ## Usage +/// /// ```gleam /// import dot_env/env /// import gleam/io /// import gleam/result /// -/// env.get_string("FOO") -/// |> result.unwrap("NOT SET") -/// |> io.println +/// fn main() { +/// env.get_string("APP_NAME") +/// |> result.unwrap("app") +/// |> io.println +/// } /// ``` +/// @external(erlang, "dot_env_ffi", "get_env") @external(javascript, "../dot_env_ffi.mjs", "get_env") pub fn get_string(key: String) -> Result(String, String) @@ -56,23 +65,64 @@ pub fn get_or(key: String, default: String) -> String { } /// Get an environment variable or return a default value if it is not set +/// +/// ## Usage +/// +/// ```gleam +/// import dot_env/env +/// import gleam/io +/// +/// fn main() { +/// let app_name = env.get_string_or("APP_NAME", "My App") +/// io.println(app_name) +/// } +/// ``` +/// pub fn get_string_or(key: String, default: String) -> String { get_string(key) |> result.unwrap(default) } -/// An alternative implementation of `get` that allows for chaining using `use` +/// An alternative implementation of `get` that allows for chaining using `use` statements and for early returns. +/// +/// ## Usage +/// +/// ```gleam +/// import dot_env/env +/// import gleam/io +/// +/// fn main() { +/// use app_name <- env.get_then("APP_NAME") +/// io.println(app_name) +/// } +/// ``` +/// pub fn get_then( key: String, - f: fn(String) -> Result(t, String), + next: fn(String) -> Result(t, String), ) -> Result(t, String) { case get_string(key) { - Ok(value) -> f(value) + Ok(value) -> next(value) Error(err) -> Error(err) } } -/// Get an environment variable as an integer +/// Get an environment variable as an integer (this is the same as calling `get_string` and then parsing the `Ok` value) +/// +/// ## Usage +/// +/// ```gleam +/// import dot_env/env +/// import gleam/io +/// import gleam/result +/// +/// fn main() { +/// env.get_int("PORT") +/// |> result.unwrap(9000) +/// |> io.println +/// } +/// ``` +/// pub fn get_int(key: String) -> Result(Int, String) { use raw_value <- get_then(key) @@ -83,12 +133,40 @@ pub fn get_int(key: String) -> Result(Int, String) { } /// Get an environment variable as an integer or return a default value if it is not set +/// +/// ## Usage +/// +/// ```gleam +/// import dot_env/env +/// import gleam/io +/// +/// fn main() { +/// let port = env.get_int_or("PORT", 9000) +/// io.debug(port) +/// } +/// ``` +/// pub fn get_int_or(key: String, default: Int) -> Int { get_int(key) |> result.unwrap(default) } /// Get an environment variable as a boolean +/// +/// ## Usage +/// +/// ```gleam +/// import dot_env/env +/// import gleam/io +/// import gleam/result +/// +/// fn main() { +/// env.get_bool("IS_DEBUG") +/// |> result.unwrap(False) +/// |> io.println +/// } +/// ``` +/// pub fn get_bool(key: String) -> Result(Bool, String) { use raw_value <- get_then(key) @@ -105,6 +183,19 @@ pub fn get_bool(key: String) -> Result(Bool, String) { } /// Get an environment variable as a boolean or return a default value if it is not set +/// +/// ## Usage +/// +/// ```gleam +/// import dot_env/env +/// import gleam/io +/// +/// fn main() { +/// let is_debug = env.get_bool_or("IS_DEBUG", True) +/// io.debug(is_debug) +/// } +/// ``` +/// pub fn get_bool_or(key: String, default: Bool) -> Bool { get_bool(key) |> result.unwrap(default)