diff --git a/.travis.yml b/.travis.yml index c2512c1..35b5021 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,9 +1,15 @@ +cache: + directories: + - target/deps/ + - target/.fingerprint/ + - target/build/ language: rust -rust: - - stable - - beta - - nightly + matrix: allow_failures: - rust: nightly +rust: + - stable + - beta + - nightly \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index ac773cf..55018ed 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,7 @@ name = "opm" version = "0.1.0" dependencies = [ + "chrono 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "clap 2.20.5 (registry+https://github.com/rust-lang/crates.io-index)", "flexi_logger 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", "hyper 0.10.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -36,6 +37,15 @@ dependencies = [ "time 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "chrono" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "num 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "clap" version = "2.20.5" @@ -349,6 +359,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "23ac7c30002a5accbf7e8987d0632fa6de155b7c3d39d0067317a391e00a2ef6" "checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d" "checksum chrono 0.2.25 (registry+https://github.com/rust-lang/crates.io-index)" = "9213f7cd7c27e95c2b57c49f0e69b1ea65b27138da84a170133fd21b07659c00" +"checksum chrono 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "158b0bd7d75cbb6bf9c25967a48a2e9f77da95876b858eadfabaa99cd069de6e" "checksum clap 2.20.5 (registry+https://github.com/rust-lang/crates.io-index)" = "7db281b0520e97fbd15cd615dcd8f8bcad0c26f5f7d5effe705f090f39e9a758" "checksum flexi_logger 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "04d22e6979b41afe7b4da2a113c93156ab2b51d8482960c319faa3e1f5db3272" "checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb" diff --git a/Cargo.toml b/Cargo.toml index da04223..4c99d7a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,3 +12,4 @@ flexi_logger = "0.5.2" hyper = "0.10.5" log = "0.3.7" json = "0.11.5" +chrono = "0.3.0" \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index e7a11a9..6e79e45 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,11 @@ +#[macro_use] +extern crate json; +#[macro_use] +extern crate hyper; +extern crate chrono; + +mod ore; + fn main() { println!("Hello, world!"); } diff --git a/src/ore.rs b/src/ore.rs new file mode 100644 index 0000000..3acf883 --- /dev/null +++ b/src/ore.rs @@ -0,0 +1,258 @@ +use ::chrono::prelude::*; +use ::std::io::{Result, Read}; +use ::hyper::*; +use ::hyper::client::*; +use ::json; +use ::json::*; + +#[derive(Debug, Clone, Copy)] +pub enum PluginCategory { + AdminTools, Chat, DeveloperTools, Economy, Gameplay, Games, Protection, RolePlaying, WorldManagement, Miscellaneous +} + +impl PluginCategory { + fn to_int(&self) -> u8 { + match *self { + PluginCategory::AdminTools => 0, + PluginCategory::Chat => 1, + PluginCategory::DeveloperTools => 2, + PluginCategory::Economy => 3, + PluginCategory::Gameplay => 4, + PluginCategory::Games => 5, + PluginCategory::Protection => 6, + PluginCategory::RolePlaying => 7, + PluginCategory::WorldManagement => 8, + PluginCategory::Miscellaneous => 9, + } + } + fn from_int(id: u8) -> Self { + match id { + 0 => PluginCategory::AdminTools, + 1 => PluginCategory::Chat, + 2 => PluginCategory::DeveloperTools, + 3 => PluginCategory::Economy, + 4 => PluginCategory::Gameplay, + 5 => PluginCategory::Games, + 6 => PluginCategory::Protection, + 7 => PluginCategory::RolePlaying, + 8 => PluginCategory::WorldManagement, + 9 => PluginCategory::Miscellaneous, + _ => unreachable!(), + } + } + fn from_str(id: &str) -> Self { + match id { + "Admin Tools" => PluginCategory::AdminTools, + "Chat" => PluginCategory::Chat, + "Developer Tools" => PluginCategory::DeveloperTools, + "Economy" => PluginCategory::Economy, + "Gameplay" => PluginCategory::Gameplay, + "Games" => PluginCategory::Games, + "Protection" => PluginCategory::Protection, + "Role Playing" => PluginCategory::RolePlaying, + "World Management" => PluginCategory::WorldManagement, + "Miscellaneous" => PluginCategory::Miscellaneous, + _ => unreachable!(), + } + } +} + +#[derive(Debug, Clone, Copy)] +pub enum SortType { + RecentlyUpdated, MostStars, MostDownloads, MostViews, Newest +} + +impl SortType { + fn to_int(&self) -> u8 { + match *self { + SortType::MostStars => 0, + SortType::MostDownloads => 1, + SortType::MostViews => 2, + SortType::Newest => 3, + SortType::RecentlyUpdated => 4, + } + } + fn from_int(id: u8) -> Self { + match id { + 0 => SortType::MostStars, + 1 => SortType::MostDownloads, + 2 => SortType::MostViews, + 3 => SortType::Newest, + 4 => SortType::RecentlyUpdated, + _ => unreachable!(), + } + } +} + +#[derive(Clone, Debug)] +pub struct SearchQuery<'a> { + categories: Option>, + sort: Option, + query: &'a str, + limit: Option, + offset: Option, +} + +impl<'a> SearchQuery<'a> { + pub fn set_categories(&mut self, categories: &Vec) -> &mut Self { + self.categories = Some(categories.clone()); + self + } + pub fn set_sort_type(&mut self, sort_type: SortType) -> &mut Self { + self.sort = Some(sort_type); + self + } + pub fn set_limit(&mut self, limit: u32) -> &mut Self { + self.limit = Some(limit); + self + } + pub fn set_offset(&mut self, offset: u32) -> &mut Self { + self.offset = Some(offset); + self + } + pub fn exec<'b>(self) -> Vec> { + let client = Client::new(); + let mut url = Url::parse("https://ore.spongepowered.org/api/projects").unwrap(); + { + let mut pairs = url.query_pairs_mut(); + if let Some(categories) = self.categories { + pairs.append_pair("categories", categories.into_iter().map(|x| x.to_int()).fold(String::new(), |x, y| x + "," + y.to_string().as_str()).as_str()); + } + if let Some(sort) = self.sort { + pairs.append_pair("sort", sort.to_int().to_string().as_str()); + } + if let Some(limit) = self.limit { + pairs.append_pair("limit", limit.to_string().as_str()); + } + if let Some(offset) = self.offset { + pairs.append_pair("offset", offset.to_string().as_str()); + } + pairs.append_pair("q", self.query); + } + let mut plugins: Vec = Vec::new(); + + let mut res = String::new(); + client.get(url).send().unwrap().read_to_string(&mut res); + let result = json::parse(res.as_str()).unwrap(); + for plugin in result.members() { + plugins.push(parse_plugin(plugin)); + } + + plugins + } +} + +pub fn search(query: &str) -> SearchQuery { + SearchQuery { + categories: Some(Vec::new()), + sort: Some(SortType::RecentlyUpdated), + query: query, + limit: Some(25), + offset: Some(0), + } +} + +fn parse_plugin<'a>(plugin: &'a JsonValue) -> Plugin<'a> { + Plugin { + plugin_id: plugin["pluginId"].as_str().unwrap(), + created_at: parse_date(&plugin["createdAt"].as_str().unwrap()), + name: plugin["name"].as_str().unwrap(), + owner: plugin["owner"].as_str().unwrap(), + description: plugin["description"].as_str().unwrap(), + href: plugin["href"].as_str().unwrap(), + members: plugin["members"].members().into_iter().map(parse_user).fold(Vec::new(), |mut v, f| {v.push(f); v}), + channels: plugin["channels"].members().into_iter().map(parse_channel).fold(Vec::new(), |mut v, f| {v.push(f); v}), + recommended: parse_version(&plugin["recommended"]), + category: PluginCategory::from_str(plugin["category"]["title"].as_str().unwrap()), + views: plugin["views"].as_u32().unwrap(), + downloads: plugin["downloads"].as_u32().unwrap(), + stars: plugin["stars"].as_u32().unwrap(), + } +} + +fn parse_date(s: &str) -> DateTime { + unimplemented!() +} + +fn parse_user<'a>(user: &'a JsonValue) -> User<'a> { + User { + user_id: user["userId"].as_u32().unwrap(), + name: user["name"].as_str().unwrap(), + roles: user["roles"].members().into_iter().map(|role| role.as_str().unwrap()).fold(Vec::new(), |mut v, f| {v.push(f); v}), + head_role: user["headRole"].as_str().unwrap(), + } +} + +fn parse_channel<'a>(channel: &'a JsonValue) -> Channel<'a> { + Channel { + name: channel["name"].as_str().unwrap(), + color: channel["color"].as_str().unwrap(), + } +} + +fn parse_version<'a>(version: &'a JsonValue) -> Version<'a> { + Version { + id: version["id"].as_u32().unwrap(), + created_at: parse_date(version["createdAt"].as_str().unwrap()), + name: version["name"].as_str().unwrap(), + dependencies: version["dependencies"].members().into_iter().map(parse_dependency).fold(Vec::new(), |mut v, f| {v.push(f); v}), + plugin_id: version["pluginId"].as_str().unwrap(), + channel: parse_channel(&version["channel"]), + file_size: version["fileSize"].as_u32().unwrap(), + } +} + +fn parse_dependency<'a>(dependency: &'a JsonValue) -> Dependency<'a> { + Dependency { + plugin_id: dependency["pluginId"].as_str().unwrap(), + version: dependency["version"].as_str().unwrap(), + } +} + +#[derive(Clone, Debug)] +pub struct Plugin<'a> { + pub plugin_id: &'a str, + pub created_at: DateTime, + pub name: &'a str, + pub owner: &'a str, + pub description: &'a str, + pub href: &'a str, + pub members: Vec>, + pub channels: Vec>, + pub recommended: Version<'a>, + pub category: PluginCategory, + pub views: u32, + pub downloads: u32, + pub stars: u32, +} + +#[derive(Clone, Debug)] +pub struct User<'a> { + pub user_id: u32, + pub name: &'a str, + pub roles: Vec<&'a str>, + pub head_role: &'a str, +} + +#[derive(Clone, Copy, Debug)] +pub struct Channel<'a> { + pub name: &'a str, + pub color: &'a str, +} + +#[derive(Clone, Debug)] +pub struct Version<'a> { + pub id: u32, + pub created_at: DateTime, + pub name: &'a str, + pub dependencies: Vec>, + pub plugin_id: &'a str, + pub channel: Channel<'a>, + pub file_size: u32, +} + +#[derive(Clone, Debug)] +pub struct Dependency<'a> { + pub plugin_id: &'a str, + pub version: &'a str, +} \ No newline at end of file