<commit_before>// TODO
// impl ToGeojson for ....
// generic number instead of f64 for position?
// rename Position to Pos or Positions

extern crate serialize;

use std::collections::TreeMap;
use serialize::json::ToJson;
use serialize::json;

/// Position
///
/// [GeoJSON Format Specification § 2.1.1](http://geojson.org/geojson-spec.html#positions)
pub struct Position(pub Vec<f64>);

impl ToJson for Position {
    fn to_json(&self) -> json::Json {
        let &Position(ref nums) = self;
        nums.to_json()
    }
}

impl Clone for Position {
    fn clone(&self) -> Position {
        let &Position(ref nums) = self;
        Position(nums.clone())
    }
}

/// Point
///
/// [GeoJSON Format Specification § 2.1.2](http://geojson.org/geojson-spec.html#point)
pub struct Point {
    pub coordinates: Position,
}

impl ToJson for Point {
    fn to_json(&self) -> json::Json {
        let mut d = TreeMap::new();
        d.insert("type".to_string(), "Point".to_string().to_json());
        d.insert("coordinates".to_string(), self.coordinates.to_json());
        d.to_json()
    }
}

/// MultiPoint
///
/// [GeoJSON Format Specification § 2.1.3](http://geojson.org/geojson-spec.html#multipoint)
pub struct MultiPoint {
    pub points: Vec<Point>,
}

impl ToJson for MultiPoint {
    fn to_json(&self) -> json::Json {
        let coordinates: Vec<Position> =
            self.points.iter().map(|p| p.coordinates.clone()).collect();
        let mut d = TreeMap::new();
        d.insert("type".to_string(), "MultiPoint".to_string().to_json());
        d.insert("coordinates".to_string(), coordinates.to_json());
        d.to_json()
    }
}

/// LineString
///
/// [GeoJSON Format Specification § 2.1.4](http://geojson.org/geojson-spec.html#linestring)
pub struct LineString {
    pub coordinates: Vec<Position>,
}

impl ToJson for LineString {
    fn to_json(&self) -> json::Json {
        let mut d = TreeMap::new();
        d.insert("type".to_string(), "LineString".to_string().to_json());
        d.insert("coordinates".to_string(), self.coordinates.to_json());
        d.to_json()
    }
}

/// MultiLineString
///
/// [GeoJSON Format Specification § 2.1.5](http://geojson.org/geojson-spec.html#multilinestring)
pub struct MultiLineString {
    pub line_strings: Vec<LineString>,
}

impl ToJson for MultiLineString {
    fn to_json(&self) -> json::Json {
        let coordinates: Vec<Vec<Position>> =
            self.line_strings.iter().map(|l| l.coordinates.clone()).collect();
        let mut d = TreeMap::new();
        d.insert("type".to_string(), "MultiLineString".to_string().to_json());
        d.insert("coordinates".to_string(), coordinates.to_json());
        d.to_json()
    }
}

/// Polygon
///
/// [GeoJSON Format Specification § 2.1.6](http://geojson.org/geojson-spec.html#polygon)
pub struct Polygon {
    pub exterior: Vec<Position>,
    pub holes: Vec<Vec<Position>>,  // TODO: this should be optional
}

impl Polygon {
    fn coordinates(&self) -> Vec<Vec<Position>> {
        let mut coordinates = self.holes.clone();
        coordinates.insert(0, self.exterior.clone());
        coordinates
    }
}

impl ToJson for Polygon {
    fn to_json(&self) -> json::Json {
        let mut d = TreeMap::new();
        d.insert("type".to_string(), "Polygon".to_string().to_json());
        d.insert("coordinates".to_string(), self.coordinates().to_json());
        d.to_json()
    }
}

/// MultiPolygon
///
/// [GeoJSON Format Specification § 2.1.7](http://geojson.org/geojson-spec.html#multipolygon)
pub struct MultiPolygon {
    pub polygons: Vec<Polygon>,
}

impl ToJson for MultiPolygon {
    fn to_json(&self) -> json::Json {
        let coordinates: Vec<Vec<Vec<Position>>> =
            self.polygons.iter().map(|p| p.coordinates()).collect();
        let mut d = TreeMap::new();
        d.insert("type".to_string(), "MultiPolygon".to_string().to_json());
        d.insert("coordinates".to_string(), coordinates.to_json());
        d.to_json()
    }
}

/// Geometry
pub enum Geometry {
    Point(Point),
    MultiPoint(MultiPoint),
    LineString(LineString),
    MultiLineString(MultiLineString),
    Polygon(Polygon),
    MultiPolygon(MultiPolygon),
}

impl ToJson for Geometry {
    fn to_json(&self) -> json::Json {
        match *self {
            // TODO: is there a better way of doing this?
            Point(ref geom) => geom.to_json(),
            MultiPoint(ref geom) => geom.to_json(),
            LineString(ref geom) => geom.to_json(),
            MultiLineString(ref geom) => geom.to_json(),
            Polygon(ref geom) => geom.to_json(),
            MultiPolygon(ref geom) => geom.to_json(),
            // TODO: GeometryCollection(ref geom) => geom.to_json(),
        }
    }
}

/// GeometryCollection
///
/// [GeoJSON Format Specification § 2.1.8](http://geojson.org/geojson-spec.html#geometry-collection)
pub struct GeometryCollection {
    geometries: Vec<Geometry>,
}

impl ToJson for GeometryCollection {
    fn to_json(&self) -> json::Json {
        let mut d = TreeMap::new();
        d.insert("type".to_string(), "GeometryCollection".to_string().to_json());
        d.insert("geometries".to_string(), self.geometries.to_json());
        d.to_json()
    }
}

/// Feature
///
/// [GeoJSON Format Specification § 2.2](http://geojson.org/geojson-spec.html#feature-objects)
pub struct Feature {
    geometry: Geometry,
    properties: json::Json,
}

impl ToJson for Feature {
    fn to_json(&self) -> json::Json {
        let mut d = TreeMap::new();
        d.insert("type".to_string(), "Feature".to_string().to_json());
        d.insert("geometry".to_string(), self.geometry.to_json());
        d.insert("properties".to_string(), self.properties.to_json());
        d.to_json()
    }
}

/// FeatureCollection
///
/// [GeoJSON Format Specification § 2.3](http://geojson.org/geojson-spec.html#feature-collection-objects)
pub struct FeatureCollection {
    features: Vec<Feature>,
}

impl ToJson for FeatureCollection {
    fn to_json(&self) -> json::Json {
        let mut d = TreeMap::new();
        d.insert("features".to_string(), self.features.to_json());
        d.to_json()
    }
}

fn main() {
    let point = Point {
        coordinates: Position(vec![1., 2., 3.]),
    };

    let j: json::Json = point.to_json();
    let s: String = j.to_pretty_str();

    println!("{}", s);
}
<commit_msg>Allow GeometryCollections to contain themselves<commit_after>// TODO
// impl ToGeojson for ....
// generic number instead of f64 for position?
// rename Position to Pos or Positions

extern crate serialize;

use std::collections::TreeMap;
use serialize::json::ToJson;
use serialize::json;

/// Position
///
/// [GeoJSON Format Specification § 2.1.1](http://geojson.org/geojson-spec.html#positions)
pub struct Position(pub Vec<f64>);

impl ToJson for Position {
    fn to_json(&self) -> json::Json {
        let &Position(ref nums) = self;
        nums.to_json()
    }
}

impl Clone for Position {
    fn clone(&self) -> Position {
        let &Position(ref nums) = self;
        Position(nums.clone())
    }
}

/// Point
///
/// [GeoJSON Format Specification § 2.1.2](http://geojson.org/geojson-spec.html#point)
pub struct Point {
    pub coordinates: Position,
}

impl ToJson for Point {
    fn to_json(&self) -> json::Json {
        let mut d = TreeMap::new();
        d.insert("type".to_string(), "Point".to_string().to_json());
        d.insert("coordinates".to_string(), self.coordinates.to_json());
        d.to_json()
    }
}

/// MultiPoint
///
/// [GeoJSON Format Specification § 2.1.3](http://geojson.org/geojson-spec.html#multipoint)
pub struct MultiPoint {
    pub points: Vec<Point>,
}

impl ToJson for MultiPoint {
    fn to_json(&self) -> json::Json {
        let coordinates: Vec<Position> =
            self.points.iter().map(|p| p.coordinates.clone()).collect();
        let mut d = TreeMap::new();
        d.insert("type".to_string(), "MultiPoint".to_string().to_json());
        d.insert("coordinates".to_string(), coordinates.to_json());
        d.to_json()
    }
}

/// LineString
///
/// [GeoJSON Format Specification § 2.1.4](http://geojson.org/geojson-spec.html#linestring)
pub struct LineString {
    pub coordinates: Vec<Position>,
}

impl ToJson for LineString {
    fn to_json(&self) -> json::Json {
        let mut d = TreeMap::new();
        d.insert("type".to_string(), "LineString".to_string().to_json());
        d.insert("coordinates".to_string(), self.coordinates.to_json());
        d.to_json()
    }
}

/// MultiLineString
///
/// [GeoJSON Format Specification § 2.1.5](http://geojson.org/geojson-spec.html#multilinestring)
pub struct MultiLineString {
    pub line_strings: Vec<LineString>,
}

impl ToJson for MultiLineString {
    fn to_json(&self) -> json::Json {
        let coordinates: Vec<Vec<Position>> =
            self.line_strings.iter().map(|l| l.coordinates.clone()).collect();
        let mut d = TreeMap::new();
        d.insert("type".to_string(), "MultiLineString".to_string().to_json());
        d.insert("coordinates".to_string(), coordinates.to_json());
        d.to_json()
    }
}

/// Polygon
///
/// [GeoJSON Format Specification § 2.1.6](http://geojson.org/geojson-spec.html#polygon)
pub struct Polygon {
    pub exterior: Vec<Position>,
    pub holes: Vec<Vec<Position>>,  // TODO: this should be optional
}

impl Polygon {
    fn coordinates(&self) -> Vec<Vec<Position>> {
        let mut coordinates = self.holes.clone();
        coordinates.insert(0, self.exterior.clone());
        coordinates
    }
}

