Last active
August 29, 2015 14:06
-
-
Save globin/fd6f8ba3f636520a5c93 to your computer and use it in GitHub Desktop.
Cloneable Trait Object
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
extern crate http; | |
extern crate url; | |
extern crate semver; | |
extern crate serialize; | |
use std::os; | |
use std::str; | |
use std::cmp::max; | |
use std::io::File; | |
use std::sync::Future; | |
use std::collections::TreeMap; | |
use semver::{Version, VersionReq}; | |
use http::client::RequestWriter; | |
use http::method::Get; | |
use serialize::json::{mod, Json}; | |
use url::Url; | |
trait Dependency : Clone { | |
fn to_check(dependency_file_contents: &str, Option<Self>) -> Vec<Box<Dependency>>; | |
fn name(&self) -> &String; | |
fn version_req(&self) -> &VersionReq; | |
fn registry_version(&self) -> Option<Version>; | |
fn clone_dep(&self) -> Box<Dependency> { | |
box self.clone() as Box<Dependency> | |
} | |
} | |
#[deriving(Show, Clone)] | |
struct ComposerDependency { | |
name: String, | |
version_req: VersionReq | |
} | |
impl ComposerDependency { | |
fn packagist_version_from_json(&self, json: &Json) -> Option<Version> { | |
json.find_path(&[&"package".to_string(), &"versions".to_string()]) | |
.and_then(|versions_json| versions_json.as_object()) | |
.and_then(|versions_map| { | |
versions_map.keys().map( | |
|version_string| Version::parse(version_string.as_slice()).ok() | |
).fold(None, |a, b| { | |
match (a, b) { | |
(None, b@_) => b, | |
(a@Some(_), None) => a, | |
(Some(a), Some(b)) => Some(max(a, b)) | |
} | |
}) | |
}) | |
} | |
fn packagist_url(&self) -> Url { | |
Url::parse( | |
format!("https://packagist.org/packages/{}.json", self.name).as_slice() | |
).unwrap() | |
} | |
} | |
impl Dependency for ComposerDependency { | |
fn to_check(composer_json_contents: &str, _: Option<ComposerDependency>) -> Vec<Box<Dependency>> { | |
let composer_json = json::from_str(composer_json_contents).unwrap(); | |
let default_map = TreeMap::new(); | |
let requires = composer_json.find(&"require".to_string()).map( | |
|r| r.as_object().unwrap() | |
).unwrap_or(&default_map); | |
let require_devs = composer_json.find(&"require-dev".to_string()).map( | |
|r| r.as_object().unwrap() | |
).unwrap_or(&default_map); | |
requires.iter().chain(require_devs.iter()).map( | |
|(k, v)| { | |
match v { | |
&json::String(ref version) => Some((k.clone(), version.clone())), | |
_ => None | |
} | |
} | |
).filter_map(|opt| match opt { | |
Some((ref name, ref version)) => { | |
match VersionReq::parse(version.as_slice()) { | |
Ok(vr) => Some(box ComposerDependency { name: name.clone(), version_req: vr } as Box<Dependency>), | |
Err(err) => { | |
println!("{} ignored (could not parse {}: {})", name, version, err); | |
None | |
} | |
} | |
}, | |
_ => None | |
}).collect() | |
} | |
fn name(&self) -> &String { | |
&self.name | |
} | |
fn version_req(&self) -> &VersionReq { | |
&self.version_req | |
} | |
fn registry_version(&self) -> Option<Version> { | |
let request: RequestWriter = RequestWriter::new(Get, self.packagist_url()).unwrap(); | |
let mut response = match request.read_response() { | |
Ok(response) => response, | |
Err((_request, error)) => fail!(":-( {}", error), | |
}; | |
let response_string = response.read_to_string().unwrap(); | |
match json::from_str(response_string.as_slice()) { | |
Ok(version_struct) => self.packagist_version_from_json(&version_struct), | |
Err(_) => None | |
} | |
} | |
} | |
fn main() { | |
let args = os::args(); | |
let file_raw_bytes = match File::open(&Path::new(args[1].as_slice())).read_to_end() { | |
Ok(bytes) => bytes, | |
Err(err) => { | |
println!("{}", err); | |
return; | |
} | |
}; | |
let dependency_file_contents = str::from_utf8(file_raw_bytes.as_slice()).unwrap(); | |
let dependencies_to_check = Dependency::to_check(dependency_file_contents, None::<ComposerDependency>); | |
let mut version_ftrs: Vec<Future<(&String, Option<Version>)>> = dependencies_to_check.iter().map(|d| { | |
let dependency = d.clone_dep(); | |
let name = d.name(); | |
Future::spawn(proc() {; | |
(name, dependency.registry_version()) | |
}) | |
}).collect(); | |
let versions: Vec<(&String, Version)> = version_ftrs.iter_mut().map( | |
|ftr| ftr.get() | |
).filter_map( | |
|tpl| match tpl { | |
(name, Some(version)) => Some((name, version)), | |
(_, None) => None | |
} | |
).collect(); | |
for dependency in dependencies_to_check.iter() { | |
for &(name, ref version) in versions.iter() { | |
if dependency.name() == name { | |
if !dependency.version_req().matches(version) { | |
println!("{}: {} doesn't match {}", dependency.name(), version, dependency.version_req()) | |
} else { | |
println!("{}: {} matches {}", dependency.name(), version, dependency.version_req()) | |
} | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment