Merge pull request #1 from Trivernis/develop

Endpoints to retrieve types by id/slug/basename
main
Trivernis 3 years ago committed by GitHub
commit 07f27d718f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -0,0 +1,33 @@
name: Build and Test
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main, develop ]
env:
CARGO_TERM_COLOR: always
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Cache build data
uses: actions/cache@v2
with:
path: |
target
~/.cargo/
key: ${{ runner.os }}-cargo-${{ hashFiles('Cargo.lock') }}
restore-keys: |
${{ runner.os }}-cargo-
- name: Build
run: cargo build --verbose
- name: Run tests
run: cargo test --verbose -- --test-threads 3

2
Cargo.lock generated

@ -2,7 +2,7 @@
# It is not intended for manual editing.
[[package]]
name = "animethemes-rs"
version = "0.1.0"
version = "0.2.0"
dependencies = [
"reqwest",
"serde",

@ -1,6 +1,6 @@
[package]
name = "animethemes-rs"
version = "0.1.0"
version = "0.2.0"
authors = ["trivernis <trivernis@protonmail.com>"]
edition = "2018"
readme = "README.md"

@ -1,7 +1,13 @@
use crate::error::ApiResult;
use crate::models::SearchResponse;
use crate::models::{
Anime, AnimeSynonym, Artist, Image, Resource, SearchResponse, Series, Song, Theme, ThemeEntry,
Video,
};
use reqwest::Response;
use serde::de::DeserializeOwned;
use serde::Serialize;
use std::collections::HashMap;
use std::fmt::Display;
pub static DEFAULT_API_ENDPOINT: &str = "https://staging.animethemes.moe/api";
pub static DEFAULT_VIDEO_ENDPOINT: &str = "https://animethemes.moe/video/";
@ -56,12 +62,10 @@ impl AnimeThemesClient {
fields: &[&str],
include: &[&str],
) -> ApiResult<SearchResponse> {
let fields = fields.join(",");
let include = include.join(",");
let mut query = vec![("q", query), ("include", include.as_str())];
let mut query = vec![("q", query.to_string()), ("include", include.join(","))];
if !fields.is_empty() {
query.push(("fields[search]", fields.as_str()));
query.push(("fields[search]", fields.join(",")));
}
let mut response: HashMap<String, SearchResponse> =
self.api_get("/search", &query[..]).await?.json().await?;
@ -69,8 +73,78 @@ impl AnimeThemesClient {
Ok(response.remove("search").unwrap())
}
/// Posts a get request to the API endpoint
async fn api_get(&self, path: &str, query: &[(&str, &str)]) -> ApiResult<Response> {
/// Returns an anime by a given slug string
pub async fn anime(&self, slug: &str, include: &[&str]) -> ApiResult<Anime> {
self.entry_by_id_with_include("anime", slug, include).await
}
/// Returns an artist by a given slug string
pub async fn artist(&self, slug: &str, include: &[&str]) -> ApiResult<Artist> {
self.entry_by_id_with_include("artist", slug, include).await
}
/// Returns an entry by a given id
pub async fn entry(&self, id: u32, include: &[&str]) -> ApiResult<ThemeEntry> {
self.entry_by_id_with_include("entry", id, include).await
}
/// Returns an image by id
pub async fn image(&self, id: u32, include: &[&str]) -> ApiResult<Image> {
self.entry_by_id_with_include("image", id, include).await
}
/// Returns a resource by id
pub async fn resource(&self, id: u32, include: &[&str]) -> ApiResult<Resource> {
self.entry_by_id_with_include("resource", id, include).await
}
/// Returns a series by slug
pub async fn series(&self, slug: &str, include: &[&str]) -> ApiResult<Series> {
self.entry_by_id_with_include("series", slug, include).await
}
/// Returns a song by id
pub async fn song(&self, id: u32, include: &[&str]) -> ApiResult<Song> {
self.entry_by_id_with_include("song", id, include).await
}
/// Returns a synonym by id
pub async fn synonym(&self, id: u32, include: &[&str]) -> ApiResult<AnimeSynonym> {
self.entry_by_id_with_include("synonym", id, include).await
}
/// Returns a theme by id
pub async fn theme(&self, id: u32, include: &[&str]) -> ApiResult<Theme> {
self.entry_by_id_with_include("theme", id, include).await
}
/// Returns a video by basename
pub async fn video(&self, basename: &str, include: &[&str]) -> ApiResult<Video> {
self.entry_by_id_with_include("video", basename, include)
.await
}
/// Generic endpoint with the format /<endpoint>/<id> returning the type on the json field <endpoint>
async fn entry_by_id_with_include<T: DeserializeOwned, I: Display>(
&self,
endpoint: &str,
id: I,
include: &[&str],
) -> ApiResult<T> {
let mut response: HashMap<String, T> = self
.api_get(
format!("/{}/{}", endpoint, id).as_str(),
&[("include", include.join(","))],
)
.await?
.json()
.await?;
Ok(response.remove(endpoint).unwrap())
}
/// Starts a get request to the API endpoint
async fn api_get<T: Serialize + ?Sized>(&self, path: &str, query: &T) -> ApiResult<Response> {
let response = self
.client
.get(format!("{}{}", self.api_endpoint, path))

@ -36,6 +36,7 @@ pub struct AnimeSynonym {
#[serde(flatten)]
pub meta: EntryMetadata,
pub text: String,
pub anime: Option<Anime>,
}
#[derive(Debug, Clone, Deserialize)]
@ -49,6 +50,8 @@ pub struct Theme {
pub group: String,
pub slug: String,
pub song: Option<Song>,
pub anime: Option<Anime>,
pub entries: Option<Vec<ThemeEntry>>,
}
#[derive(Debug, Clone, Deserialize)]
@ -63,7 +66,7 @@ pub struct Song {
pub meta: EntryMetadata,
pub title: String,
pub artists: Option<Vec<Artist>>,
pub entries: Option<Vec<ThemeEntry>>,
pub themes: Option<Vec<Theme>>,
}
#[derive(Debug, Clone, Deserialize)]
@ -71,9 +74,10 @@ pub struct Artist {
#[serde(flatten)]
pub meta: EntryMetadata,
pub name: String,
pub slog: String,
pub slug: String,
#[serde(alias = "as")]
pub as_character: String,
pub as_character: Option<String>,
pub songs: Option<Vec<Song>>,
}
#[derive(Debug, Clone, Deserialize)]
@ -107,6 +111,7 @@ pub struct Video {
pub source: Option<VideoSource>,
pub overlap: VideoOverlap,
pub link: String,
pub entries: Option<Vec<ThemeEntry>>,
}
#[derive(Debug, Clone, Deserialize)]
@ -131,6 +136,7 @@ pub struct Series {
pub meta: EntryMetadata,
pub name: String,
pub slug: String,
pub anime: Option<Vec<Anime>>,
}
#[derive(Debug, Clone, Deserialize)]
@ -139,10 +145,11 @@ pub struct Resource {
pub meta: EntryMetadata,
pub link: String,
pub external_id: u32,
#[serde(alias = "type")]
pub resource_type: String,
pub site: String,
#[serde(alias = "as")]
pub resource_as: String,
pub resource_as: Option<String>,
pub anime: Option<Vec<Anime>>,
pub artists: Option<Vec<Artist>>,
}
#[derive(Debug, Clone, Deserialize)]
@ -151,6 +158,8 @@ pub struct Image {
pub meta: EntryMetadata,
pub path: String,
pub facet: ImageFacet,
pub anime: Option<Vec<Anime>>,
pub artists: Option<Vec<Artist>>,
}
#[derive(Debug, Clone, Deserialize)]

@ -13,3 +13,92 @@ async fn it_searches() {
assert!(result.themes.is_some());
assert!(result.videos.is_some());
}
#[tokio::test]
async fn it_returns_anime_by_slug() {
let client = AnimeThemesClient::default();
let result = client
.anime("vivy_fluorite_eyes_song", &["themes"])
.await
.unwrap();
assert!(result.themes.is_some());
}
#[tokio::test]
async fn it_returns_artists_by_slug() {
let client = AnimeThemesClient::default();
let result = client.artist("lisa", &["songs"]).await.unwrap();
assert!(result.songs.is_some());
}
#[tokio::test]
async fn it_returns_entries_by_id() {
let client = AnimeThemesClient::default();
let result = client.entry(11948, &["videos"]).await.unwrap();
assert!(result.videos.is_some())
}
#[tokio::test]
async fn it_returns_images_by_id() {
let client = AnimeThemesClient::default();
let result = client.image(7247, &["anime"]).await.unwrap();
assert!(result.anime.is_some())
}
#[tokio::test]
async fn it_returns_resources_by_id() {
let client = AnimeThemesClient::default();
let result = client.resource(3588, &["anime"]).await.unwrap();
assert!(result.anime.is_some())
}
#[tokio::test]
async fn it_returns_series_by_slug() {
let client = AnimeThemesClient::default();
let result = client
.series("shingeki_no_kyojin", &["anime"])
.await
.unwrap();
assert!(result.anime.is_some())
}
#[tokio::test]
async fn it_returns_synonyms_by_id() {
let client = AnimeThemesClient::default();
let result = client.synonym(2462, &["anime"]).await.unwrap();
assert!(result.anime.is_some())
}
#[tokio::test]
async fn it_returns_songs_by_id() {
let client = AnimeThemesClient::default();
let result = client.song(8188, &["themes"]).await.unwrap();
assert!(result.themes.is_some())
}
#[tokio::test]
async fn it_returns_themes_by_id() {
let client = AnimeThemesClient::default();
let result = client.theme(8187, &["entries"]).await.unwrap();
assert!(result.entries.is_some())
}
#[tokio::test]
async fn it_returns_videos_by_basename() {
let client = AnimeThemesClient::default();
let result = client
.video("KimiUso-OP2.webm", &["entries"])
.await
.unwrap();
assert!(result.entries.is_some())
}

Loading…
Cancel
Save