impl ToJson for Polygon {
    fn to_json(&self) -> json::Json {
        let mut d = TreeMap::new();
        d.insert("type".to_string(), "Polygon".to_string().to_json());
        d.insert("coordinates".to_string(), self.coordinates().to_json());
        d.to_json()
    }
}

/// MultiPolygon
///
/// [GeoJSON Format Specification § 2.1.7](http://geojson.org/geojson-spec.html#multipolygon)
pub struct MultiPolygon {
    pub polygons: Vec<Polygon>,
}

impl ToJson for MultiPolygon {
    fn to_json(&self) -> json::Json {
        let coordinates: Vec<Vec<Vec<Position>>> =
            self.polygons.iter().map(|p| p.coordinates()).collect();
        let mut d = TreeMap::new();
        d.insert("type".to_string(), "MultiPolygon".to_string().to_json());
        d.insert("coordinates".to_string(), coordinates.to_json());
        d.to_json()
    }
}

/// Geometry
pub enum Geometry {
    Point(Point),
    MultiPoint(MultiPoint),
    LineString(LineString),
    MultiLineString(MultiLineString),
    Polygon(Polygon),
    MultiPolygon(MultiPolygon),
    GeometryCollection(GeometryCollection),
}

impl ToJson for Geometry {
    fn to_json(&self) -> json::Json {
        match *self {
            // TODO: is there a better way of doing this?
            Point(ref geom) => geom.to_json(),
            MultiPoint(ref geom) => geom.to_json(),
            LineString(ref geom) => geom.to_json(),
            MultiLineString(ref geom) => geom.to_json(),
            Polygon(ref geom) => geom.to_json(),
            MultiPolygon(ref geom) => geom.to_json(),
            GeometryCollection(ref geom) => geom.to_json(),
        }
    }
}

/// GeometryCollection
///
/// [GeoJSON Format Specification § 2.1.8](http://geojson.org/geojson-spec.html#geometry-collection)
pub struct GeometryCollection {
    geometries: Vec<Geometry>,
}

impl ToJson for GeometryCollection {
    fn to_json(&self) -> json::Json {
        let mut d = TreeMap::new();
        d.insert("type".to_string(), "GeometryCollection".to_string().to_json());
        d.insert("geometries".to_string(), self.geometries.to_json());
        d.to_json()
    }
}

/// Feature
///
/// [GeoJSON Format Specification § 2.2](http://geojson.org/geojson-spec.html#feature-objects)
pub struct Feature {
    geometry: Geometry,
    properties: json::Json,
}

impl ToJson for Feature {
    fn to_json(&self) -> json::Json {
        let mut d = TreeMap::new();
        d.insert("type".to_string(), "Feature".to_string().to_json());
        d.insert("geometry".to_string(), self.geometry.to_json());
        d.insert("properties".to_string(), self.properties.to_json());
        d.to_json()
    }
}

/// FeatureCollection
///
/// [GeoJSON Format Specification § 2.3](http://geojson.org/geojson-spec.html#feature-collection-objects)
pub struct FeatureCollection {
    features: Vec<Feature>,
}

impl ToJson for FeatureCollection {
    fn to_json(&self) -> json::Json {
        let mut d = TreeMap::new();
        d.insert("features".to_string(), self.features.to_json());
        d.to_json()
    }
}

