Open tonymushah opened 1 year ago
The API could be made more ergonomic. The builder pattern felt clunky whenever I tried to use my own library.
Could you please describe what feels clunky about it? I'd like to help improve the pattern but since I've not used this wrapper yet (planning to use it in my next project) I can't really make out what feels clunky
The API could be made more ergonomic. The builder pattern felt clunky whenever I tried to use my own library.
Could you please describe what feels clunky about it? I'd like to help improve the pattern but since I've not used this wrapper yet (planning to use it in my next project) I can't really make out what feels clunky
In the following example,
let manga = client
.manga()
.aggregate()
.manga_id(&manga_id)
.translated_language(languages)
.build()
.unwrap()
.send()
.await;
The build().unwrap()
part felt excessive and would be more intuitive to just call send()
after building the parameters like the manga ID; this is because of the use of the derive_builder
crate (docs). I believe version 3.0.0 refactors this to make it simpler to make requests.
The API could be made more ergonomic. The builder pattern felt clunky whenever I tried to use my own library.
Could you please describe what feels clunky about it? I'd like to help improve the pattern but since I've not used this wrapper yet (planning to use it in my next project) I can't really make out what feels clunky
In the following example,
let manga = client .manga() .aggregate() .manga_id(&manga_id) .translated_language(languages) .build() .unwrap() .send() .await;
The
build().unwrap()
part felt excessive and would be more intuitive to just callsend()
after building the parameters like the manga ID; this is because of the use of thederive_builder
crate (docs). I believe version 3.0.0 refactors this to make it simpler to make requests.
Instead of validating during build()
, you could do that during send()
and have the unwrap after that and return a Result<>
.
But to my knowledge an unwrap is needed somewhere in there, how would you otherwise let the user know something is wrong?
The link to burntsushi's comment does not work anymore so here's an alternative: https://red.artemislena.eu/r/rust/comments/gj8inf/rust_structuring_and_handling_errors_in_2020/fqlmknt/?context=3 The article they are referring to is this one here: https://nick.groenen.me/posts/rust-error-handling/
For convenience, I have copied the content here in case the new link goes down as well.
u/burntsushi > Great article! I agree with a lot of it. > > I do think it would be wise to be a bit more circumspect with recommendations for `thiserror` (and similar error handling helper libraries). In particular, they can have a big impact on compilation times. If you're already using proc macros somewhere, or if your dependency tree is already necessarily large, then `thiserror` seems like a great choice. But otherwise, writing out the impls by hand is very easy to do. I tried it myself and it only took me a couple minutes: > > ```rust > /// WordCountError enumerates all possible errors returned by this library. > #[derive(Debug)] > enum WordCountError { > /// Represents an empty source. For example, an empty text file being given > /// as input to `count_words()`. > EmptySource, > > /// Represents a failure to read from input. > ReadError { source: std::io::Error }, > > /// Represents all other cases of `std::io::Error`. > IOError(std::io::Error), > } > > impl std::error::Error for WordCountError { > fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { > match *self { > WordCountError::EmptySource => None, > WordCountError::ReadError { ref source } => Some(source), > WordCountError::IOError(_) => None, > } > } > } > > impl std::fmt::Display for WordCountError { > fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { > match *self { > WordCountError::EmptySource => { > write!(f, "Source contains no data") > } > WordCountError::ReadError { .. } => { > write!(f, "Read error") > } > WordCountError::IOError(ref err) => { > err.fmt(f) > } > } > } > } > > impl From
I have a bunch of stuff :sweat_smile: ... There are probably more I can list but this is already a lot.
anyhow
andthiserror
can be replaced with an error type that is manually implemented rather than derived. See burntsushi's comment https://libreddit.spike.codes/r/rust/comments/gj8inf/rust_structuring_and_handling_errors_in_2020/fqlmknt/?context=3 for some guidelines.derive-builder
can easily be done manually and would be good to remove.Owner Edit: This syntax doesn't work since v3 So the following example should be:
mangadex-api-types
,mangadex-api-schema
) was worth the trouble. It might be good to put them back into a single workspace but I can't say which is better honestly.v5
module is probably unnecessary. I made it because of the potential of future major API versions since at the time I made it, there was av2
andv5
of the MangaDex API.String
fields converted to an enum type where appropriate. Here is one example: https://github.com/tonymushah/mangadex-api/blob/a4da7a17ee93dfae3944f05840e39ff332c26ca7/mangadex-api-schema/src/v5/check_token_response.rs#L7Notes about some of the custom deserialization I added:
language_array_or_skip_null
exists because there are some manga that havenull
in theavailableTranslatedLanguages
field. (Example manga: https://api.mangadex.org/manga/a96676e5-8ae2-425e-b549-7f15dd34a6d8)localizedstring_array_or_map
used to be necessary for the.data.attributes.description
field of https://api.mangadex.org/manga/fe38c4c2-8cea-4f91-9e80-b368172bdd5c but it looks like that has been fixed.volume_aggregate_array_or_map
fixes an issue with some manga aggregate results where the.volumes.0.chapters
field would be an array instead of an object. For example, https://api.mangadex.org/manga/d773c8be-8e82-4ff1-a4e9-46171395319b/aggregate. This one has "none" as a key within.volumes
: https://api.mangadex.org/manga/14e157c3-7856-4f86-a2d2-8faa86660744/aggregateOriginally posted by @gondolyr in https://github.com/tonymushah/mangadex-api/issues/1#issuecomment-1462293341