Skip to content

Commit f2cc724

Browse files
committed
Initial commit!
This adds .gitignore, Cargo.toml, README.md and LICENSE, as well as the start of the API class and a test for logging in and getting authentication failure!
0 parents  commit f2cc724

File tree

6 files changed

+304
-0
lines changed

6 files changed

+304
-0
lines changed

Diff for: .gitignore

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# Compiled source #
2+
###################
3+
target
4+
/build-out/
5+
6+
# Packages #
7+
############
8+
*.7z
9+
*.dmg
10+
*.gz
11+
*.iso
12+
*.jar
13+
*.rar
14+
*.tar
15+
*.zip
16+
17+
# OS generated files #
18+
######################
19+
.DS_Store
20+
ehthumbs.db
21+
Icon?
22+
Thumbs.db
23+
24+
# Project files #
25+
#################
26+
.classpath
27+
.externalToolBuilders
28+
.idea
29+
.project
30+
.settings
31+
build
32+
dist
33+
nbproject
34+
atlassian-ide-plugin.xml
35+
build.xml
36+
nb-configuration.xml
37+
*.iml
38+
*.ipr
39+
*.iws
40+
*.sublime-project
41+
*.sublime-workspace
42+
Cargo.lock

Diff for: Cargo.toml

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
[package]
2+
name = "screeps-api"
3+
version = "0.1.0"
4+
authors = ["Dabo Ross <[email protected]>"]
5+
6+
[dependencies]
7+
hyper = "0.10"
8+
hyper-rustls = "0.3"
9+
serde_derive = "0.9"
10+
serde = "0.9"
11+
serde_json = "0.9"

Diff for: LICENSE

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
Copyright (c) 2017 David Ross
2+
3+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4+
5+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6+
7+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Diff for: README.md

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
rust-screeps-api
2+
================
3+
4+
Provides a wrapper in rust of the https://screeps.com API, using hyper for making requests and serde_json for interpreting results.
5+
6+
The API provided for screeps.com is unofficial, and not officially documented, but the developers of screeps have not been hostile in the past to those who use it. This API, and rust-screeps-api, can also be used to access Screeps Private Servers, provided the `screepsmod-auth` mod has been installed on them to provide non-steam-based authentication.
7+
8+
Documentation for the API calls that rust-screeps-api makes can be found at https://github.com/screepers/python-screeps/blob/master/docs/Endpoints.md.
9+
10+
When this library is relatively stable, I will post the rust docs online and provide some example usages. For now, the library does not do enough to warrant this.

Diff for: src/error.rs

+123
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
extern crate hyper;
2+
extern crate serde_json;
3+
4+
use std::fmt;
5+
use std::io;
6+
use std::error::Error as StdError;
7+
use self::Error::*;
8+
9+
#[derive(Debug)]
10+
pub enum Error {
11+
SerdeJson(serde_json::error::Error),
12+
Hyper(hyper::error::Error),
13+
Io(io::Error),
14+
StatusCode(hyper::status::StatusCode),
15+
Api(ApiError),
16+
}
17+
18+
pub type Result<T> = ::std::result::Result<T, Error>;
19+
20+
21+
impl From<serde_json::error::Error> for Error {
22+
fn from(err: serde_json::error::Error) -> Error { Error::SerdeJson(err) }
23+
}
24+
25+
impl From<hyper::error::Error> for Error {
26+
fn from(err: hyper::error::Error) -> Error { Error::Hyper(err) }
27+
}
28+
29+
impl From<io::Error> for Error {
30+
fn from(err: io::Error) -> Error { Error::Io(err) }
31+
}
32+
33+
impl From<hyper::status::StatusCode> for Error {
34+
fn from(code: hyper::status::StatusCode) -> Error { Error::StatusCode(code) }
35+
}
36+
37+
impl From<ApiError> for Error {
38+
fn from(err: ApiError) -> Error { Error::Api(err) }
39+
}
40+
41+
impl fmt::Display for Error {
42+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
43+
match *self {
44+
SerdeJson(ref err) => fmt::Display::fmt(err, f),
45+
Hyper(ref err) => fmt::Display::fmt(err, f),
46+
Io(ref err) => fmt::Display::fmt(err, f),
47+
StatusCode(ref status) => fmt::Display::fmt(status, f),
48+
Api(ref err) => fmt::Display::fmt(err, f),
49+
}
50+
}
51+
}
52+
53+
impl StdError for Error {
54+
fn description(&self) -> &str {
55+
match *self {
56+
SerdeJson(ref err) => err.description(),
57+
Hyper(ref err) => err.description(),
58+
Io(ref err) => err.description(),
59+
StatusCode(ref status) => {
60+
if let Some(reason) = status.canonical_reason() {
61+
return reason;
62+
}
63+
if status.is_success() {
64+
return "status code error: success";
65+
}
66+
if status.is_informational() {
67+
return "status code error: informational";
68+
}
69+
if status.is_redirection() {
70+
return "status code error: redirection";
71+
}
72+
if status.is_client_error() {
73+
return "status code error: client error";
74+
}
75+
if status.is_server_error() {
76+
return "status code error: server error";
77+
}
78+
if status.is_strange_status() {
79+
return "status code error: strange status";
80+
}
81+
return "status code error";
82+
},
83+
Api(ref err) => err.description(),
84+
}
85+
}
86+
87+
fn cause(&self) -> Option<&StdError> {
88+
match *self {
89+
SerdeJson(ref err) => Some(err),
90+
Hyper(ref err) => Some(err),
91+
Io(ref err) => Some(err),
92+
StatusCode(_) => None,
93+
Api(ref err) => Some(err),
94+
}
95+
}
96+
}
97+
98+
#[derive(Debug, Copy, Clone)]
99+
pub enum ApiError {
100+
NotOk(i32),
101+
MissingField(&'static str),
102+
}
103+
104+
105+
impl fmt::Display for ApiError {
106+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
107+
match *self {
108+
ApiError::NotOk(code) => write!(f, "non-ok result from api call: {}", code),
109+
ApiError::MissingField(field) => write!(f, "missing field from api call: {}", field),
110+
}
111+
}
112+
}
113+
114+
impl StdError for ApiError {
115+
fn description(&self) -> &str {
116+
match *self {
117+
ApiError::NotOk(_) => "non-ok result from api call",
118+
ApiError::MissingField(_) => "missing field from api call",
119+
}
120+
}
121+
122+
fn cause(&self) -> Option<&StdError> { None }
123+
}