fn main() {
    let point = Point {
        coordinates: Position(vec![1., 2., 3.]),
    };

    let j: json::Json = point.to_json();
    let s: String = j.to_pretty_str();

    println!("{}", s);
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate clap;

fn main() {
    let version = crate_version!();
    let matches = clap::App::new("to")
                      .about("Bookmark directories")
                      .version(version)
                      .author("Jason Campbell <jason@artifact.sh>")
                      .get_matches();
}
<commit_msg>Adds parsing CLI operations into an aoperation struct.<commit_after>#[macro_use]
extern crate clap;

use clap::{App, Arg};
use std::path::PathBuf;

fn main() {
    // Use the clap crate to handle argument parsing.
    let version = crate_version!();
    let program = App::new("to")
                    .about("Bookmark directories")
                    .version(version)
                    .author("Jason Campbell <jason@artifact.sh>")
                    .arg(Arg::with_name("name")
                        .help("Name of the bookamrk")
                        .index(1))
                    .arg(Arg::with_name("directory")
                        .help("Path of the bookamrk")
                        .index(2));

    // Take the matches from clap and convert them into name, directory, and
    // action.
    let operation = parse(program);
}

struct Operation {
    name: String,
    directory: PathBuf,
}

fn parse(cli: clap::App) -> Operation {
    let matches = cli.get_matches();

    if let Some(name) = matches.value_of("name") {
        println!("A name was passed in: {}", name);
    }

    if let Some(directory) = matches.value_of("directory") {
        println!("A name was passed in: {}", directory);
    }

    return Operation{ name: "foo".to_string(), directory: PathBuf::from(".")}
}
<|endoftext|>


<commit_before>extern crate centralgithook;
extern crate git2;
extern crate clap;

use std::env;
use std::process::exit;
use std::path::Path;
use std::process::Command;
use centralgithook::migrate;

const GERRIT_PORT: &'static str = "29418";
const AUTOMATION_USER: &'static str = "automation";
const MODULE_PATH_PREFIX: &'static str = "bsw/modules";
const CENTRAL_NAME: &'static str = "bsw/central";

// create module project on gerrit (if not existing)
fn check_module_git(module: &str) -> Result<(), git2::Error> {
    match Command::new("git")
        .arg("-p").arg(GERRIT_PORT)
        .arg("gerrit-test-git")
        .arg("gerrit")
        .arg("create-project")
        .arg(format!("{}/{}", MODULE_PATH_PREFIX, module))
        .arg("--empty-commit")
        .output() {
            Ok(output) => {
                println!("create-project: {}", String::from_utf8_lossy(&output.stderr));
                Ok(())
            },
            Err(_) => Err(git2::Error::from_str("failed to create project")),
        }
}
fn remote_module_url(module_path: &str) -> String {
    format!("ssh://{}@gerrit-test-git:{}/{}",
            GERRIT_PORT,
            AUTOMATION_USER,
            module_path)
}

fn main() { exit(main_ret()); } fn main_ret() -> i32 {

  let args = clap::App::new("centralgithook")
    .arg(clap::Arg::with_name("oldrev").long("oldrev").takes_value(true))
    .arg(clap::Arg::with_name("newrev").long("newrev").takes_value(true))
    .arg(clap::Arg::with_name("project").long("project").takes_value(true))
    .arg(clap::Arg::with_name("refname").long("refname").takes_value(true))
    .arg(clap::Arg::with_name("uploader").long("uploader").takes_value(true))
    .arg(clap::Arg::with_name("commit").long("commit").takes_value(true))
    .arg(clap::Arg::with_name("change").long("change").takes_value(true))
    .arg(clap::Arg::with_name("change-url").long("change-url").takes_value(true))
    .arg(clap::Arg::with_name("change-owner").long("change-owner").takes_value(true))
    .arg(clap::Arg::with_name("branch").long("branch").takes_value(true))
    .arg(clap::Arg::with_name("submitter").long("submitter").takes_value(true))
    .arg(clap::Arg::with_name("topic").long("topic").takes_value(true))
    .get_matches();

  let newrev = args.value_of("newrev").unwrap_or("");
  let project = args.value_of("project").unwrap_or("");
  let refname = args.value_of("refname").unwrap_or("");
  let commit = args.value_of("commit").unwrap_or("");

  // ref-update: fired after push
  // change-merged: fired after gerrit-submit
  if let Some(hook) = env::args().nth(0) {
    let is_review = refname == "refs/for/master";
    let is_module = project != CENTRAL_NAME;
    let is_update = hook.ends_with("ref-update");
    let is_submit = hook.ends_with("change-merged");

    // // TODO
    // let uploader = args.value_of("uploader").unwrap_or("");
    // if !is_review && !uploader.contains("Automation") {
    //   println!("only push to refs/for/master");
    //   return 1;
    // }

    // FIXME why not use the port here?
    let remote_addr = &format!("ssh://{}@gerrit-test-git/{}.git",
                               AUTOMATION_USER,
                               CENTRAL_NAME);
    if is_submit {
      // submit to central
      migrate::central_submit(remote_addr,
                              commit,
                              &remote_module_url,
                              // &local_module_path,
                              &check_module_git,
                              CENTRAL_NAME,
                              &Path::new(".")).unwrap();
    }
    else if is_module && is_update && is_review {
      // module was pushed, get changes to central
      migrate::module_review_upload(project,
                                    Path::new(project),//TODO: not correct, find out when testing on gerrit
                                    newrev,
                                    CENTRAL_NAME,
                                    &format!("{}.git", &CENTRAL_NAME)).unwrap();
      // stop gerrit from allowing push to module directly
      return 1;
    }
    else if !is_module && is_update && !is_review {
      // direct push to master-branch of central
      migrate::central_submit(remote_addr,
                              newrev,
                              &remote_module_url,
                              // &local_module_path,
                              &check_module_git,
                              CENTRAL_NAME,
                              &Path::new(".")).unwrap();
    }
  }
  return 0;
}

<commit_msg>fix ssh call and module url<commit_after>extern crate centralgithook;
extern crate git2;
extern crate clap;

use std::env;
use std::process::exit;
use std::path::Path;
use std::process::Command;
use centralgithook::migrate;

const GERRIT_PORT: &'static str = "29418";
const AUTOMATION_USER: &'static str = "automation";
const MODULE_PATH_PREFIX: &'static str = "bsw/modules";
const CENTRAL_NAME: &'static str = "bsw/central";

// create module project on gerrit (if not existing)
fn check_module_git(module: &str) -> Result<(), git2::Error> {
    match Command::new("ssh")
        .arg("-p").arg(GERRIT_PORT)
        .arg("gerrit-test-git")
        .arg("gerrit")
        .arg("create-project")
        .arg(format!("{}/{}", MODULE_PATH_PREFIX, module))
        .arg("--empty-commit")
        .output() {
            Ok(output) => {
                println!("create-project: {}", String::from_utf8_lossy(&output.stderr));
                Ok(())
            },
            Err(_) => Err(git2::Error::from_str("failed to create project")),
        }
}
fn remote_module_url(module_path: &str) -> String {
    format!("ssh://{}@gerrit-test-git:{}/{}",
            AUTOMATION_USER,
            GERRIT_PORT,
            module_path)
}

fn main() { exit(main_ret()); } fn main_ret() -> i32 {

  let args = clap::App::new("centralgithook")
    .arg(clap::Arg::with_name("oldrev").long("oldrev").takes_value(true))
    .arg(clap::Arg::with_name("newrev").long("newrev").takes_value(true))
    .arg(clap::Arg::with_name("project").long("project").takes_value(true))
    .arg(clap::Arg::with_name("refname").long("refname").takes_value(true))
    .arg(clap::Arg::with_name("uploader").long("uploader").takes_value(true))
    .arg(clap::Arg::with_name("commit").long("commit").takes_value(true))
    .arg(clap::Arg::with_name("change").long("change").takes_value(true))
    .arg(clap::Arg::with_name("change-url").long("change-url").takes_value(true))
    .arg(clap::Arg::with_name("change-owner").long("change-owner").takes_value(true))
    .arg(clap::Arg::with_name("branch").long("branch").takes_value(true))
    .arg(clap::Arg::with_name("submitter").long("submitter").takes_value(true))
    .arg(clap::Arg::with_name("topic").long("topic").takes_value(true))
    .get_matches();

  let newrev = args.value_of("newrev").unwrap_or("");
  let project = args.value_of("project").unwrap_or("");
  let refname = args.value_of("refname").unwrap_or("");
  let commit = args.value_of("commit").unwrap_or("");

  // ref-update: fired after push
  // change-merged: fired after gerrit-submit
  if let Some(hook) = env::args().nth(0) {
    let is_review = refname == "refs/for/master";
    let is_module = project != CENTRAL_NAME;
    let is_update = hook.ends_with("ref-update");
    let is_submit = hook.ends_with("change-merged");

    // // TODO
    // let uploader = args.value_of("uploader").unwrap_or("");
    // if !is_review && !uploader.contains("Automation") {
    //   println!("only push to refs/for/master");
    //   return 1;
    // }

    // FIXME why not use the port here?
    let remote_addr = &format!("ssh://{}@gerrit-test-git/{}.git",
                               AUTOMATION_USER,
                               CENTRAL_NAME);
    if is_submit {
      // submit to central
      migrate::central_submit(remote_addr,
                              commit,
                              &remote_module_url,
                              // &local_module_path,
                              &check_module_git,
                              CENTRAL_NAME,
                              &Path::new(".")).unwrap();
    }
    else if is_module && is_update && is_review {
      // module was pushed, get changes to central
      migrate::module_review_upload(project,
                                    Path::new(project),//TODO: not correct, find out when testing on gerrit
                                    newrev,
                                    CENTRAL_NAME,
                                    &format!("{}.git", &CENTRAL_NAME)).unwrap();
      // stop gerrit from allowing push to module directly
      return 1;
    }
    else if !is_module && is_update && !is_review {
      // direct push to master-branch of central
      migrate::central_submit(remote_addr,
                              newrev,
                              &remote_module_url,
                              // &local_module_path,
                              &check_module_git,
                              CENTRAL_NAME,
                              &Path::new(".")).unwrap();
    }
  }
  return 0;
}

<|endoftext|>


<commit_before>#![feature(plugin, custom_derive)]
#![plugin(rocket_codegen)]

extern crate env_logger;
extern crate futures;
extern crate getopts;
extern crate hyper;
extern crate mote;
extern crate reqwest;
extern crate rgb;
extern crate rocket;
extern crate rocket_contrib;
extern crate scroll_phat_hd;
extern crate serde_json;
extern crate staticfile;
extern crate tokio_core;
extern crate unicase;
extern crate url;

#[macro_use]
extern crate log;

#[macro_use]
extern crate serde_derive;

#[macro_use]
extern crate maplit;

use std::env;
use std::sync::{Arc, Mutex};
use std::thread;
use std::time;

use getopts::Options;
use rocket::State;
use rocket::response::Response;
use rocket_contrib::Json;
use unicase::UniCase;
use rocket::http::hyper::header::{AccessControlAllowHeaders, AccessControlAllowOrigin};

mod google_actions;
use google_actions::{ActionRequest, ExecuteResponse, ExecuteResponsePayload, QueryResponse,
                     QueryResponsePayload, SyncResponse, SyncResponsePayload};

mod light;
use light::{Light, LightMode, LightStatus, LightType};

mod thermostat;
use thermostat::{TemperatureUnit, Thermostat, ThermostatMode, ThermostatStatus};

mod scene;
use scene::Scene;

mod device;
use device::Device;

mod color;

mod oauth;

const BLACK: rgb::RGB8 = rgb::RGB8 { r: 0, g: 0, b: 0 };

#[derive(Serialize, Deserialize)]
#[serde(untagged)]
enum ActionResponse {
    Sync(SyncResponse),
    Query(QueryResponse),
    Execute(ExecuteResponse),
}

struct Hub {
    devices: Vec<Arc<Mutex<Device>>>,
    proxy_urls: Vec<String>,
}

#[post("/action", format = "application/json", data = "<message>")]
fn action(message: Json<ActionRequest>, state: State<Hub>) -> Option<Json<ActionResponse>> {
    info!("action_request: {:?}", message);
    for input in message.0.inputs {
        match input.intent.as_ref() {
            "action.devices.SYNC" => {
                let mut response = SyncResponse {
                    request_id: message.0.request_id.clone(),
                    payload: SyncResponsePayload {
                        agent_user_id: "1111".to_string(),
                        devices: vec![],
                    },
                };

                for device in &state.devices {
                    let device = device.lock().unwrap();
                    response.payload.devices.push(device.sync().unwrap());
                }

                let client = reqwest::Client::new();
                for url in &state.proxy_urls {
                    debug!("proxy url: {:?}", url);
                    // XXX
                    //let mut res = client.post(url).body(body.to_string()).send().unwrap();
                    //debug!("proxy response: {:?}", res);
                    //let proxy_response = res.json::<SyncResponse>().unwrap();
                    //response
                    //.payload
                    //.devices
                    //.extend(proxy_response.payload.devices);
                }
                info!("response: {:?}", serde_json::to_string(&response));

                return Some(Json(ActionResponse::Sync(response)));
            }
            "action.devices.QUERY" => {
                let mut response = QueryResponse {
                    request_id: message.0.request_id.clone(),
                    payload: QueryResponsePayload {
                        devices: btreemap!{},
                    },
                };

                if let Some(payload) = input.payload {
                    for request_device in payload.devices {
                        for device in &state.devices {
                            let device = device.lock().unwrap();
                            if request_device.id == device.id() {
                                response
                                    .payload
                                    .devices
                                    .insert(device.id(), device.query().unwrap());
                            }
                        }
                    }
                }

                let client = reqwest::Client::new();
                for url in &state.proxy_urls {
                    debug!("proxy url: {:?}", url);
                    // XXX
                    //let mut res = client.post(url).body(body.to_string()).send().unwrap();
                    //debug!("proxy response: {:?}", res);
                    //let proxy_response = res.json::<QueryResponse>().unwrap();
                    //response
                    //.payload
                    //.devices
                    //.extend(proxy_response.payload.devices);
                }

                return Some(Json(ActionResponse::Query(response)));
            }
            "action.devices.EXECUTE" => {
                let mut response = ExecuteResponse {
                    request_id: message.0.request_id.clone(),
                    payload: ExecuteResponsePayload {
                        error_code: None,
                        debug_string: None,
                        commands: vec![],
                    },
                };

                if let Some(ref p) = input.payload {
                    for command in &p.commands {
                        debug!("command: {:?}", command);
                        for execution in &command.execution {
                            debug!("execution: {:?}", execution);
                            for request_device in &command.devices {
                                debug!("request_device: {:?}", request_device);
                                for device in &state.devices {
                                    let mut device = device.lock().unwrap();
                                    if request_device.id == device.id() {
                                        response
                                            .payload
                                            .commands
                                            .push(device.execute(&execution.params).unwrap());
                                    }
                                }
                            }
                        }
                    }
                }

                let client = reqwest::Client::new();
                for url in &state.proxy_urls {
                    debug!("proxy url: {:?}", url);
                    // XXX
                    //let mut res = client.post(url).body(body.to_string()).send().unwrap();
                    //debug!("proxy response: {:?}", res);
                    //let proxy_response = res.json::<ExecuteResponse>().unwrap();
                    //response
                    //.payload
                    //.commands
                    //.extend(proxy_response.payload.commands);
                }

                return Some(Json(ActionResponse::Execute(response)));
            }
            i => {
                debug!("unsupported intent: {:?}", i);
                return None;
            }
        }
    }

    return None;
}

fn main() {
    env_logger::init().unwrap();

    let args: Vec<String> = env::args().collect();

    let mut opts = Options::new();
    opts.optopt("", "http_port", "HTTP port to listen on", "N");
    opts.optopt("", "mote_dev", "Serial port connecting to Mote", "FILE");
    opts.optopt("", "display_i2c", "I2C port to use as display", "N");

    debug!("parsing args");
    let matches = opts.parse(&args[1..]).unwrap();
    let http_port = matches
        .opt_str("http_port")
        .unwrap_or("1234".to_string())
        .parse::<u16>()
        .unwrap();
    let mote_dev = matches
        .opt_str("mote_dev")
        .unwrap_or("/dev/ttyACM0".to_string());
    let display_i2c = matches.opt_str("display_i2c").unwrap_or("".to_string());
    debug!("args parsed");

    let bedroom_lights = Arc::new(Mutex::new(Light {
        id: "111".to_string(),
        name: "Bedroom lights".to_string(),
        status: LightStatus::default(),
        type_: LightType::Light,
        available_light_modes: vec![
            LightMode::OnOff,
            LightMode::Brightness,
            LightMode::ColorSpectrum,
        ],
        color_func: Box::new(color::SolidColor { c: BLACK }),
    }));

    let kitchen_lights = Arc::new(Mutex::new(Light {
        id: "222".to_string(),
        name: "Kitchen lights".to_string(),
        status: LightStatus::default(),
        type_: LightType::Light,
        available_light_modes: vec![
            LightMode::OnOff,
            LightMode::Brightness,
            LightMode::ColorSpectrum,
        ],
        color_func: Box::new(color::SolidColor { c: BLACK }),
    }));

    let bathroom_lights = Arc::new(Mutex::new(Light {
        id: "333".to_string(),
        name: "Bathroom lights".to_string(),
        status: LightStatus::default(),
        type_: LightType::Light,
        available_light_modes: vec![
            LightMode::OnOff,
            LightMode::Brightness,
            LightMode::ColorSpectrum,
        ],
        color_func: Box::new(color::SolidColor { c: BLACK }),
    }));

    let living_room_lights = Arc::new(Mutex::new(Light {
        id: "444".to_string(),
        name: "Living Room lights".to_string(),
        status: LightStatus::default(),
        type_: LightType::Light,
        available_light_modes: vec![
            LightMode::OnOff,
            LightMode::Brightness,
            LightMode::ColorSpectrum,
        ],
        color_func: Box::new(color::SolidColor { c: BLACK }),
    }));

    let all_lights = vec![
        bedroom_lights.clone(),
        kitchen_lights.clone(),
        bathroom_lights.clone(),
        living_room_lights.clone(),
    ];

    let party_mode = Arc::new(Mutex::new(Scene {
        id: "1001".to_string(),
        name: "Party mode".to_string(),
        reversible: true,
        lights: all_lights.clone(),
    }));

    let italian_mode = Arc::new(Mutex::new(Scene {
        id: "1002".to_string(),
        name: "Italian Mode".to_string(),
        reversible: true,
        lights: all_lights.clone(),
    }));

    let night_mode = Arc::new(Mutex::new(Scene {
        id: "1003".to_string(),
        name: "Night Mode".to_string(),
        reversible: true,
        lights: all_lights.clone(),
    }));

    let strobe_mode = Arc::new(Mutex::new(Scene {
        id: "1004".to_string(),
        name: "Strobe Mode".to_string(),
        reversible: true,
        lights: all_lights.clone(),
    }));

    let thermostat = Arc::new(Mutex::new(Thermostat {
        id: "66".to_string(),
        name: "Thermostat".to_string(),
        available_thermostat_modes: vec![ThermostatMode::Off, ThermostatMode::Heat],
        thermostat_temperature_unit: TemperatureUnit::C,
        status: ThermostatStatus {
            mode: ThermostatMode::Off,
            temperature_setpoint: 21.0,
            temperature_ambient: 20.0,
            temperature_setpoint_low: 10.0,
            temperature_setpoint_high: 30.0,
            humidity_ambient: 50.0,
        },
    }));

    let hub = Hub {
        devices: vec![
            bedroom_lights.clone(),
            kitchen_lights.clone(),
            bathroom_lights.clone(),
            living_room_lights.clone(),
            //party_mode.clone(),
            //italian_mode.clone(),
            //night_mode.clone(),
            //strobe_mode.clone(),
            //thermostat.clone(),
        ],
        proxy_urls: vec![],
    };

    thread::spawn(move || {
        let mut mote = mote::Mote::new(&mote_dev, true);

        let mut pixels = [BLACK; 16 * 4];
        let mut t = 0u64;

        fn update_lights(
            pixels: &mut [rgb::RGB8; 16 * 4],
            t: u64,
            lights: &Arc<Mutex<Light>>,
            offset: usize,
        ) {
            match lights.lock() {
                Ok(lights) => for i in 0..16 {
                    let b0 = &pixels.clone()[offset..offset + 16];
                    let b1 = lights.color_func.step(t, b0);
                    pixels[i + offset] = b1[i];
                },
                Err(err) => error!("could not lock light mutex: {:?}", err),
            }
        }

        loop {
            update_lights(&mut pixels, t, &bedroom_lights, 0);
            update_lights(&mut pixels, t, &kitchen_lights, 16);
            update_lights(&mut pixels, t, &bathroom_lights, 32);
            update_lights(&mut pixels, t, &living_room_lights, 48);
            mote.write(&pixels);

            thread::sleep(time::Duration::from_millis(10));
            t += 1;
        }
    });

    thread::spawn(move || {
        // XXX
        return;
        let mut display: Box<scroll_phat_hd::display::Display> = if display_i2c == "" {
            Box::new(scroll_phat_hd::display::UnicodeDisplay::new())
        } else {
            // TODO: Parse I2C port.
            Box::new(scroll_phat_hd::display::I2CDisplay::new(1))
        };

        let mut scroller = scroll_phat_hd::scroller::Scroller::new(&mut *display);

        loop {
            match thermostat.lock() {
                Ok(thermostat) => {
                    match thermostat.status.mode {
                        thermostat::ThermostatMode::Off => {
                            scroller.set_text("--°C");
                            scroller.show();
                        }
                        _ => {
                            scroller.set_text(&format!(
                                "{}°C",
                                thermostat.status.temperature_setpoint
                            ));
                            scroller.show();
                        }
                    };
                }
                Err(err) => {
                    error!("could not lock thermostat mutex: {:?}", err);
                }
            };

            thread::sleep(time::Duration::from_millis(100));
        }
    });

    let config = rocket::Config::build(rocket::config::Environment::Development)
        .port(http_port)
        .log_level(rocket::config::LoggingLevel::Debug)
        .unwrap();

    rocket::custom(config, true)
        .manage(hub)
        .mount(
            "/",
            routes![
                action,
                get_action_handler,
                options_action_handler,
                oauth::auth,
                oauth::token,
                oauth::login,
            ],
        )
        .launch();
}

#[get("/action")]
fn get_action_handler() -> String {
    debug!("get action");
    "get action".to_string()
}

#[options("/action")]
fn options_action_handler() -> Response<'static> {
    debug!("options action");
    Response::build()
        .header(AccessControlAllowOrigin::Any)
        .header(AccessControlAllowHeaders(vec![
            UniCase("Content-Type".to_string()),
        ]))
        .finalize()
}
<commit_msg>Address<commit_after>#![feature(plugin, custom_derive)]
#![plugin(rocket_codegen)]

