Skip to content

Commit a9c0605

Browse files
committed
add rust swapi client
1 parent 2727fa6 commit a9c0605

File tree

6 files changed

+251
-0
lines changed

6 files changed

+251
-0
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
/target
2+
Cargo.lock

Cargo.toml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
[package]
2+
name = "swapi_client"
3+
version = "0.0.1"
4+
edition = "2018"
5+
authors = ["Jensen Wijaya <[email protected]>"]
6+
description = "Rust client for Star Wars API (https://swapi.co/)"
7+
license = "MIT"
8+
9+
[dependencies]
10+
reqwest = { version = "0.11", features = ["blocking", "json"] }
11+
serde = { version = "1.0.130", features = ["derive"] }
12+
serde_json = "1.0"

src/common.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
//! Shared libs
2+
3+
use std::error::Error;
4+
use std::fmt;
5+
6+
#[derive(Debug, serde::Deserialize, Default, Clone)]
7+
pub struct ListData<T> {
8+
pub count: usize,
9+
pub next: Option<String>,
10+
pub previous: Option<String>,
11+
pub results: Vec<T>,
12+
}
13+
14+
impl<T> IntoIterator for ListData<T> {
15+
type Item = T;
16+
type IntoIter = std::vec::IntoIter<Self::Item>;
17+
18+
fn into_iter(self) -> Self::IntoIter {
19+
self.results.into_iter()
20+
}
21+
}
22+
23+
#[derive(Debug)]
24+
pub struct RequestFailed(pub reqwest::blocking::Response);
25+
26+
impl fmt::Display for RequestFailed {
27+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
28+
write!(f, "Request failed")
29+
}
30+
}
31+
32+
impl Error for RequestFailed {}

src/lib.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
//! Rust client for Star Wars API
2+
3+
pub mod common;
4+
pub mod objects;
5+
mod requests;
6+
7+
pub use crate::objects::{Film, People, Planet, Species, Starship, Vehicle};
8+
pub use crate::requests::RequestHandler;

