diff --git a/src/tatoeba/api.gleam b/src/tatoeba/api.gleam index 4a3891b..94cbece 100644 --- a/src/tatoeba/api.gleam +++ b/src/tatoeba/api.gleam @@ -1,5 +1,7 @@ +import gleam/dynamic.{type Dynamic} import gleam/http.{type Header} import gleam/http/request.{type Request} +import gleam/json import gleam/list import gleam/option.{None, Some} import gleam/uri.{type Uri, Uri} @@ -22,6 +24,15 @@ pub const headers: List(Header) = [ #("User-Agent", "tatoeba.gleam (https://github.com/vxern/tatoeba)"), ] +/// The possible errors API calls can fail with. +/// +pub type ApiError { + /// Failed to make a request to Tatoeba. + RequestError(Dynamic) + /// Failed to decode the response data. + DecodeError(json.DecodeError) +} + /// Creates a new request to the given endpoint. /// /// This function is used over the bare `request.from_uri()` to add a step diff --git a/src/tatoeba/search.gleam b/src/tatoeba/search.gleam index 480ea75..122f759 100644 --- a/src/tatoeba/search.gleam +++ b/src/tatoeba/search.gleam @@ -319,7 +319,7 @@ fn results( /// Runs a search query using the passed `options` to filter the results. /// -pub fn run(options: SearchOptions) -> Result(SearchResults, String) { +pub fn run(options: SearchOptions) -> Result(SearchResults, api.ApiError) { let request = api.new_request_to("/search") |> request.set_method(http.Get) @@ -327,14 +327,18 @@ pub fn run(options: SearchOptions) -> Result(SearchResults, String) { use response <- result.try( httpc.send(request) - |> result.map_error(fn(_) { "Failed to send request to Tatoeba." }), + |> result.map_error(fn(error) { api.RequestError(error) }), ) + + response.body |> decode_payload() +} + +/// Decodes the received search payload. +/// +fn decode_payload(payload: String) -> Result(SearchResults, api.ApiError) { use results <- result.try( - json.decode(response.body, results) - |> result.map_error(fn(error) { - "Failed to decode sentence data: " - <> dynamic.classify(dynamic.from(error)) - }), + json.decode(payload, results) + |> result.map_error(fn(error) { api.DecodeError(error) }), ) Ok(results) diff --git a/src/tatoeba/sentence.gleam b/src/tatoeba/sentence.gleam index b4a3ecb..047d611 100644 --- a/src/tatoeba/sentence.gleam +++ b/src/tatoeba/sentence.gleam @@ -562,25 +562,16 @@ pub fn new_id(id: Int) -> Result(SentenceId, IdError) { } } -/// The possible errors sentence retrieval can fail with. -/// -pub type SentenceError { - /// Failed to make a request to Tatoeba. - RequestError(Dynamic) - /// Failed to decode the response data. - DecodeError(json.DecodeError) -} - /// Gets data of a single sentence in the Tatoeba corpus. /// -pub fn get(id id: SentenceId) -> Result(Option(Sentence), SentenceError) { +pub fn get(id id: SentenceId) -> Result(Option(Sentence), api.ApiError) { let request = api.new_request_to("/sentence/" <> int.to_string(id.value)) |> request.set_method(http.Get) use response <- result.try( httpc.send(request) - |> result.map_error(fn(error) { RequestError(error) }), + |> result.map_error(fn(error) { api.RequestError(error) }), ) case response.body |> string.length() { @@ -591,10 +582,10 @@ pub fn get(id id: SentenceId) -> Result(Option(Sentence), SentenceError) { /// Decodes the received sentence payload. /// -fn decode_payload(payload: String) -> Result(Sentence, SentenceError) { +fn decode_payload(payload: String) -> Result(Sentence, api.ApiError) { use sentence <- result.try( json.decode(payload, sentence) - |> result.map_error(fn(error) { DecodeError(error) }), + |> result.map_error(fn(error) { api.DecodeError(error) }), ) Ok(sentence)