extern crate env_logger;
extern crate futures;
extern crate getopts;
extern crate hyper;
extern crate mote;
extern crate reqwest;
extern crate rgb;
extern crate rocket;
extern crate rocket_contrib;
extern crate scroll_phat_hd;
extern crate serde_json;
extern crate staticfile;
extern crate tokio_core;
extern crate unicase;
extern crate url;

#[macro_use]
extern crate log;

#[macro_use]
extern crate serde_derive;

#[macro_use]
extern crate maplit;

use std::env;
use std::sync::{Arc, Mutex};
use std::thread;
use std::time;

use getopts::Options;
use rocket::State;
use rocket::response::Response;
use rocket_contrib::Json;
use unicase::UniCase;
use rocket::http::hyper::header::{AccessControlAllowHeaders, AccessControlAllowOrigin};

mod google_actions;
use google_actions::{ActionRequest, ExecuteResponse, ExecuteResponsePayload, QueryResponse,
                     QueryResponsePayload, SyncResponse, SyncResponsePayload};

mod light;
use light::{Light, LightMode, LightStatus, LightType};

mod thermostat;
use thermostat::{TemperatureUnit, Thermostat, ThermostatMode, ThermostatStatus};

mod scene;
use scene::Scene;

mod device;
use device::Device;

mod color;

mod oauth;

const BLACK: rgb::RGB8 = rgb::RGB8 { r: 0, g: 0, b: 0 };

#[derive(Serialize, Deserialize)]
#[serde(untagged)]
enum ActionResponse {
    Sync(SyncResponse),
    Query(QueryResponse),
    Execute(ExecuteResponse),
}

struct Hub {
    devices: Vec<Arc<Mutex<Device>>>,
    proxy_urls: Vec<String>,
}

#[post("/action", format = "application/json", data = "<message>")]
fn action(message: Json<ActionRequest>, state: State<Hub>) -> Option<Json<ActionResponse>> {
    info!("action_request: {:?}", message);
    for input in message.0.inputs {
        match input.intent.as_ref() {
            "action.devices.SYNC" => {
                let mut response = SyncResponse {
                    request_id: message.0.request_id.clone(),
                    payload: SyncResponsePayload {
                        agent_user_id: "1111".to_string(),
                        devices: vec![],
                    },
                };

                for device in &state.devices {
                    let device = device.lock().unwrap();
                    response.payload.devices.push(device.sync().unwrap());
                }

                let client = reqwest::Client::new();
                for url in &state.proxy_urls {
                    debug!("proxy url: {:?}", url);
                    // XXX
                    //let mut res = client.post(url).body(body.to_string()).send().unwrap();
                    //debug!("proxy response: {:?}", res);
                    //let proxy_response = res.json::<SyncResponse>().unwrap();
                    //response
                    //.payload
                    //.devices
                    //.extend(proxy_response.payload.devices);
                }
                info!("response: {:?}", serde_json::to_string(&response));

                return Some(Json(ActionResponse::Sync(response)));
            }
            "action.devices.QUERY" => {
                let mut response = QueryResponse {
                    request_id: message.0.request_id.clone(),
                    payload: QueryResponsePayload {
                        devices: btreemap!{},
                    },
                };

                if let Some(payload) = input.payload {
                    for request_device in payload.devices {
                        for device in &state.devices {
                            let device = device.lock().unwrap();
                            if request_device.id == device.id() {
                                response
                                    .payload
                                    .devices
                                    .insert(device.id(), device.query().unwrap());
                            }
                        }
                    }
                }

                let client = reqwest::Client::new();
                for url in &state.proxy_urls {
                    debug!("proxy url: {:?}", url);
                    // XXX
                    //let mut res = client.post(url).body(body.to_string()).send().unwrap();
                    //debug!("proxy response: {:?}", res);
                    //let proxy_response = res.json::<QueryResponse>().unwrap();
                    //response
                    //.payload
                    //.devices
                    //.extend(proxy_response.payload.devices);
                }

                return Some(Json(ActionResponse::Query(response)));
            }
            "action.devices.EXECUTE" => {
                let mut response = ExecuteResponse {
                    request_id: message.0.request_id.clone(),
                    payload: ExecuteResponsePayload {
                        error_code: None,
                        debug_string: None,
                        commands: vec![],
                    },
                };

                if let Some(ref p) = input.payload {
                    for command in &p.commands {
                        debug!("command: {:?}", command);
                        for execution in &command.execution {
                            debug!("execution: {:?}", execution);
                            for request_device in &command.devices {
                                debug!("request_device: {:?}", request_device);
                                for device in &state.devices {
                                    let mut device = device.lock().unwrap();
                                    if request_device.id == device.id() {
                                        response
                                            .payload
                                            .commands
                                            .push(device.execute(&execution.params).unwrap());
                                    }
                                }
                            }
                        }
                    }
                }

                let client = reqwest::Client::new();
                for url in &state.proxy_urls {
                    debug!("proxy url: {:?}", url);
                    // XXX
                    //let mut res = client.post(url).body(body.to_string()).send().unwrap();
                    //debug!("proxy response: {:?}", res);
                    //let proxy_response = res.json::<ExecuteResponse>().unwrap();
                    //response
                    //.payload
                    //.commands
                    //.extend(proxy_response.payload.commands);
                }

                return Some(Json(ActionResponse::Execute(response)));
            }
            i => {
                debug!("unsupported intent: {:?}", i);
                return None;
            }
        }
    }

    return None;
}