src/objects.rs

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
//! Star Wars Objects
2+
3+
use crate::requests::RequestHandler;
4+
5+
use serde::Deserialize;
6+
7+
/// Planet resource within the Star Wars universe.
8+
#[derive(Debug, Deserialize, Default, Clone)]
9+
10+
pub struct Planet {
11+
pub climate: String,
12+
pub diameter: String,
13+
pub gravity: String,
14+
pub name: String,
15+
pub orbital_period: String,
16+
pub population: String,
17+
pub residents: Vec<String>,
18+
pub rotation_period: String,
19+
pub surface_water: String,
20+
pub terrain: String,
21+
pub url: String,
22+
}
23+
24+
/// People resource within the Star Wars universe.
25+
#[derive(Debug, Deserialize, Default, Clone)]
26+
pub struct People {
27+
pub birth_year: String,
28+
pub eye_color: String,
29+
pub films: Vec<String>,
30+
pub gender: String,
31+
pub hair_color: String,
32+
pub height: String,
33+
pub homeworld: String,
34+
pub mass: String,
35+
pub name: String,
36+
pub skin_color: String,
37+
pub created: String,
38+
pub edited: String,
39+
pub species: Vec<String>,
40+
pub starships: Vec<String>,
41+
pub url: String,
42+
pub vehicles: Vec<String>,
43+
}
44+
45+
/// Film resource within the Star Wars universe.
46+
#[derive(Debug, Deserialize, Default, Clone)]
47+
pub struct Film {
48+
characters: Vec<String>,
49+
created: String,
50+
director: String,
51+
edited: String,
52+
episode_id: usize,
53+
opening_crawl: String,
54+
planets: Vec<String>,
55+
producer: String,
56+
release_date: String,
57+
species: Vec<String>,
58+
starships: Vec<String>,
59+
title: String,
60+
url: String,
61+
vehicles: Vec<String>,
62+
}
63+
64+
/// Starship resource within the Star Wars universe.
65+
#[derive(Debug, Deserialize, Default, Clone)]
66+
pub struct Starship {
67+
#[serde(alias = "MGLT")]
68+
mglt: String,
69+
70+
cargo_capacity: String,
71+
consumables: String,
72+
cost_in_credits: String,
73+
created: String,
74+
crew: String,
75+
edited: String,
76+
hyperdrive_rating: String,
77+
length: String,
78+
manufacturer: String,
79+
max_atmosphering_speed: String,
80+
model: String,
81+
name: String,
82+
passengers: String,
83+
films: Vec<String>,
84+
pilots: Vec<String>,
85+
starship_class: String,
86+
url: String,
87+
}
88+
89+
/// Vehicle resource within the Star Wars universe.
90+
#[derive(Debug, Deserialize, Default, Clone)]
91+
pub struct Vehicle {
92+
cargo_capacity: String,
93+
consumables: String,
94+
cost_in_credits: String,
95+
created: String,
96+
crew: String,
97+
edited: String,
98+
length: String,
99+
manufacturer: String,
100+
max_atmosphering_speed: String,
101+
model: String,
102+
name: String,
103+
passengers: String,
104+
pilots: Vec<String>,
105+
films: Vec<String>,
106+
url: String,
107+
vehicle_class: String,
108+
}
109+
110+
/// Species resource within the Star Wars universe.
111+
#[derive(Debug, Deserialize, Default, Clone)]
112+
pub struct Species {
113+
average_height: String,
114+
average_lifespan: String,
115+
classification: String,
116+
created: String,
117+
designation: String,
118+
edited: String,
119+
eye_colors: String,
120+
hair_colors: String,
121+
homeworld: Option<String>,
122+
language: String,
123+
name: String,
124+
people: Vec<String>,
125+
films: Vec<String>,
126+
skin_colors: String,
127+
url: String,
128+
}
129+
130+
impl RequestHandler for Planet {
131+
const URL_PATH: &'static str = "/planets/";
132+
}
133+
134+
impl RequestHandler for People {
135+
const URL_PATH: &'static str = "/people/";
136+
}
137+
138+
impl RequestHandler for Film {
139+
const URL_PATH: &'static str = "/films/";
140+
}
141+
142+
impl RequestHandler for Starship {
143+
const URL_PATH: &'static str = "/starships/";
144+
}
145+
146+
impl RequestHandler for Vehicle {
147+
const URL_PATH: &'static str = "/vehicles/";
148+
}
149+
150+
impl RequestHandler for Species {
151+
const URL_PATH: &'static str = "/species/";
152+
}

src/requests.rs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
use crate::common::{ListData, RequestFailed};
2+
3+
use reqwest::StatusCode;
4+
use serde::de::DeserializeOwned;
5+
use std::error::Error;
6+
7+
const API_URL: &'static str = "https://swapi.dev/api";
8+
9+
type Output<T> = std::result::Result<T, Box<dyn Error>>;
10+
11+
/// Main request handler for query starwars objects
12+
pub trait RequestHandler {
13+
const URL_PATH: &'static str;
14+
15+
fn get(id: usize) -> Output<Box<Self>>
16+
where
17+
Self: DeserializeOwned,
18+
{
19+
match reqwest::blocking::get(API_URL.to_owned() + Self::URL_PATH + &id.to_string()) {
20+
Ok(res) => match res.status() {
21+
StatusCode::OK => Ok(Box::new(res.json::<Self>().unwrap())),
22+
_ => Err(RequestFailed(res).into()),
23+
},
24+
Err(error) => Err(error.into()),
25+
}
26+
}
27+
28+
fn list(page: Option<usize>) -> Output<Box<ListData<Self>>>
29+
where
30+
Self: DeserializeOwned,
31+
{
32+
let page = match page {
33+
Some(x) => "?page=".to_owned() + &x.to_string(),
34+
None => String::from("?page=1"),
35+
};
36+
37+
match reqwest::blocking::get(API_URL.to_owned() + Self::URL_PATH + &page) {
38+
Ok(res) => match res.status() {
39+
StatusCode::OK => Ok(Box::new(res.json::<ListData<Self>>().unwrap())),
40+
_ => Err(RequestFailed(res).into()),
41+
},
42+
Err(error) => Err(error.into()),
43+
}
44+
}
45+
}

0 commit comments

Comments
 (0)