Current section

Files

Jump to
given src given.gleam
Raw

src/given.gleam

//// This library attempts to make guards:
////
//// - Applicable to `Bool`, `Result`, `Option` and `List` types.
//// - Ergonomic to use by providing ways to handle both branches early.
//// - Expressive by making it easy to read through function names and labels.
//// - Comprehendable by not having to negate the conditions.
//// - Safe to use by not accidentally running discarded branches much like
//// `bool.lazy_guard`.
////
import given/internal/lib/optionx
import gleam/list
import gleam/option.{type Option, None, Some}
import gleam/result
/// Checks if the condition is true and runs the consequence if it is, else
/// runs the alternative.
///
/// ## Examples
///
/// ```gleam
/// import given
///
/// let user_understood = True
///
/// use <- given.that(user_understood, return: fn() { "Great!" })
/// // …else handle case where user did not understand here…
/// "Woof!"
/// ```
///
/// ```gleam
/// import given.{that as given}
///
/// let user_understood = True
///
/// use <- given(user_understood, return: fn() { "Great!" })
/// // …else handle case where user did not understand here…
/// "Woof!"
/// ```
///
pub fn that(
the_case requirement: Bool,
return consequence: fn() -> b,
else_return alternative: fn() -> b,
) -> b {
case requirement {
True -> consequence()
False -> alternative()
}
}
/// Checks if any of the conditions are true and runs the consequence if any
/// are, else runs the alternative.
///
/// ## Examples
///
/// ```gleam
/// import given
///
/// let is_admin = False
/// let is_editor = True
///
/// use <- given.any([is_admin, is_editor], return: fn() { "Great!" })
///
/// // …else handle case where user has no special role…
/// "Woof!"
/// ```
///
/// ```gleam
/// import given
///
/// let is_admin = False
/// let is_editor = True
///
/// use <- given.any(are_true_in: [is_admin, is_editor], return: fn() { "Great!" })
///
/// // …else handle case where user has no special role…
/// "Woof!"
/// ```
///
pub fn any(
are_true_in requirements: List(Bool),
return consequence: fn() -> b,
else_return alternative: fn() -> b,
) -> b {
case requirements |> list.any(fn(v) { v == True }) {
True -> consequence()
False -> alternative()
}
}
/// Checks if all of the conditions are true and runs the consequence if all
/// are, else runs the alternative.
///
/// ## Examples
///
/// ```gleam
/// import given
///
/// let is_active = True
/// let is_confirmed = True
///
/// use <- given.all([is_active, is_confirmed], return: fn() { "Great!" })
///
/// // …else handle case where user is not both active and confirmed…
/// "Woof!"
/// ```
///
/// ```gleam
/// import given
///
/// let is_active = True
/// let is_confirmed = True
///
/// use <- given.all(are_true_in: [is_active, is_confirmed], return: fn() { "Great!" })
///
/// // …else handle case where user is not both active and confirmed…
/// "Woof!"
/// ```
pub fn all(
are_true_in requirements: List(Bool),
return consequence: fn() -> b,
else_return alternative: fn() -> b,
) -> b {
case requirements |> list.all(fn(v) { v == True }) {
True -> consequence()
False -> alternative()
}
}
/// Checks if the condition is false and runs the consequence if it is, else
/// runs the alternative.
///
/// ## Examples
///
/// ```gleam
/// import given
///
/// let user_understood = True
///
/// use <- given.not(user_understood, return: fn() { "Woof!" })
///
/// // …else handle case where user understood here…
/// "Great!"
/// ```
///
/// ```gleam
/// import given
///
/// let user_understood = True
///
/// use <- given.not(the_case: user_understood, return: fn() { "Woof!" })
///
/// // …else handle case where user understood here…
/// "Great!"
/// ```
///
/// ```gleam
/// import given.{not as not_given}
///
/// let user_understood = True
///
/// use <- not_given(user_understood, return: fn() { "Woof!" })
///
/// // …else handle case where user understood here…
/// "Great!"
/// ```
///
pub fn not(
the_case requirement: Bool,
return consequence: fn() -> b,
else_return alternative: fn() -> b,
) -> b {
case requirement {
False -> consequence()
True -> alternative()
}
}
/// Checks if any of the conditions are false and runs the consequence if any
/// are, else runs the alternative.
///
/// ## Examples
///
/// ```gleam
/// import given
///
/// let is_admin = False
/// let is_editor = True
///
/// use <- given.not_any([is_admin, is_editor], return: fn() { "At least either Admin or Editor!" })
///
/// // …else handle case where user no special role…
/// "Woof!"
/// ```
///
/// ```gleam
/// import given
///
/// let is_admin = False
/// let is_editor = True
///
/// use <- given.not_any(are_true_in: [is_admin, is_editor], return: fn() { "At least either Admin or Editor!" })
///
/// // …else handle case where user no special role…
/// "Woof!"
/// ```
///
pub fn not_any(
are_true_in requirements: List(Bool),
return consequence: fn() -> b,
else_return alternative: fn() -> b,
) -> b {
case requirements |> list.any(fn(v) { v == True }) {
False -> consequence()
True -> alternative()
}
}
/// Checks if all of the conditions are false and runs the consequence if all
/// are, else runs the alternative.
///
/// ## Examples
///
/// ```gleam
/// import given
///
/// let is_active = True
/// let is_confirmed = True
///
/// use <- given.not_all([is_active, is_confirmed], return: fn() { "Cylone Sleeper Agent!" })
///
/// // …else handle case where user is neither active nor confirmed…
/// "Woof!"
/// ```
///
/// ```gleam
/// import given
///
/// let is_active = True
/// let is_confirmed = True
///
/// use <- given.not_all(are_true_in: [is_active, is_confirmed], return: fn() { "Cylone Sleeper Agent!" })
///
/// // …else handle case where user is neither active nor confirmed…
/// "Woof!"
///
pub fn not_all(
are_true_in requirements: List(Bool),
return consequence: fn() -> b,
else_return alternative: fn() -> b,
) -> b {
case requirements |> list.all(fn(v) { v == True }) {
False -> consequence()
True -> alternative()
}
}
/// Checks if the condition function returns `True` and runs the consequence if
/// it is, else runs the alternative.
///
/// Use to lazily evaluate a complex condition and return early if they fail.
///
/// ## Examples
///
/// ```gleam
/// import given
///
/// let enabled = fn() { False }
///
/// use <- given.when(enabled, else_return: fn() { "Not an Admin" })
///
/// // …handle case where user is an Admin…
/// "Indeed an Admin"
/// ```
///
/// ```gleam
/// import given
///
/// let enabled = fn() { False }
///
/// use <- given.when(enabled, return: fn() { "Indeed an Admin" })
///
/// // …handle case where user is not an Admin…
/// "Not an Admin"
/// ```
///
pub fn when(
the_case condition: fn() -> Bool,
else_return alternative: fn() -> b,
return consequence: fn() -> b,
) -> b {
case condition() {
True -> consequence()
False -> alternative()
}
}
/// Checks if the condition function returns `False` and runs the consequence if
/// it is, else runs the alternative.
///
/// Use to lazily evaluate a complex condition and return early if they fail.
///
/// ## Examples
///
/// ```gleam
/// import given
///
/// let enabled = fn() { False }
///
/// use <- given.when_not(enabled, else_return: fn() { "Indeed an Admin" })
///
/// // …handle case where user is not an Admin…
/// "Not an Admin"
/// ```
///
/// ```gleam
/// import given
///
/// let enabled = fn() { False }
///
/// use <- given.when_not(enabled, return: fn() { "Not an Admin" })
///
/// // …handle case where user is an Admin…
/// "Indeed an Admin"
/// ```
///
pub fn when_not(
the_case condition: fn() -> Bool,
else_return alternative: fn() -> b,
return consequence: fn() -> b,
) -> b {
case condition() {
False -> consequence()
True -> alternative()
}
}
/// Checks if the list is empty and runs the consequence if it is, else runs
/// the alternative.
///
/// ## Examples
///
/// ```gleam
/// import given
///
/// let list = []
///
/// use <- given.empty(list, else_return: fn() { "Non-empty" })
///
/// // …handle empty list here…
/// "Empty"
/// ```
///
pub fn empty(
list list: List(a),
else_return alternative: fn() -> b,
return consequence: fn() -> b,
) -> b {
case list {
[] -> consequence()
_not_empty -> alternative()
}
}
/// Checks if the list is non-empty and runs the consequence if it is, else
/// runs the alternative.
///
/// ## Examples
///
/// ```gleam
/// import given
///
/// let list = []
///
/// use <- given.non_empty(list, else_return: fn() { "Empty" })
///
/// // …handle non-empty list here…
/// "Non-empty"
/// ```
///
pub fn non_empty(
list list: List(a),
else_return alternative: fn() -> b,
return consequence: fn() -> b,
) -> b {
case list {
[] -> alternative()
_not_empty -> consequence()
}
}
/// Checks if the result is an `Ok` and runs the consequence if it is, else
/// runs the alternative.
///
/// ## Examples
///
/// ```gleam
/// import given
///
/// let result = Ok("Great")
///
/// use ok_value <- given.ok(in: result, else_return: fn(error_value) { "Error" })
///
/// // …handle Ok value here…
/// "Ok"
/// ```
///
/// ```gleam
/// import given.{ok as given_ok_in}
///
/// let result = Ok("Great")
///
/// use ok_value <- given_ok_in(result, else_return: fn(error_value) { "Error" })
///
/// // …handle Ok value here…
/// "Ok"
/// ```
///
pub fn ok(
in rslt: Result(a, e),
else_return alternative: fn(e) -> b,
return consequence: fn(a) -> b,
) -> b {
case rslt {
Ok(val) -> consequence(val)
Error(err) -> alternative(err)
}
}
/// Checks if any of the results are `Ok` and runs the consequence - passing in
/// the `Ok` and `Error` values - if they are, else runs the alternative passing
/// in all `Error` values.
///
/// ## Examples
///
/// ```gleam
/// import given
///
/// let results = [Ok("Great"), Error("Bad")]
///
/// use _oks, _errors <- given.any_ok(in: results, else_return: fn(_errors) { "All Errors" })
///
/// // …handle at least some OKs here…
/// "At least some OKs"
/// ```
///
pub fn any_ok(
in rslts: List(Result(a, e)),
else_return alternative: fn(List(e)) -> b,
return consequence: fn(List(a), List(e)) -> b,
) -> b {
let #(oks, errors) = rslts |> result.partition
case oks {
[] -> alternative(errors)
_non_zero_oks -> consequence(oks, errors)
}
}
/// Checks if all of the results are `Ok` and runs the consequence - passing in
/// the `Ok` values - if they are, else runs the alternative passing in all
/// `Ok` and `Error` values.
///
/// ## Examples
///
/// ```gleam
/// import given
///
/// let results = [Ok("Great"), Error("Bad")]
///
/// use oks <- given.all_ok(in: results, else_return: fn(_oks, _errors) { "Some Errors" })
///
/// // …handle all OKs here…
/// "All OKs"
/// ```
///
pub fn all_ok(
in rslts: List(Result(a, e)),
else_return alternative: fn(List(a), List(e)) -> b,
return consequence: fn(List(a)) -> b,
) -> b {
let #(oks, errors) = rslts |> result.partition
case errors {
[] -> consequence(oks)
_non_zero_errors -> alternative(oks, errors)
}
}
/// Checks if the result is an `Error` and runs the consequence if it is, else
/// runs the alternative.
///
/// ## Examples
///
/// ```gleam
/// import given
///
/// let result = Error(Nil)
///
/// use error_value <- given.error(in: result, else_return: fn(ok_value) { "Ok" })
///
/// // …handle Error value here…
/// "Error"
/// ```
///
/// ```gleam
/// import given.{error as given_error_in}
///
/// let result = Error(Nil)
///
/// use error_value <- given_error_in(result, else_return: fn(ok_value) { "Ok" })
///
/// // …handle Error value here…
/// "Error"
/// ```
///
pub fn error(
in rslt: Result(a, e),
else_return alternative: fn(a) -> b,
return consequence: fn(e) -> b,
) -> b {
case rslt {
Error(err) -> consequence(err)
Ok(val) -> alternative(val)
}
}
/// Checks if any of the results are `Error` and runs the consequence - passing
/// in the `Ok` and `Error` values - if they are, else runs the alternative
/// passing in all `Ok` values.
///
/// ## Examples
///
/// ```gleam
/// import given
///
/// let results = [Ok("Great"), Error("Bad")]
///
/// use _oks, _errors <- given.any_error(in: results, else_return: fn(_oks) { "Only OKs" })
///
/// // …handle at least some Errors here…
/// "At least some Errors"
/// ```
///
pub fn any_error(
in rslts: List(Result(a, e)),
else_return alternative: fn(List(a)) -> b,
return consequence: fn(List(a), List(e)) -> b,
) -> b {
let #(oks, errors) = rslts |> result.partition
case errors {
[] -> alternative(oks)
_non_zero_errors -> consequence(oks, errors)
}
}
/// Checks if all of the results are `Error` and runs the consequence - passing
/// in the `Error` values - if they are, else runs the alternative passing in
/// all `Ok` and `Error` values.
///
/// ## Examples
///
/// ```gleam
/// import given
///
/// let results = [Ok("Great"), Error("Bad")]
///
/// use _errors <- given.all_error(in: results, else_return: fn(_oks, _errors) { "Only some Errors" })
///
/// // …handle all errors here…
/// "All Errors"
/// ```
///
pub fn all_error(
in rslts: List(Result(a, e)),
else_return alternative: fn(List(a), List(e)) -> b,
return consequence: fn(List(e)) -> b,
) -> b {
let #(oks, errors) = rslts |> result.partition
case oks {
[] -> consequence(errors)
_non_zero_oks -> alternative(oks, errors)
}
}
/// Checks if the option is `Some` and runs the consequence if it is, else runs
/// the alternative.
///
/// ## Examples
///
/// ```gleam
/// import given
/// import gleam/option.{Some}
///
/// let option = Some("One")
///
/// use some_value <- given.some(in: option, else_return: fn() { "None" })
///
/// // …handle Some value here…
/// "Some value"
/// ```
///
/// ```gleam
/// import given.{some as given_some_in}
/// import gleam/option.{Some}
///
/// let option = Some("One")
///
/// use some_value <- given_some_in(option, else_return: fn() { "None" })
///
/// // …handle Some value here…
/// "Some value"
/// ```
///
pub fn some(
in optn: Option(a),
else_return alternative: fn() -> b,
return consequence: fn(a) -> b,
) -> b {
case optn {
Some(val) -> consequence(val)
None -> alternative()
}
}
/// Checks if any of the options are `Some` and runs the consequence - passing
/// in the `Some` values and a count of the `None` values - if they are, else
/// runs the alternative passing in the count of `None` values.
///
/// ## Examples
///
/// ```gleam
/// import given
///
/// let options = [Some("One"), None]
///
/// use _somes, _nones_count <- given.any_some(in: options, else_return: fn(_nones_count) { "All are None" })
///
/// // …handle at least some None values here…
/// "At least some are None"
/// ```
///
pub fn any_some(
in optns: List(Option(a)),
else_return alternative: fn(Int) -> b,
return consequence: fn(List(a), Int) -> b,
) -> b {
let #(somes, nones_count) = optns |> optionx.partition
case somes {
[] -> alternative(nones_count)
_non_zero_somes -> consequence(somes, nones_count)
}
}
/// Checks if all of the options are `Some` and runs the consequence - passing
/// in the `Some` values - if they are, else runs the alternative passing in
/// the `Some` and a count of the `None` values.
///
/// ## Examples
///
/// ```gleam
/// import given
///
/// let options = [Some("One"), None]
///
/// use _somes <- given.all_some(in: options, else_return: fn(_somes, _nones_count) { "Some are None" })
///
/// // …handle all Some values here…
/// "All are Some"
/// ```
///
pub fn all_some(
in optns: List(Option(a)),
else_return alternative: fn(List(a), Int) -> b,
return consequence: fn(List(a)) -> b,
) -> b {
let #(somes, nones_count) = optns |> optionx.partition
case nones_count {
0 -> consequence(somes)
_positive_none_count -> alternative(somes, nones_count)
}
}
/// Checks if the option is `None` and runs the consequence if it is, else runs
/// the alternative.
///
/// ## Examples
///
/// ```gleam
/// import given
/// import gleam/option.{None}
///
/// let option = None
///
/// use <- given.none(in: option, else_return: fn(some_value) { "Some value" })
///
/// // …handle None here…
/// "None"
/// ```
///
/// ```gleam
/// import given.{none as given_none_in}
/// import gleam/option.{None}
///
/// let option = None
///
/// use <- given_none_in(option, else_return: fn(some_value) { "Some value" })
/// // …handle None here…
///
/// "None"
/// ```
///
pub fn none(
in optn: Option(a),
else_return alternative: fn(a) -> b,
return consequence: fn() -> b,
) -> b {
case optn {
None -> consequence()
Some(val) -> alternative(val)
}
}
/// Checks if any of the options are `None` and runs the consequence if they
/// are, else runs the alternative passing in the `Some` values and the count
/// of `None` values.
///
/// ## Examples
///
/// ```gleam
/// import given
///
/// let options = [Some("One"), None]
///
/// use <- given.any_none(in: options, else_return: fn(_somes) { "All are Some" })
///
/// // …handle at least some None values here…
/// "At least some are None"
/// ```
///
pub fn any_none(
in optns: List(Option(a)),
else_return alternative: fn(List(a)) -> b,
return consequence: fn(List(a), Int) -> b,
) -> b {
let #(somes, nones_count) = optns |> optionx.partition
case nones_count {
0 -> alternative(somes)
_positive_none_count -> consequence(somes, nones_count)
}
}
/// Checks if all of the options are `None` and runs the consequence if they
/// are, else runs the alternative passing in the `Some` values.
///
/// ## Examples
///
/// ```gleam
/// import given
///
/// let options = [Some("One"), None]
///
/// use <- given.all_none(in: options, else_return: fn(_somes, _nones_count) { "Some are Some" })
///
/// // …handle all None values here…
/// "All are None"
/// ```
///
pub fn all_none(
in optns: List(Option(a)),
else_return alternative: fn(List(a), Int) -> b,
return consequence: fn() -> b,
) -> b {
let #(somes, nones_count) = optns |> optionx.partition
case somes {
[] -> consequence()
_non_zero_somes -> alternative(somes, nones_count)
}
}