fn main() {
    env_logger::init().unwrap();

    let args: Vec<String> = env::args().collect();

    let mut opts = Options::new();
    opts.optopt("", "http_port", "HTTP port to listen on", "N");
    opts.optopt("", "mote_dev", "Serial port connecting to Mote", "FILE");
    opts.optopt("", "display_i2c", "I2C port to use as display", "N");

    debug!("parsing args");
    let matches = opts.parse(&args[1..]).unwrap();
    let http_port = matches
        .opt_str("http_port")
        .unwrap_or("1234".to_string())
        .parse::<u16>()
        .unwrap();
    let mote_dev = matches
        .opt_str("mote_dev")
        .unwrap_or("/dev/ttyACM0".to_string());
    let display_i2c = matches.opt_str("display_i2c").unwrap_or("".to_string());
    debug!("args parsed");

    let bedroom_lights = Arc::new(Mutex::new(Light {
        id: "111".to_string(),
        name: "Bedroom lights".to_string(),
        status: LightStatus::default(),
        type_: LightType::Light,
        available_light_modes: vec![
            LightMode::OnOff,
            LightMode::Brightness,
            LightMode::ColorSpectrum,
        ],
        color_func: Box::new(color::SolidColor { c: BLACK }),
    }));

    let kitchen_lights = Arc::new(Mutex::new(Light {
        id: "222".to_string(),
        name: "Kitchen lights".to_string(),
        status: LightStatus::default(),
        type_: LightType::Light,
        available_light_modes: vec![
            LightMode::OnOff,
            LightMode::Brightness,
            LightMode::ColorSpectrum,
        ],
        color_func: Box::new(color::SolidColor { c: BLACK }),
    }));

    let bathroom_lights = Arc::new(Mutex::new(Light {
        id: "333".to_string(),
        name: "Bathroom lights".to_string(),
        status: LightStatus::default(),
        type_: LightType::Light,
        available_light_modes: vec![
            LightMode::OnOff,
            LightMode::Brightness,
            LightMode::ColorSpectrum,
        ],
        color_func: Box::new(color::SolidColor { c: BLACK }),
    }));

    let living_room_lights = Arc::new(Mutex::new(Light {
        id: "444".to_string(),
        name: "Living Room lights".to_string(),
        status: LightStatus::default(),
        type_: LightType::Light,
        available_light_modes: vec![
            LightMode::OnOff,
            LightMode::Brightness,
            LightMode::ColorSpectrum,
        ],
        color_func: Box::new(color::SolidColor { c: BLACK }),
    }));

    let all_lights = vec![
        bedroom_lights.clone(),
        kitchen_lights.clone(),
        bathroom_lights.clone(),
        living_room_lights.clone(),
    ];

    let party_mode = Arc::new(Mutex::new(Scene {
        id: "1001".to_string(),
        name: "Party mode".to_string(),
        reversible: true,
        lights: all_lights.clone(),
    }));

    let italian_mode = Arc::new(Mutex::new(Scene {
        id: "1002".to_string(),
        name: "Italian Mode".to_string(),
        reversible: true,
        lights: all_lights.clone(),
    }));

    let night_mode = Arc::new(Mutex::new(Scene {
        id: "1003".to_string(),
        name: "Night Mode".to_string(),
        reversible: true,
        lights: all_lights.clone(),
    }));

    let strobe_mode = Arc::new(Mutex::new(Scene {
        id: "1004".to_string(),
        name: "Strobe Mode".to_string(),
        reversible: true,
        lights: all_lights.clone(),
    }));

    let thermostat = Arc::new(Mutex::new(Thermostat {
        id: "66".to_string(),
        name: "Thermostat".to_string(),
        available_thermostat_modes: vec![ThermostatMode::Off, ThermostatMode::Heat],
        thermostat_temperature_unit: TemperatureUnit::C,
        status: ThermostatStatus {
            mode: ThermostatMode::Off,
            temperature_setpoint: 21.0,
            temperature_ambient: 20.0,
            temperature_setpoint_low: 10.0,
            temperature_setpoint_high: 30.0,
            humidity_ambient: 50.0,
        },
    }));

    let hub = Hub {
        devices: vec![
            bedroom_lights.clone(),
            kitchen_lights.clone(),
            bathroom_lights.clone(),
            living_room_lights.clone(),
            //party_mode.clone(),
            //italian_mode.clone(),
            //night_mode.clone(),
            //strobe_mode.clone(),
            //thermostat.clone(),
        ],
        proxy_urls: vec![],
    };

    thread::spawn(move || {
        let mut mote = mote::Mote::new(&mote_dev, true);

        let mut pixels = [BLACK; 16 * 4];
        let mut t = 0u64;

        fn update_lights(
            pixels: &mut [rgb::RGB8; 16 * 4],
            t: u64,
            lights: &Arc<Mutex<Light>>,
            offset: usize,
        ) {
            match lights.lock() {
                Ok(lights) => for i in 0..16 {
                    let b0 = &pixels.clone()[offset..offset + 16];
                    let b1 = lights.color_func.step(t, b0);
                    pixels[i + offset] = b1[i];
                },
                Err(err) => error!("could not lock light mutex: {:?}", err),
            }
        }

        loop {
            update_lights(&mut pixels, t, &bedroom_lights, 0);
            update_lights(&mut pixels, t, &kitchen_lights, 16);
            update_lights(&mut pixels, t, &bathroom_lights, 32);
            update_lights(&mut pixels, t, &living_room_lights, 48);
            mote.write(&pixels);

            thread::sleep(time::Duration::from_millis(10));
            t += 1;
        }
    });

    thread::spawn(move || {
        // XXX
        return;
        let mut display: Box<scroll_phat_hd::display::Display> = if display_i2c == "" {
            Box::new(scroll_phat_hd::display::UnicodeDisplay::new())
        } else {
            // TODO: Parse I2C port.
            Box::new(scroll_phat_hd::display::I2CDisplay::new(1))
        };

        let mut scroller = scroll_phat_hd::scroller::Scroller::new(&mut *display);

        loop {
            match thermostat.lock() {
                Ok(thermostat) => {
                    match thermostat.status.mode {
                        thermostat::ThermostatMode::Off => {
                            scroller.set_text("--°C");
                            scroller.show();
                        }
                        _ => {
                            scroller.set_text(&format!(
                                "{}°C",
                                thermostat.status.temperature_setpoint
                            ));
                            scroller.show();
                        }
                    };
                }
                Err(err) => {
                    error!("could not lock thermostat mutex: {:?}", err);
                }
            };

            thread::sleep(time::Duration::from_millis(100));
        }
    });

    let config = rocket::Config::build(rocket::config::Environment::Development)
        .address("0.0.0.0")
        .port(http_port)
        .log_level(rocket::config::LoggingLevel::Debug)
        .unwrap();

    rocket::custom(config, true)
        .manage(hub)
        .mount(
            "/",
            routes![
                action,
                get_action_handler,
                options_action_handler,
                oauth::auth,
                oauth::token,
                oauth::login,
            ],
        )
        .launch();
}

#[get("/action")]
fn get_action_handler() -> String {
    debug!("get action");
    "get action".to_string()
}

#[options("/action")]
fn options_action_handler() -> Response<'static> {
    debug!("options action");
    Response::build()
        .header(AccessControlAllowOrigin::Any)
        .header(AccessControlAllowHeaders(vec![
            UniCase("Content-Type".to_string()),
        ]))
        .finalize()
}
<|endoftext|>


<commit_before>extern crate rsdocs2docset;
extern crate html5ever;
#[macro_use]
extern crate quick_error;
extern crate clap;
extern crate url;

use clap::{Arg, App};
use html5ever::driver::{ParseOpts, parse_document};
use html5ever::rcdom::RcDom;
use html5ever::serialize::{SerializeOpts, serialize};
use html5ever::tendril::TendrilSink;
use html5ever::tree_builder::TreeBuilderOpts;

use std::ffi::OsStr;
use std::fs::{self, DirBuilder, DirEntry, File};
use std::io::{self,Write};
use std::path::Path;
use std::string::String;

use rsdocs2docset::dom::{manipulator, parser};

type Result<T> = std::result::Result<T, RsDoc2DocsetError>;

// #### read https://thesquareplanet.com/blog/rust-tips-and-tricks/ Cloning iterators
// #### read https://thesquareplanet.com/blog/rust-tips-and-tricks/ Partial matching

quick_error! {
    #[derive(Debug)]
    pub enum RsDoc2DocsetError {
        Io(err: std::io::Error) {
            from()
                cause(err)
                description(err.description())
        }
        Utf8(err: std::string::FromUtf8Error) {
            from()
                cause(err)
                description(err.description())
        }
    }
}