Diff for: src/lib.rs

+111
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
#[macro_use]
2+
extern crate serde_derive;
3+
extern crate hyper;
4+
extern crate serde;
5+
extern crate serde_json;
6+
7+
mod error;
8+
9+
pub use error::{Error, ApiError, Result};
10+
use std::borrow::Cow;
11+
use hyper::header::{Headers, ContentType};
12+
13+
14+
/// Login details
15+
#[derive(Serialize, Debug)]
16+
pub struct LoginDetails<'a> {
17+
/// The email or username to log in with (either works)
18+
pub email: Cow<'a, str>,
19+
/// The password to log in with (steam auth is not supported)
20+
pub password: Cow<'a, str>,
21+
}
22+
23+
impl<'a> LoginDetails<'a> {
24+
/// Create a new login details with the given username and password
25+
pub fn new<'b, T: Into<Cow<'b, str>>>(email: T, password: T) -> LoginDetails<'b> {
26+
LoginDetails {
27+
email: email.into(),
28+
password: password.into()
29+
}
30+
}
31+
}
32+
33+
#[derive(Deserialize, Debug)]
34+
struct LoginResponse {
35+
ok: i32,
36+
token: Option<String>,
37+
}
38+
39+
#[derive(Debug)]
40+
pub struct API<'a> {
41+
token: Option<Cow<'a, str>>,
42+
}
43+
44+
impl<'a> API<'a> {
45+
pub fn new_anonymous() -> API<'static> {
46+
API { token: None }
47+
}
48+
49+
pub fn new_logged_in<'b>(client: &hyper::Client, login_details: &LoginDetails) -> Result<API<'b>> {
50+
let mut api = API::new_anonymous();
51+
52+
api.get_token(client, login_details)?;
53+
54+
Ok(api)
55+
}
56+
57+
fn get_token(&mut self, client: &hyper::Client, login_details: &LoginDetails) -> Result<()> {
58+
let body = serde_json::to_string(&login_details)?;
59+
60+
let mut headers = Headers::new();
61+
headers.set(ContentType::json());
62+
63+
let mut response = client.post("https://screeps.com/api/auth/signin")
64+
.body(&body)
65+
.headers(headers)
66+
.send()?;
67+
68+
if !response.status.is_success() {
69+
return Err(response.status.into());
70+
}
71+
72+
let result: LoginResponse = serde_json::from_reader(&mut response)?;
73+
74+
if result.ok != 1 {
75+
return Err(ApiError::NotOk(result.ok).into());
76+
}
77+
78+
if let Some(token) = result.token {
79+
self.token = Some(token.into());
80+
Ok(())
81+
} else {
82+
Err(ApiError::MissingField("token").into())
83+
}
84+
}
85+
}
86+
87+
#[cfg(test)]
88+
mod tests {
89+
use ::{API, LoginDetails};
90+
extern crate hyper;
91+
extern crate hyper_rustls;
92+
use hyper::client::Client;
93+
use hyper::net::HttpsConnector;
94+
95+
#[test]
96+
fn anonymous_creation() {
97+
let _unused = API::new_anonymous();
98+
}
99+
100+
#[test]
101+
fn login_creation_auth_failure() {
102+
let client = Client::with_connector(HttpsConnector::new(hyper_rustls::TlsClient::new()));
103+
let login = LoginDetails::new("username", "password");
104+
let result = API::new_logged_in(&client, &login);
105+
106+
match result {
107+
Err(::error::Error::StatusCode(hyper::status::StatusCode::Unauthorized)) => println!("Success!"),
108+
other => panic!("Expected unauthorized error, found {:?}", other),
109+
}
110+
}
111+
}

0 commit comments

Comments
 (0)