//// A type that represents a remote data fetch import gleam/list import gleam/option.{type Option, None, Some} pub type RemoteData(a, error) { NotAsked Loading Failure(error) Success(a) } /// Map a function over the success value of a RemoteData /// /// ## Examples /// ```gleam /// map(over: Success(42), with: fn(x) { x * 2 }) /// // -> Success(84) /// ``` /// /// ```gleam /// map(over: Failure("error"), with: fn(x) { x * 2 }) /// // -> Failure("error") /// ``` pub fn map( over data: RemoteData(a, error), with mapper: fn(a) -> b, ) -> RemoteData(b, error) { case data { NotAsked -> NotAsked Loading -> Loading Failure(error) -> Failure(error) Success(a) -> Success(mapper(a)) } } /// Map a function over the success value of two RemoteData values /// /// ## Examples /// ```gleam /// map_2(over: Success(42), over_2: Success(2), with: fn(a, b) { a + b }) /// // -> Success(44) /// ``` /// /// ```gleam /// map_2(over: Failure("error"), over_2: Success(2), with: fn(a, b) { a + b }) /// // -> Failure("error") /// ``` pub fn map2( over data1: RemoteData(a, error), over2 data2: RemoteData(b, error), with mapper: fn(a, b) -> c, ) -> RemoteData(c, error) { case data1, data2 { Success(a), Success(b) -> Success(mapper(a, b)) Failure(error), _ | _, Failure(error) -> Failure(error) NotAsked, _ | _, NotAsked -> NotAsked Loading, _ | _, Loading -> Loading } } /// The same as `map2`, but with three RemoteData values /// Check `map2` for more details pub fn map3( over data1: RemoteData(a, error), over_2 data2: RemoteData(b, error), over_3 data3: RemoteData(c, error), with mapper: fn(a, b, c) -> d, ) -> RemoteData(d, error) { case data1, data2, data3 { Success(a), Success(b), Success(c) -> Success(mapper(a, b, c)) Failure(error), _, _ | _, Failure(error), _ | _, _, Failure(error) -> Failure(error) NotAsked, _, _ | _, NotAsked, _ | _, _, NotAsked -> NotAsked Loading, _, _ | _, Loading, _ | _, _, Loading -> Loading } } /// Map a function over the error value of a RemoteData /// /// ## Examples /// ```gleam /// map_error(over: Success(42), with: fn(_) { "error" }) /// // -> Success(42) /// ``` /// /// ```gleam /// map_error(over: Failure(42), with: fn(_) { "error" }) /// // -> Failure("error") /// ``` pub fn map_error( over data: RemoteData(a, error), with mapper: fn(error) -> error_b, ) -> RemoteData(a, error_b) { case data { NotAsked -> NotAsked Loading -> Loading Failure(error) -> Failure(mapper(error)) Success(a) -> Success(a) } } /// Chain a function that returns a RemoteData over the success value of a RemoteData /// /// ## Examples /// ```gleam /// and_then(over: Success(42), with: fn(x) { Success(x * 2) }) /// // -> Success(84) /// ``` /// /// ```gleam /// and_then(over: Failure("error"), with: fn(x) { Success(x * 2) }) /// // -> Failure("error") /// ``` /// /// ```gleam /// and_then(over: Success(42), with: fn(x) { Failure("error") }) /// // -> Failure("error") /// ``` pub fn try( over data: RemoteData(a, error), with mapper: fn(a) -> RemoteData(b, error), ) -> RemoteData(b, error) { case data { NotAsked -> NotAsked Loading -> Loading Failure(error) -> Failure(error) Success(a) -> mapper(a) } } /// Unwrap a RemoteData, providing a default value if the data is not Success /// ## Examples /// ```gleam /// unwrap(data: Success(42), or: 0) /// // -> 42 /// ``` /// /// ```gleam /// unwrap(data: Failure("error"), or: 0) /// // -> 0 /// ``` pub fn unwrap(data: RemoteData(a, error), or default: a) -> a { case data { Success(a) -> a _ -> default } } /// Convert a RemoteData to an Option /// ## Examples /// ```gleam /// to_option(data: Success(42)) /// // -> Some(42) /// ``` /// /// ```gleam /// to_option(data: Failure("error")) /// // -> None /// ``` pub fn to_option(data: RemoteData(a, error)) -> Option(a) { case data { Success(a) -> Some(a) _ -> None } } /// Convert an Option to a RemoteData /// ## Examples /// ```gleam /// from_option(option: Some(42), or: "error") /// // -> Success(42) /// ``` /// /// ```gleam /// from_option(option: None, or: "error") /// // -> Failure("error") /// ``` pub fn from_option(option: Option(a), or error: error) -> RemoteData(a, error) { case option { Some(a) -> Success(a) None -> Failure(error) } } /// Convert a RemoteData to a Result /// If the data is NotAsked or Loading, it will be converted to an Error with the provided error /// ## Examples /// ```gleam /// to_result(data: Success(42), or: "error") /// // -> Ok(42) /// ``` /// /// ```gleam /// to_result(data: Failure("error"), or: "another error") /// // -> Error("error") /// ``` /// /// ```gleam /// to_result(data: Loading, or: "another error") /// // -> Error("another error") /// ``` pub fn to_result( data: RemoteData(a, error), or error: error, ) -> Result(a, error) { case data { Success(a) -> Ok(a) Failure(error) -> Error(error) _ -> Error(error) } } /// Convert a Result to a RemoteData /// ## Examples /// ```gleam /// from_result(result: Ok(42)) /// // -> Success(42) /// ``` /// /// ```gleam /// from_result(result: Error("error")) /// // -> Failure("error") /// ``` pub fn from_result(result: Result(a, error)) -> RemoteData(a, error) { case result { Ok(a) -> Success(a) Error(error) -> Failure(error) } } /// Convert a list of RemoteData to a RemoteData of a list /// ## Examples /// ```gleam /// from_list([Success(42), Success(43)]) /// // -> Success([42, 43]) /// ``` /// /// ```gleam /// from_list([Success(42), Failure("error")]) /// // -> Failure("error") /// ``` pub fn from_list( data_list: List(RemoteData(a, error)), ) -> RemoteData(List(a), error) { use acc, data <- list.fold(data_list, Success([])) use acc_value, data_value <- map2(acc, data) list.append(acc_value, [data_value]) } /// Check if a RemoteData is a Success /// ## Examples /// ```gleam /// is_not_asked(Success(42)) /// // -> False /// ``` /// /// ```gleam /// is_not_asked(NotAsked) /// // -> True /// ``` pub fn is_not_asked(data: RemoteData(_, _)) -> Bool { case data { NotAsked -> True _ -> False } } /// Check if a RemoteData is a Success /// ## Examples /// ```gleam /// is_loading(Success(42)) /// // -> True /// ``` /// /// ```gleam /// is_loading(Failure("error")) /// // -> False /// ``` pub fn is_loading(data: RemoteData(_, _)) -> Bool { case data { Loading -> True _ -> False } } /// Check if a RemoteData is a Success /// ## Examples /// ```gleam /// is_failure(Success(42)) /// // -> True /// ``` /// /// ```gleam /// is_failure(Failure("error")) /// // -> False /// ``` pub fn is_failure(data: RemoteData(_, _)) -> Bool { case data { Failure(_) -> True _ -> False } } /// Check if a RemoteData is a Success /// ## Examples /// ```gleam /// is_success(Success(42)) /// // -> True /// ``` /// /// ```gleam /// is_success(Failure("error")) /// // -> False /// ``` pub fn is_success(data: RemoteData(_, _)) -> Bool { case data { Success(_) -> True _ -> False } }