fn main() {
    let args = App::new("RsDoc2Docset")
        .version("0.0.1")
        .about("A tool that converts rust docs to Dash docset files")
        .arg(Arg::with_name("indir")
             .short("i")
             .long("rsdoc")
             .value_name("INDIR")
             .help("directory that contains rustdoc files")
             .required(true)
             .takes_value(true))
        .arg(Arg::with_name("name")
             .short("n")
             .long("name")
             .value_name("NAME")
             .help("name of the docset")
             .required(true)
             .takes_value(true))
        .get_matches();

    if let Err(e) = docset_from_rs_doc_tree(Path::new(args.value_of("indir").unwrap()),
    format!("{}.docset/Contents/Resources/Documents/", &args.value_of("name").unwrap()).as_str(),
    &annotate_file) {
        println!{"error: {}", e}
    }
}

fn docset_from_rs_doc_tree(source_dir: &Path, out_dir: &str,
                           annotate_file: &Fn(&DirEntry, &str) -> Result<()>)
    -> Result<()> {
        if !source_dir.exists() {
            return Err(io::Error::new(io::ErrorKind::NotFound, format!("{} does not exist", source_dir.to_str().unwrap())).into());
        }

        if source_dir.is_dir() {
            for entry in fs::read_dir(source_dir)? {
                let entry = entry?;
                if entry.path().is_dir() {
                    try!(docset_from_rs_doc_tree(&entry.path(), &out_dir, annotate_file));
                } else {
                    try!(annotate_file(&entry, &out_dir));
                }
            }
        }
        Ok(())
    }

fn annotate_file(in_file: &DirEntry, output_prefix: &str) -> Result<()> {
    let out_dir = Path::new(output_prefix).join(in_file.path().parent().unwrap());
    let out_file = out_dir.join(in_file.file_name());

    try!(DirBuilder::new()
         .recursive(true)
         .create(out_dir));

    if in_file.path().extension() != Some(OsStr::new("html")) {
        try!(fs::copy(in_file.path(), &out_file));
        println!("{:70} | ", in_file.path().display());
        //print!(".")
    } else {
        let opts = ParseOpts {
            tree_builder: TreeBuilderOpts { drop_doctype: true, ..Default::default() },
            ..Default::default()
        };

        let mut dom = parse_document(RcDom::default(), opts)
            .from_utf8()
            .read_from( &mut File::open(&in_file.path())? )?;

        let entries = parser::find_entry_elements(&mut dom);
        for entry in entries.iter().filter_map(|x| x.as_ref()) {
            print!("{:70} | ", in_file.path().display());
            println!("{}", entry)
        }
        manipulator::add_dash_links(&mut dom, &entries);

        let mut bytes = vec![];
        try!(serialize(&mut bytes, &dom.document, SerializeOpts::default()));

        let result = String::from_utf8(bytes)?;

        File::create(&out_file)?.write_all(result.as_ref())?;

        //print!(".")
    }

    Ok(())
}
<commit_msg>Create Info.plist file<commit_after>extern crate rsdocs2docset;
extern crate html5ever;
#[macro_use]
extern crate quick_error;
extern crate clap;
extern crate url;

use clap::{Arg, App};
use html5ever::driver::{ParseOpts, parse_document};
use html5ever::rcdom::RcDom;
use html5ever::serialize::{SerializeOpts, serialize};
use html5ever::tendril::TendrilSink;
use html5ever::tree_builder::TreeBuilderOpts;

use std::ffi::OsStr;
use std::fs::{self, DirBuilder, DirEntry, File};
use std::io::{self,Write};
use std::path::Path;
use std::string::String;

use rsdocs2docset::dom::{manipulator, parser};

type Result<T> = std::result::Result<T, RsDoc2DocsetError>;

// #### read https://thesquareplanet.com/blog/rust-tips-and-tricks/ Cloning iterators
// #### read https://thesquareplanet.com/blog/rust-tips-and-tricks/ Partial matching

quick_error! {
    #[derive(Debug)]
    pub enum RsDoc2DocsetError {
        Io(err: std::io::Error) {
            from()
                cause(err)
                description(err.description())
        }
        Utf8(err: std::string::FromUtf8Error) {
            from()
                cause(err)
                description(err.description())
        }
    }
}

fn main() {
    let args = App::new("RsDoc2Docset")
        .version("0.0.1")
        .about("A tool that converts rust docs to Dash docset files")
        .arg(Arg::with_name("indir")
             .short("i")
             .long("rsdoc")
             .value_name("INDIR")
             .help("directory that contains rustdoc files")
             .required(true)
             .takes_value(true))
        .arg(Arg::with_name("name")
             .short("n")
             .long("name")
             .value_name("NAME")
             .help("name of the docset")
             .required(true)
             .takes_value(true))
        .get_matches();

    if let Err(e) = docset_from_rs_doc_tree(Path::new(args.value_of("indir").unwrap()),
    format!("{}.docset/Contents/Resources/Documents/", &args.value_of("name").unwrap()).as_str(),
	&annotate_file) {
		println!{"error: {}", e}
	}

	let info_plist = format!(r##"<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
  <dict>
    <key>CFBundleIdentifier</key>
    <string>{identifier}</string>

    <key>CFBundleName</key>
    <string>{name}</string>

    <key>DocSetPlatformFamily</key>
    <string>dashtoc2</string>

    <key>isDashDocset</key>
    <true/>
  </dict>
</plist>
"##, name = args.value_of("name").unwrap(), identifier = String::from(args.value_of("name").unwrap()).replace(" ", "-").to_lowercase());

	let path_str = format!("{}.docset/Contents/Info.plist", &args.value_of("name").unwrap());
    let plist_path = Path::new(&path_str);
	if let Err(e) = File::create(plist_path).and_then(|mut x| x.write_all(info_plist.as_ref())) {
		println!{"error: {}", e}
	}
}

fn docset_from_rs_doc_tree(source_dir: &Path, out_dir: &str,
                           annotate_file: &Fn(&DirEntry, &str) -> Result<()>)
    -> Result<()> {
        if !source_dir.exists() {
            return Err(io::Error::new(io::ErrorKind::NotFound, format!("{} does not exist", source_dir.to_str().unwrap())).into());
        }

        if source_dir.is_dir() {
            for entry in fs::read_dir(source_dir)? {
                let entry = entry?;
                if entry.path().is_dir() {
                    try!(docset_from_rs_doc_tree(&entry.path(), &out_dir, annotate_file));
                } else {
                    try!(annotate_file(&entry, &out_dir));
                }
            }
        }
        Ok(())
    }

fn annotate_file(in_file: &DirEntry, output_prefix: &str) -> Result<()> {
    let out_dir = Path::new(output_prefix).join(in_file.path().parent().unwrap());
    let out_file = out_dir.join(in_file.file_name());

    try!(DirBuilder::new()
         .recursive(true)
         .create(out_dir));

    if in_file.path().extension() != Some(OsStr::new("html")) {
        try!(fs::copy(in_file.path(), &out_file));
        println!("{:70} | ", in_file.path().display());
        //print!(".")
    } else {
        let opts = ParseOpts {
            tree_builder: TreeBuilderOpts { drop_doctype: true, ..Default::default() },
            ..Default::default()
        };

        let mut dom = parse_document(RcDom::default(), opts)
            .from_utf8()
            .read_from( &mut File::open(&in_file.path())? )?;

        let entries = parser::find_entry_elements(&mut dom);
        for entry in entries.iter().filter_map(|x| x.as_ref()) {
            print!("{:70} | ", in_file.path().display());
            println!("{}", entry)
        }
        manipulator::add_dash_links(&mut dom, &entries);

        let mut bytes = vec![];
        try!(serialize(&mut bytes, &dom.document, SerializeOpts::default()));

        let result = String::from_utf8(bytes)?;

        File::create(&out_file)?.write_all(result.as_ref())?;

        //print!(".")
    }

    Ok(())
}
<|endoftext|>


<commit_before>#![cfg_attr(test, allow(dead_code))]
#![feature(collections, env, libc, old_io, os)]
#![cfg_attr(not(windows), feature(old_path, fs, io, std_misc))]

extern crate libc;

mod args;
mod matching;
mod screen;
#[cfg(not(windows))] mod ansi;

use std::env;
use std::old_io;
use std::cmp::min;
use screen::Screen;
use screen::Key;
use screen::Key::*;
use self::SearchState::*;

fn main() {
    let args = match args::parse_args() {
        Some(args) => args,
        None => {
            env::set_exit_status(1);
            return;
        },
    };

    if args.help { return; }

    let choices = read_choices();
    let initial_search = args.initial_search.clone();
    let choices = choices.iter().map(|x| &x[..]).collect::<Vec<&str>>();
    if args.use_first {
        let matches = matching::compute_matches(&choices, &initial_search);
        println!("{}", matches.get(0).unwrap_or(&""));
        return;
    } else {
        event_loop(&choices, &initial_search);
    }
}

fn event_loop(choices: &[&str], initial_search: &str) {
    let mut search = Search::new(choices, initial_search.to_string());
    let mut screen = Screen::open_screen();

    loop {
        search.recompute_matches();

        match search.state {
            InProgress => draw_screen(&mut screen, &search),
            _ => break,
        }

        let keys = screen.get_buffered_keys();
        for key in keys.iter() {
            handle_key(&mut search, key, screen.visible_choices);
        }
    }

    screen.move_cursor_to_bottom();
    println!("{}", search.get_selection());
}

fn handle_key(search: &mut Search, key: &Key, visible_choices: u16) {
    match *key {
        Char(x) => search.append(x),
        Backspace => search.backspace(),
        Control('h') => search.backspace(),
        Control('w') => search.delete_word(),
        Control('u') => search.clear_query(),
        Control('c') => search.cancel(),
        Control('g') => search.cancel(),
        Control('n') => search.down(visible_choices),
        Control('p') => search.up(visible_choices),
        Enter => search.done(),
        _ => {}
    }
}

struct Search<'a> {
    choices: &'a [&'a str],
    query: String,
    matches: Vec<&'a str>,
    stale: bool,
    index: usize,
    state: SearchState,
}

#[derive(PartialEq, Eq)]
enum SearchState {
    InProgress,
    Done,
    Canceled,
}

impl<'a> Search<'a> {
    fn new(choices: &'a [&'a str], initial_search: String) -> Search<'a> {
        let matches = choices.to_vec();
        Search {
            choices: choices,
            query: initial_search,
            matches: matches,
            stale: true,
            index: 0,
            state: InProgress,
        }
    }

    fn up(&mut self, visible_choices: u16) {
        let limit = min(visible_choices as usize - 1, self.matches.len() - 1);
        self.index = if self.index == 0 { limit } else { self.index - 1 };
    }

    fn down(&mut self, visible_choices: u16) {
        let limit = min(visible_choices as usize - 1, self.matches.len() - 1);
        self.index = if self.index == limit { 0 } else { self.index + 1 }
    }

    fn backspace(&mut self) {
        self.query.pop();
        self.stale = true;
        self.index = 0;
        self.matches = self.choices.to_vec();
    }

    fn delete_word(&mut self) {
        self.stale = true;
        delete_last_word(&mut self.query);
        self.matches = self.choices.to_vec();
    }

    fn append(&mut self, c: char) {
        self.query.push(c);
        self.stale = true;
        self.index = 0;
    }

    fn clear_query(&mut self) {
        self.query.clear();
        self.matches = self.choices.to_vec();
    }

    fn recompute_matches(&mut self) {
        if self.stale {
            self.matches = matching::compute_matches(&self.matches, &self.query);
            self.stale = false;
        }
    }

    fn get_selection(&mut self) -> String {
        if self.state == Canceled {
            "".to_string()
        } else {
            self.recompute_matches();
            self.matches.get(self.index).unwrap_or(&"").to_string()
        }
    }

    fn cancel(&mut self) {
        self.state = Canceled
    }

    fn done(&mut self) {
        self.state = Done
    }
}

fn draw_screen(screen: &mut Screen, search: &Search) {
    screen.hide_cursor();
    screen.blank_screen();
    screen.write(&format!("> {} ({}/{} choices)\n", search.query, search.matches.len(), search.choices.len()));

    print_matches(screen, &search.matches, &search.query, search.index);

    let start = screen.start_line;
    screen.move_cursor(start, 2 + search.query.len() as u16);
    screen.show_cursor();
}

fn print_matches(screen: &mut Screen, matches: &[&str], query: &str, index: usize) {
    let mut i = 1;
    for choice in matches.iter() {
        let indices = matching::visual_score(choice, query);
        print_match(choice, &indices, &mut |s, highlight| {
            if i == index + 1 {
                if highlight { screen.write_red_inverted(s); } else { screen.write_inverted(s); }
            } else {
                if highlight { screen.write_red(s); } else { screen.write(s); }
            }
        });
        if i >= screen.visible_choices as usize {
            return;
        } else {
            screen.write("\n");
        }
        i += 1;
    }
}

fn print_match(choice: &str, indices: &[usize], writer: &mut FnMut(&str, bool)) {
    let mut last_idx = 0;
    for &idx in indices {
        writer(&choice[last_idx..idx], false);
        writer(&choice[idx..idx + 1], true);
        last_idx = idx + 1;
    }
    writer(&choice[last_idx..choice.len()], false);
}

fn read_choices() -> Vec<String> {
    let mut stdin = old_io::stdio::stdin();
    let mut lines = Vec::new();

    while let Ok(mut s) = stdin.read_line() {
        trim(&mut s);
        lines.push(s);
    }

    lines
}

pub fn trim(s: &mut String) {
    while let Some(x) = s.pop() {
        if x != '\n' {
            s.push(x);
            return;
        }
    }
}

fn delete_last_word(s: &mut String) {
    let mut deleted_something = false;
    while let Some(x) = s.pop() {
        if x == ' ' {
            if deleted_something {
                s.push(x);
                return;
            }
        } else {
            deleted_something = true;
        }
    }
}

#[test]
fn trim_test() {
    fn should_become(before: &str, after: &str) {
        let mut x = before.to_string();
        trim(&mut x);
        assert_eq!(after.to_string(), x);
    }
    should_become("", "");
    should_become("\n", "");
    should_become("\n\n", "");
    should_become("asdf", "asdf");
    should_become("asdf\n", "asdf");
    should_become("asdf\nasdf\n", "asdf\nasdf");
}

#[test]
fn delete_word_test() {
    fn should_become(before: &str, after: &str) {
        let mut x = before.to_string();
        delete_last_word(&mut x);
        assert_eq!(after.to_string(), x);
    }
    should_become("", "");
    should_become("a", "");
    should_become("asdf", "");
    should_become("asdf asdf asdf", "asdf asdf ");
    should_become("asdf asdf asdf ", "asdf asdf ");
    should_become("asdf asdf asdf  ", "asdf asdf ");
}
<commit_msg>Fix char slicing in print_match<commit_after>#![cfg_attr(test, allow(dead_code))]
#![feature(collections, env, libc, old_io, os)]
#![cfg_attr(not(windows), feature(old_path, fs, io, std_misc))]

extern crate libc;

mod args;
mod matching;
mod screen;
#[cfg(not(windows))] mod ansi;

use std::env;
use std::old_io;
use std::cmp::min;
use screen::Screen;
use screen::Key;
use screen::Key::*;
use self::SearchState::*;

fn main() {
    let args = match args::parse_args() {
        Some(args) => args,
        None => {
            env::set_exit_status(1);
            return;
        },
    };

    if args.help { return; }

    let choices = read_choices();
    let initial_search = args.initial_search.clone();
    let choices = choices.iter().map(|x| &x[..]).collect::<Vec<&str>>();
    if args.use_first {
        let matches = matching::compute_matches(&choices, &initial_search);
        println!("{}", matches.get(0).unwrap_or(&""));
        return;
    } else {
        event_loop(&choices, &initial_search);
    }
}

fn event_loop(choices: &[&str], initial_search: &str) {
    let mut search = Search::new(choices, initial_search.to_string());
    let mut screen = Screen::open_screen();

    loop {
        search.recompute_matches();

        match search.state {
            InProgress => draw_screen(&mut screen, &search),
            _ => break,
        }

        let keys = screen.get_buffered_keys();
        for key in keys.iter() {
            handle_key(&mut search, key, screen.visible_choices);
        }
    }

    screen.move_cursor_to_bottom();
    println!("{}", search.get_selection());
}

fn handle_key(search: &mut Search, key: &Key, visible_choices: u16) {
    match *key {
        Char(x) => search.append(x),
        Backspace => search.backspace(),
        Control('h') => search.backspace(),
        Control('w') => search.delete_word(),
        Control('u') => search.clear_query(),
        Control('c') => search.cancel(),
        Control('g') => search.cancel(),
        Control('n') => search.down(visible_choices),
        Control('p') => search.up(visible_choices),
        Enter => search.done(),
        _ => {}
    }
}

struct Search<'a> {
    choices: &'a [&'a str],
    query: String,
    matches: Vec<&'a str>,
    stale: bool,
    index: usize,
    state: SearchState,
}

#[derive(PartialEq, Eq)]
enum SearchState {
    InProgress,
    Done,
    Canceled,
}

impl<'a> Search<'a> {
    fn new(choices: &'a [&'a str], initial_search: String) -> Search<'a> {
        let matches = choices.to_vec();
        Search {
            choices: choices,
            query: initial_search,
            matches: matches,
            stale: true,
            index: 0,
            state: InProgress,
        }
    }

    fn up(&mut self, visible_choices: u16) {
        let limit = min(visible_choices as usize - 1, self.matches.len() - 1);
        self.index = if self.index == 0 { limit } else { self.index - 1 };
    }

    fn down(&mut self, visible_choices: u16) {
        let limit = min(visible_choices as usize - 1, self.matches.len() - 1);
        self.index = if self.index == limit { 0 } else { self.index + 1 }
    }

    fn backspace(&mut self) {
        self.query.pop();
        self.stale = true;
        self.index = 0;
        self.matches = self.choices.to_vec();
    }

    fn delete_word(&mut self) {
        self.stale = true;
        delete_last_word(&mut self.query);
        self.matches = self.choices.to_vec();
    }

    fn append(&mut self, c: char) {
        self.query.push(c);
        self.stale = true;
        self.index = 0;
    }

    fn clear_query(&mut self) {
        self.query.clear();
        self.matches = self.choices.to_vec();
    }

    fn recompute_matches(&mut self) {
        if self.stale {
            self.matches = matching::compute_matches(&self.matches, &self.query);
            self.stale = false;
        }
    }

    fn get_selection(&mut self) -> String {
        if self.state == Canceled {
            "".to_string()
        } else {
            self.recompute_matches();
            self.matches.get(self.index).unwrap_or(&"").to_string()
        }
    }

    fn cancel(&mut self) {
        self.state = Canceled
    }

    fn done(&mut self) {
        self.state = Done
    }
}

fn draw_screen(screen: &mut Screen, search: &Search) {
    screen.hide_cursor();
    screen.blank_screen();
    screen.write(&format!("> {} ({}/{} choices)\n", search.query, search.matches.len(), search.choices.len()));

    print_matches(screen, &search.matches, &search.query, search.index);

    let start = screen.start_line;
    screen.move_cursor(start, 2 + search.query.len() as u16);
    screen.show_cursor();
}

fn print_matches(screen: &mut Screen, matches: &[&str], query: &str, index: usize) {
    let mut i = 1;
    for choice in matches.iter() {
        let indices = matching::visual_score(choice, query);
        print_match(choice, &indices, &mut |s, highlight| {
            if i == index + 1 {
                if highlight { screen.write_red_inverted(s); } else { screen.write_inverted(s); }
            } else {
                if highlight { screen.write_red(s); } else { screen.write(s); }
            }
        });
        if i >= screen.visible_choices as usize {
            return;
        } else {
            screen.write("\n");
        }
        i += 1;
    }
}

fn print_match(choice: &str, indices: &[usize], writer: &mut FnMut(&str, bool)) {
    let mut last_idx = 0;
    let len = choice.chars().collect::<Vec<char>>().len();
    for &idx in indices {
        writer(&choice.slice_chars(last_idx, idx), false);
        writer(&choice.slice_chars(idx, idx + 1), true);
        last_idx = idx + 1;
    }
    writer(&choice.slice_chars(last_idx, len), false);
}

fn read_choices() -> Vec<String> {
    let mut stdin = old_io::stdio::stdin();
    let mut lines = Vec::new();

    while let Ok(mut s) = stdin.read_line() {
        trim(&mut s);
        lines.push(s);
    }

    lines
}

pub fn trim(s: &mut String) {
    while let Some(x) = s.pop() {
        if x != '\n' {
            s.push(x);
            return;
        }
    }
}

fn delete_last_word(s: &mut String) {
    let mut deleted_something = false;
    while let Some(x) = s.pop() {
        if x == ' ' {
            if deleted_something {
                s.push(x);
                return;
            }
        } else {
            deleted_something = true;
        }
    }
}

#[test]
fn trim_test() {
    fn should_become(before: &str, after: &str) {
        let mut x = before.to_string();
        trim(&mut x);
        assert_eq!(after.to_string(), x);
    }
    should_become("", "");
    should_become("\n", "");
    should_become("\n\n", "");
    should_become("asdf", "asdf");
    should_become("asdf\n", "asdf");
    should_become("asdf\nasdf\n", "asdf\nasdf");
}

#[test]
fn delete_word_test() {
    fn should_become(before: &str, after: &str) {
        let mut x = before.to_string();
        delete_last_word(&mut x);
        assert_eq!(after.to_string(), x);
    }
    should_become("", "");
    should_become("a", "");
    should_become("asdf", "");
    should_become("asdf asdf asdf", "asdf asdf ");
    should_become("asdf asdf asdf ", "asdf asdf ");
    should_become("asdf asdf asdf  ", "asdf asdf ");
}
<|endoftext|>


<commit_before>#![feature(phase)]
extern crate cargo;
extern crate docopt;
#[phase(plugin)] extern crate docopt_macros;
extern crate graphviz;
extern crate serialize;

use cargo::core::resolver::Resolve;
use cargo::core::source::SourceId;
use docopt::{Error, FlagParser};
use graphviz as dot;
use std::io::File;
use std::str;

docopt!(Flags, "
Generate a graph of package dependencies in graphviz format

Usage: cargo dot [options]
       cargo dot --help

Options:
    -h, --help         Show this message
    -V, --version      Print version info and exit
    --lock-file=FILE   Specify location of input file, default \"Cargo.lock\"
    --dot-file=FILE    Specify location of output file, default \"Cargo.dot\"
")

fn main() {
    let config = docopt::Config { version: Some("0.1.0".to_string()) , ..docopt::DEFAULT_CONFIG };
    let flags: Flags = FlagParser::parse_conf(config).unwrap_or_else(|e| e.exit());
    let lock_file = unless_empty(flags.flag_lock_file, "Cargo.lock");
    let dot_file  = unless_empty(flags.flag_dot_file, "Cargo.dot");

    // TODO: figure out how to get rid of this.
    let dummy_src_id = SourceId::from_url("git+https://github.com/doopdoop/dodoododo#b3a9dee814af4846267383c800999a42b295e0d2".to_string());
    let resolved = cargo::ops::load_lockfile(&Path::new(lock_file), &dummy_src_id)
                     .unwrap_or_else(|e| exit_with(e.description().as_slice()))
                     .unwrap_or_else(||  exit_with("Lock file not found."));

    let mut graph = Graph::with_root(resolved.root().get_name());
    graph.add_dependancies(&resolved);

    let mut f = File::create(&Path::new(dot_file));

    graph.render_to(&mut f);
}

fn exit_with(s: &str) -> ! {
    fail!("Error parsing lockfile: {}", s)
}

fn unless_empty(s: String, default: &str) -> String {
    if s.is_empty() {
        default.to_string()
    } else {
        s
    }
}

pub type Nd = uint;
pub type Ed = (uint, uint);
pub struct Graph<'a> {
    nodes: Vec<&'a str>,
    edges: Vec<Ed>,
}

impl<'a> Graph<'a> {
    pub fn with_root(root: &str) -> Graph {
        Graph { nodes: vec![root], edges: vec![] }
    }

    pub fn add_dependancies(&mut self, resolved: &'a Resolve) {
        for crat in resolved.iter() {
            match resolved.deps(crat) {
                Some(mut crate_deps) => {
                    let name = crat.get_name();
                    let idl = self.find_or_add(name);
                    for dep in crate_deps {
                        let dep_name = dep.get_name();
                        let idr = self.find_or_add(dep_name);
                        self.edges.push((idl, idr));
                    };
                },
                None => { }
            }
        }
    }

    fn find_or_add(&mut self, new: &'a str) -> uint {
        for (i, s) in self.nodes.iter().enumerate() {
            if s.as_slice() == new {
                return i
            }
        }
        self.nodes.push(new);
        self.nodes.len() - 1
    }

    pub fn render_to<W:Writer>(&'a self, output: &mut W) {
        dot::render(self, output).unwrap()
    }
}

impl<'a> dot::Labeller<'a, Nd, Ed> for Graph<'a> {
    fn graph_id(&'a self) -> dot::Id<'a> {
        dot::Id::new("example3")
    }
    fn node_id(&'a self, n: &Nd) -> dot::Id {
        dot::Id::new(format!("N{:u}", *n))
    }
    fn node_label<'a>(&'a self, i: &Nd) -> dot::LabelText<'a> {
        dot::LabelStr(str::Slice(self.nodes[*i].as_slice()))
    }
}

impl<'a> dot::GraphWalk<'a, Nd, Ed> for Graph<'a> {
    fn nodes(&'a self) -> dot::Nodes<'a,Nd> {
        range(0, self.nodes.len()).collect()
    }
    fn edges(&'a self) -> dot::Edges<'a,Ed> {
        dot::maybe_owned_vec::Borrowed(self.edges.as_slice())
    }
    fn source(&self, &(s, _): &Ed) -> Nd { s }
    fn target(&self, &(_, t): &Ed) -> Nd { t }
}
<commit_msg>Print to stdout by default<commit_after>#![feature(phase)]
extern crate cargo;
extern crate docopt;
#[phase(plugin)] extern crate docopt_macros;
extern crate graphviz;
extern crate serialize;

use cargo::core::resolver::Resolve;
use cargo::core::source::SourceId;
use docopt::{Error, FlagParser};
use graphviz as dot;
use std::io::File;
use std::str;

docopt!(Flags, "
Generate a graph of package dependencies in graphviz format

Usage: cargo dot [options]
       cargo dot --help

Options:
    -h, --help         Show this message
    -V, --version      Print version info and exit
    --lock-file=FILE   Specify location of input file, default \"Cargo.lock\"
    --dot-file=FILE    Output to file, default prints to stdout
")

fn main() {
    let config = docopt::Config { version: Some("0.1.0".to_string()) , ..docopt::DEFAULT_CONFIG };
    let flags: Flags = FlagParser::parse_conf(config).unwrap_or_else(|e| e.exit());
    let lock_file  = unless_empty(flags.flag_lock_file, "Cargo.lock");
    let dot_f_flag = if flags.flag_dot_file.is_empty() { None } else { Some(flags.flag_dot_file) };

    // TODO: figure out how to get rid of this.
    let dummy_src_id = SourceId::from_url("git+https://github.com/doopdoop/dodoododo#b3a9dee814af4846267383c800999a42b295e0d2".to_string());
    let resolved = cargo::ops::load_lockfile(&Path::new(lock_file), &dummy_src_id)
                     .unwrap_or_else(|e| exit_with(e.description().as_slice()))
                     .unwrap_or_else(||  exit_with("Lock file not found."));

    let mut graph = Graph::with_root(resolved.root().get_name());
    graph.add_dependancies(&resolved);

    match dot_f_flag {
        None           => graph.render_to(&mut std::io::stdio::stdout()),
        Some(dot_file) => graph.render_to(&mut File::create(&Path::new(dot_file)))
    };

}

fn exit_with(s: &str) -> ! {
    fail!("Error parsing lockfile: {}", s)
}

fn unless_empty(s: String, default: &str) -> String {
    if s.is_empty() {
        default.to_string()
    } else {
        s
    }
}

pub type Nd = uint;
pub type Ed = (uint, uint);
pub struct Graph<'a> {
    nodes: Vec<&'a str>,
    edges: Vec<Ed>,
}

impl<'a> Graph<'a> {
    pub fn with_root(root: &str) -> Graph {
        Graph { nodes: vec![root], edges: vec![] }
    }

    pub fn add_dependancies(&mut self, resolved: &'a Resolve) {
        for crat in resolved.iter() {
            match resolved.deps(crat) {
                Some(mut crate_deps) => {
                    let name = crat.get_name();
                    let idl = self.find_or_add(name);
                    for dep in crate_deps {
                        let dep_name = dep.get_name();
                        let idr = self.find_or_add(dep_name);
                        self.edges.push((idl, idr));
                    };
                },
                None => { }
            }
        }
    }

    fn find_or_add(&mut self, new: &'a str) -> uint {
        for (i, s) in self.nodes.iter().enumerate() {
            if s.as_slice() == new {
                return i
            }
        }
        self.nodes.push(new);
        self.nodes.len() - 1
    }

    pub fn render_to<W:Writer>(&'a self, output: &mut W) {
        dot::render(self, output).unwrap()
    }
}

impl<'a> dot::Labeller<'a, Nd, Ed> for Graph<'a> {
    fn graph_id(&'a self) -> dot::Id<'a> {
        dot::Id::new("example3")
    }
    fn node_id(&'a self, n: &Nd) -> dot::Id {
        dot::Id::new(format!("N{:u}", *n))
    }
    fn node_label<'a>(&'a self, i: &Nd) -> dot::LabelText<'a> {
        dot::LabelStr(str::Slice(self.nodes[*i].as_slice()))
    }
}

impl<'a> dot::GraphWalk<'a, Nd, Ed> for Graph<'a> {
    fn nodes(&'a self) -> dot::Nodes<'a,Nd> {
        range(0, self.nodes.len()).collect()
    }
    fn edges(&'a self) -> dot::Edges<'a,Ed> {
        dot::maybe_owned_vec::Borrowed(self.edges.as_slice())
    }
    fn source(&self, &(s, _): &Ed) -> Nd { s }
    fn target(&self, &(_, t): &Ed) -> Nd { t }
}
<|endoftext|>