<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use dom::bindings::codegen::BindingDeclarations::HTMLAreaElementBinding;
use dom::bindings::codegen::InheritTypes::HTMLAreaElementDerived;
use dom::bindings::js::{JSRef, Temporary};
use dom::bindings::error::ErrorResult;
use dom::document::Document;
use dom::element::HTMLAreaElementTypeId;
use dom::eventtarget::{EventTarget, NodeTargetTypeId};
use dom::htmlelement::HTMLElement;
use dom::node::{Node, ElementNodeTypeId};
use servo_util::str::DOMString;

#[deriving(Encodable)]
pub struct HTMLAreaElement {
    pub htmlelement: HTMLElement
}

impl HTMLAreaElementDerived for EventTarget {
    fn is_htmlareaelement(&self) -> bool {
        self.type_id == NodeTargetTypeId(ElementNodeTypeId(HTMLAreaElementTypeId))
    }
}

impl HTMLAreaElement {
    pub fn new_inherited(localName: DOMString, document: &JSRef<Document>) -> HTMLAreaElement {
        HTMLAreaElement {
            htmlelement: HTMLElement::new_inherited(HTMLAreaElementTypeId, localName, document)
        }
    }

    pub fn new(localName: DOMString, document: &JSRef<Document>) -> Temporary<HTMLAreaElement> {
        let element = HTMLAreaElement::new_inherited(localName, document);
        Node::reflect_node(box element, document, HTMLAreaElementBinding::Wrap)
    }
}

pub trait HTMLAreaElementMethods {
    fn Alt(&self) -> DOMString;
    fn SetAlt(&self, _alt: DOMString) -> ErrorResult;
    fn Coords(&self) -> DOMString;
    fn SetCoords(&self, _coords: DOMString) -> ErrorResult;
    fn Shape(&self) -> DOMString;
    fn SetShape(&self, _shape: DOMString) -> ErrorResult;
    fn Href(&self) -> DOMString;
    fn SetHref(&self, _href: DOMString) -> ErrorResult;
    fn Target(&self) -> DOMString;
    fn SetTarget(&self, _target: DOMString) -> ErrorResult;
    fn Download(&self) -> DOMString;
    fn SetDownload(&self, _download: DOMString) -> ErrorResult;
    fn Ping(&self) -> DOMString;
    fn SetPing(&self, _ping: DOMString) -> ErrorResult;
    fn NoHref(&self) -> bool;
    fn SetNoHref(&mut self, _no_href: bool) -> ErrorResult;
}

impl<'a> HTMLAreaElementMethods for JSRef<'a, HTMLAreaElement> {
    fn Alt(&self) -> DOMString {
        "".to_owned()
    }

    fn SetAlt(&self, _alt: DOMString) -> ErrorResult {
        Ok(())
    }

    fn Coords(&self) -> DOMString {
        "".to_owned()
    }

    fn SetCoords(&self, _coords: DOMString) -> ErrorResult {
        Ok(())
    }

    fn Shape(&self) -> DOMString {
        "".to_owned()
    }

    fn SetShape(&self, _shape: DOMString) -> ErrorResult {
        Ok(())
    }

    fn Href(&self) -> DOMString {
        "".to_owned()
    }

    fn SetHref(&self, _href: DOMString) -> ErrorResult {
        Ok(())
    }

    fn Target(&self) -> DOMString {
        "".to_owned()
    }

    fn SetTarget(&self, _target: DOMString) -> ErrorResult {
        Ok(())
    }

    fn Download(&self) -> DOMString {
        "".to_owned()
    }

    fn SetDownload(&self, _download: DOMString) -> ErrorResult {
        Ok(())
    }

    fn Ping(&self) -> DOMString {
        "".to_owned()
    }

    fn SetPing(&self, _ping: DOMString) -> ErrorResult {
        Ok(())
    }

    fn NoHref(&self) -> bool {
        false
    }

    fn SetNoHref(&mut self, _no_href: bool) -> ErrorResult {
        Ok(())
    }
}
<commit_msg>Remove needless '&mut self' from HTMLAreaElementMethods.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use dom::bindings::codegen::BindingDeclarations::HTMLAreaElementBinding;
use dom::bindings::codegen::InheritTypes::HTMLAreaElementDerived;
use dom::bindings::js::{JSRef, Temporary};
use dom::bindings::error::ErrorResult;
use dom::document::Document;
use dom::element::HTMLAreaElementTypeId;
use dom::eventtarget::{EventTarget, NodeTargetTypeId};
use dom::htmlelement::HTMLElement;
use dom::node::{Node, ElementNodeTypeId};
use servo_util::str::DOMString;

#[deriving(Encodable)]
pub struct HTMLAreaElement {
    pub htmlelement: HTMLElement
}

impl HTMLAreaElementDerived for EventTarget {
    fn is_htmlareaelement(&self) -> bool {
        self.type_id == NodeTargetTypeId(ElementNodeTypeId(HTMLAreaElementTypeId))
    }
}

impl HTMLAreaElement {
    pub fn new_inherited(localName: DOMString, document: &JSRef<Document>) -> HTMLAreaElement {
        HTMLAreaElement {
            htmlelement: HTMLElement::new_inherited(HTMLAreaElementTypeId, localName, document)
        }
    }

    pub fn new(localName: DOMString, document: &JSRef<Document>) -> Temporary<HTMLAreaElement> {
        let element = HTMLAreaElement::new_inherited(localName, document);
        Node::reflect_node(box element, document, HTMLAreaElementBinding::Wrap)
    }
}

pub trait HTMLAreaElementMethods {
    fn Alt(&self) -> DOMString;
    fn SetAlt(&self, _alt: DOMString) -> ErrorResult;
    fn Coords(&self) -> DOMString;
    fn SetCoords(&self, _coords: DOMString) -> ErrorResult;
    fn Shape(&self) -> DOMString;
    fn SetShape(&self, _shape: DOMString) -> ErrorResult;
    fn Href(&self) -> DOMString;
    fn SetHref(&self, _href: DOMString) -> ErrorResult;
    fn Target(&self) -> DOMString;
    fn SetTarget(&self, _target: DOMString) -> ErrorResult;
    fn Download(&self) -> DOMString;
    fn SetDownload(&self, _download: DOMString) -> ErrorResult;
    fn Ping(&self) -> DOMString;
    fn SetPing(&self, _ping: DOMString) -> ErrorResult;
    fn NoHref(&self) -> bool;
    fn SetNoHref(&self, _no_href: bool) -> ErrorResult;
}

impl<'a> HTMLAreaElementMethods for JSRef<'a, HTMLAreaElement> {
    fn Alt(&self) -> DOMString {
        "".to_owned()
    }

    fn SetAlt(&self, _alt: DOMString) -> ErrorResult {
        Ok(())
    }

    fn Coords(&self) -> DOMString {
        "".to_owned()
    }

    fn SetCoords(&self, _coords: DOMString) -> ErrorResult {
        Ok(())
    }

    fn Shape(&self) -> DOMString {
        "".to_owned()
    }

    fn SetShape(&self, _shape: DOMString) -> ErrorResult {
        Ok(())
    }

    fn Href(&self) -> DOMString {
        "".to_owned()
    }

    fn SetHref(&self, _href: DOMString) -> ErrorResult {
        Ok(())
    }

    fn Target(&self) -> DOMString {
        "".to_owned()
    }

    fn SetTarget(&self, _target: DOMString) -> ErrorResult {
        Ok(())
    }

    fn Download(&self) -> DOMString {
        "".to_owned()
    }

    fn SetDownload(&self, _download: DOMString) -> ErrorResult {
        Ok(())
    }

    fn Ping(&self) -> DOMString {
        "".to_owned()
    }

    fn SetPing(&self, _ping: DOMString) -> ErrorResult {
        Ok(())
    }

    fn NoHref(&self) -> bool {
        false
    }

    fn SetNoHref(&self, _no_href: bool) -> ErrorResult {
        Ok(())
    }
}
<|endoftext|>


<commit_before>//! The `pact_mock_server` crate provides the CLI for the pact mock server for mocking HTTP requests
//! and generating responses based on a pact file. It implements the V3 Pact specification
//! (https://github.com/pact-foundation/pact-specification/tree/version-3).

#![warn(missing_docs)]

#[macro_use] extern crate clap;
#[macro_use] extern crate pact_matching;
extern crate pact_mock_server;
#[macro_use] extern crate log;
#[macro_use] extern crate maplit;
extern crate simplelog;
extern crate uuid;
#[macro_use] extern crate serde_json;
extern crate hyper;
extern crate rand;
extern crate webmachine_rust;
extern crate regex;
extern crate lazy_static;

#[cfg(test)]
extern crate quickcheck;

#[cfg(test)]
#[macro_use(expect)]
extern crate expectest;

use clap::{Arg, App, SubCommand, AppSettings, ErrorKind, ArgMatches};
use std::env;
use std::str::FromStr;
use std::fs::{self, File};
use std::io;
use log::{LogLevelFilter};
use simplelog::{CombinedLogger, TermLogger, WriteLogger, SimpleLogger, Config};
use std::path::PathBuf;
use std::fs::OpenOptions;
use uuid::Uuid;
use pact_matching::models::PactSpecification;

fn display_error(error: String, matches: &ArgMatches) -> ! {
    println!("ERROR: {}", error);
    println!();
    println!("{}", matches.usage());
    panic!("{}", error)
}

mod server;
mod create_mock;
mod list;
mod verify;
mod shutdown;

fn print_version() {
    println!("\npact mock server version  : v{}", crate_version!());
    println!("pact specification version: v{}", PactSpecification::V2.version_str());
}

fn setup_log_file(output: Option<&str>) -> Result<File, io::Error> {
  let log_file = match output {
    Some(p) => {
      fs::create_dir_all(p)?;
      let mut path = PathBuf::from(p);
      path.push("pact_mock_server.log");
      path
    },
    None => PathBuf::from("pact_mock_server.log")
  };
  OpenOptions::new()
    .read(false)
    .write(true)
    .append(true)
    .create(true)
    .open(log_file)
}

fn setup_loggers(level: &str, command: &str, output: Option<&str>, no_file_log: bool, no_term_log: bool) -> Result<(), String> {
    let log_level = match level {
        "none" => LogLevelFilter::Off,
        _ => LogLevelFilter::from_str(level).unwrap()
    };

    if command == "start" {
      match (no_file_log, no_term_log) {
        (true, true) => {
          SimpleLogger::init(log_level, Config::default()).map_err(|e| format!("{:?}", e))
        },
        (true, false) => {
          TermLogger::init(log_level, Config::default()).map_err(|e| format!("{:?}", e))
        },
        (false, true) => {
          let log_file = setup_log_file(output).map_err(|e| format!("{:?}", e))?;
          WriteLogger::init(log_level, Config::default(), log_file).map_err(|e| format!("{:?}", e))
        },
        _ => {
          let log_file = setup_log_file(output).map_err(|e| format!("{:?}", e))?;
          match TermLogger::new(log_level, Config::default()) {
            Some(logger) => CombinedLogger::init(vec![logger, WriteLogger::new(log_level,
                                                                               Config::default(), log_file)]).map_err(|e| format!("{:?}", e)),
            None => WriteLogger::init(log_level, Config::default(), log_file).map_err(|e| format!("{:?}", e))
          }
        }
      }
    } else if no_term_log {
      SimpleLogger::init(log_level, Config::default()).map_err(|e| format!("{:?}", e))
    } else {
      TermLogger::init(log_level, Config::default()).map_err(|e| format!("{:?}", e))
    }
}

fn lookup_global_option<'a>(option: &str, matches: &'a ArgMatches<'a>) -> Option<&'a str> {
    let global_value = matches.value_of(option);
    let command_value = matches.subcommand().1.unwrap().value_of(option);
    match (global_value, command_value) {
        (Some(v), None) => Some(v),
        (None, Some(v)) => Some(v),
        (Some(_), Some(v)) => {
            println!("WARNING: The value for '{}' has been provided twice, defaulting to the second value '{}'",
                option, v);
            Some(v)
        },
        (None, None) => None
    }
}

fn global_option_present(option: &str, matches: &ArgMatches) -> bool {
  matches.is_present(option) || matches.subcommand().1.unwrap().is_present(option)
}

fn integer_value(v: String) -> Result<(), String> {
    v.parse::<u16>().map(|_| ()).map_err(|e| format!("'{}' is not a valid port value: {}", v, e) )
}

fn uuid_value(v: String) -> Result<(), String> {
    Uuid::parse_str(v.as_str()).map(|_| ()).map_err(|e| format!("'{}' is not a valid UUID value: {}", v, e) )
}

fn main() {
    match handle_command_args() {
        Ok(_) => (),
        Err(err) => std::process::exit(err)
    }
}

fn handle_command_args() -> Result<(), i32> {
    let args: Vec<String> = env::args().collect();
    let program = args[0].clone();

    let version = format!("v{}", crate_version!());
    let app = App::new(program)
        .version(version.as_str())
        .about("Standalone Pact mock server")
        .version_short("v")
        .setting(AppSettings::ArgRequiredElseHelp)
        .setting(AppSettings::SubcommandRequired)
        .setting(AppSettings::GlobalVersion)
        .setting(AppSettings::VersionlessSubcommands)
        .setting(AppSettings::ColoredHelp)
        .arg(Arg::with_name("port")
            .short("p")
            .long("port")
            .takes_value(true)
            .use_delimiter(false)
            .global(true)
            .help("port the master mock server runs on (defaults to 8080)"))
        .arg(Arg::with_name("host")
            .short("h")
            .long("host")
            .takes_value(true)
            .use_delimiter(false)
            .global(true)
            .help("hostname the master mock server runs on (defaults to localhost)"))
        .arg(Arg::with_name("loglevel")
            .short("l")
            .long("loglevel")
            .takes_value(true)
            .use_delimiter(false)
            .global(true)
            .possible_values(&["error", "warn", "info", "debug", "trace", "none"])
            .help("Log level for mock servers to write to the log file (defaults to info)"))
        .arg(Arg::with_name("no-term-log")
          .long("no-term-log")
          .global(true)
          .help("Use a simple logger instead of the term based one"))
        .arg(Arg::with_name("no-file-log")
          .long("no-file-log")
          .global(true)
          .help("Do not log to an output file"))
        .subcommand(SubCommand::with_name("start")
                .about("Starts the master mock server")
                .arg(Arg::with_name("output")
                      .short("o")
                      .long("output")
                      .takes_value(true)
                      .use_delimiter(false)
                      .help("the directory where to write files to (defaults to current directory)"))
                .arg(Arg::with_name("base-port")
                    .long("base-port")
                    .takes_value(true)
                    .use_delimiter(false)
                    .required(false)
                    .help("the base port number that mock server ports will be allocated from. If not specified, ports will be randomly assigned by the OS.")
                    .validator(integer_value))
                .setting(AppSettings::ColoredHelp))
        .subcommand(SubCommand::with_name("list")
                .about("Lists all the running mock servers")
                .setting(AppSettings::ColoredHelp))
        .subcommand(SubCommand::with_name("create")
                .about("Creates a new mock server from a pact file")
                .arg(Arg::with_name("file")
                    .short("f")
                    .long("file")
                    .takes_value(true)
                    .use_delimiter(false)
                    .required(true)
                    .help("the pact file to define the mock server"))
                .setting(AppSettings::ColoredHelp))
        .subcommand(SubCommand::with_name("verify")
                .about("Verify the mock server by id or port number, and generate a pact file if all ok")
                .arg(Arg::with_name("mock-server-id")
                    .short("i")
                    .long("mock-server-id")
                    .takes_value(true)
                    .use_delimiter(false)
                    .required_unless("mock-server-port")
                    .conflicts_with("mock-server-port")
                    .help("the ID of the mock server")
                    .validator(uuid_value))
                .arg(Arg::with_name("mock-server-port")
                    .short("m")
                    .long("mock-server-port")
                    .takes_value(true)
                    .use_delimiter(false)
                    .required_unless("mock-server-host")
                    .help("the port number of the mock server")
                    .validator(integer_value))
                .setting(AppSettings::ColoredHelp))
        .subcommand(SubCommand::with_name("shutdown")
                .about("Shutdown the mock server by id or port number, releasing all its resources")
                .arg(Arg::with_name("mock-server-id")
                    .short("i")
                    .long("mock-server-id")
                    .takes_value(true)
                    .use_delimiter(false)
                    .required_unless("mock-server-port")
                    .conflicts_with("mock-server-port")
                    .help("the ID of the mock server")
                    .validator(uuid_value))
                .arg(Arg::with_name("mock-server-port")
                    .short("m")
                    .long("mock-server-port")
                    .takes_value(true)
                    .use_delimiter(false)
                    .required_unless("mock-server-host")
                    .help("the port number of the mock server")
                    .validator(integer_value))
                .setting(AppSettings::ColoredHelp));

    let matches = app.get_matches_safe();
    match matches {
        Ok(ref matches) => {
            let log_level = lookup_global_option("loglevel", matches);
            if let Err(err) = setup_loggers(log_level.unwrap_or("info"),
                matches.subcommand_name().unwrap(),
                matches.subcommand().1.unwrap().value_of("output"),
                global_option_present("no-file-log", matches),
                global_option_present("no-term-log", matches)) {
                println!("WARN: Could not setup loggers: {}", err);
                println!();
            }
            let port = lookup_global_option("port", matches).unwrap_or("8080");
            let host = lookup_global_option("host", matches).unwrap_or("localhost");
            match port.parse::<u16>() {
                Ok(p) => {
                    match matches.subcommand() {
                        ("start", Some(sub_matches)) => server::start_server(p, sub_matches),
                        ("list", Some(sub_matches)) => list::list_mock_servers(host, p, sub_matches),
                        ("create", Some(sub_matches)) => create_mock::create_mock_server(host, p, sub_matches),
                        ("verify", Some(sub_matches)) => verify::verify_mock_server(host, p, sub_matches),
                        ("shutdown", Some(sub_matches)) => shutdown::shutdown_mock_server(host, p, sub_matches),
                        _ => Err(3)
                    }
                },
                Err(_) => display_error(format!("{} is not a valid port number", port), matches)
            }
        },
        Err(ref err) => {
            match err.kind {
                ErrorKind::HelpDisplayed => {
                    println!("{}", err.message);
                    Ok(())
                },
                ErrorKind::VersionDisplayed => {
                    print_version();
                    println!();
                    Ok(())
                },
                _ => {
                    println!("{}", err.message);
                    err.exit()
                }
            }
        }
    }
}

#[cfg(test)]
mod test {

    use quickcheck::{TestResult, quickcheck};
    use rand::Rng;
    use super::{integer_value, uuid_value};
    use expectest::prelude::*;

    #[test]
    fn validates_integer_value() {
        fn prop(s: String) -> TestResult {
            let mut rng = ::rand::thread_rng();
            if rng.gen() && s.chars().any(|ch| !ch.is_numeric()) {
                TestResult::discard()
            } else {
                let validation = integer_value(s.clone());
                match validation {
                    Ok(_) => TestResult::from_bool(!s.is_empty() && s.chars().all(|ch| ch.is_numeric() )),
                    Err(_) => TestResult::from_bool(s.is_empty() || s.chars().find(|ch| !ch.is_numeric() ).is_some())
                }
            }
        }
        quickcheck(prop as fn(_) -> _);

        expect!(integer_value(s!("1234"))).to(be_ok());
        expect!(integer_value(s!("1234x"))).to(be_err());
    }

    #[test]
    fn validates_uuid_value() {
        fn prop(s: String) -> TestResult {
            let mut rng = ::rand::thread_rng();
            if rng.gen() && s.chars().any(|ch| !ch.is_digit(16)) {
                TestResult::discard()
            } else {
                let validation = uuid_value(s.clone());
                match validation {
                    Ok(_) => TestResult::from_bool(!s.is_empty() && s.len() == 32 && s.chars().all(|ch| ch.is_digit(16) )),
                    Err(_) => TestResult::from_bool(s.is_empty() || s.len() != 32 || s.chars().find(|ch| !ch.is_digit(16) ).is_some())
                }
            }
        }
        quickcheck(prop as fn(_) -> _);

        expect!(uuid_value(s!("5159135ceb064af8a6baa447d81e4fbd"))).to(be_ok());
        expect!(uuid_value(s!("1234x"))).to(be_err());
    }

}
<commit_msg>fix: CLI was reporting incorrect pact specification version<commit_after>//! The `pact_mock_server` crate provides the CLI for the pact mock server for mocking HTTP requests
//! and generating responses based on a pact file. It implements the V3 Pact specification
//! (https://github.com/pact-foundation/pact-specification/tree/version-3).

#![warn(missing_docs)]

#[macro_use] extern crate clap;
#[macro_use] extern crate pact_matching;
extern crate pact_mock_server;
#[macro_use] extern crate log;
#[macro_use] extern crate maplit;
extern crate simplelog;
extern crate uuid;
#[macro_use] extern crate serde_json;
extern crate hyper;
extern crate rand;
extern crate webmachine_rust;
extern crate regex;
extern crate lazy_static;

#[cfg(test)]
extern crate quickcheck;

#[cfg(test)]
#[macro_use(expect)]
extern crate expectest;

use clap::{Arg, App, SubCommand, AppSettings, ErrorKind, ArgMatches};
use std::env;
use std::str::FromStr;
use std::fs::{self, File};
use std::io;
use log::{LogLevelFilter};
use simplelog::{CombinedLogger, TermLogger, WriteLogger, SimpleLogger, Config};
use std::path::PathBuf;
use std::fs::OpenOptions;
use uuid::Uuid;
use pact_matching::models::PactSpecification;

fn display_error(error: String, matches: &ArgMatches) -> ! {
    println!("ERROR: {}", error);
    println!();
    println!("{}", matches.usage());
    panic!("{}", error)
}

mod server;
mod create_mock;
mod list;
mod verify;
mod shutdown;

fn print_version() {
    println!("\npact mock server version  : v{}", crate_version!());
    println!("pact specification version: v{}", PactSpecification::V3.version_str());
}

fn setup_log_file(output: Option<&str>) -> Result<File, io::Error> {
  let log_file = match output {
    Some(p) => {
      fs::create_dir_all(p)?;
      let mut path = PathBuf::from(p);
      path.push("pact_mock_server.log");
      path
    },
    None => PathBuf::from("pact_mock_server.log")
  };
  OpenOptions::new()
    .read(false)
    .write(true)
    .append(true)
    .create(true)
    .open(log_file)
}

fn setup_loggers(level: &str, command: &str, output: Option<&str>, no_file_log: bool, no_term_log: bool) -> Result<(), String> {
    let log_level = match level {
        "none" => LogLevelFilter::Off,
        _ => LogLevelFilter::from_str(level).unwrap()
    };

    if command == "start" {
      match (no_file_log, no_term_log) {
        (true, true) => {
          SimpleLogger::init(log_level, Config::default()).map_err(|e| format!("{:?}", e))
        },
        (true, false) => {
          TermLogger::init(log_level, Config::default()).map_err(|e| format!("{:?}", e))
        },
        (false, true) => {
          let log_file = setup_log_file(output).map_err(|e| format!("{:?}", e))?;
          WriteLogger::init(log_level, Config::default(), log_file).map_err(|e| format!("{:?}", e))
        },
        _ => {
          let log_file = setup_log_file(output).map_err(|e| format!("{:?}", e))?;
          match TermLogger::new(log_level, Config::default()) {
            Some(logger) => CombinedLogger::init(vec![logger, WriteLogger::new(log_level,
                                                                               Config::default(), log_file)]).map_err(|e| format!("{:?}", e)),
            None => WriteLogger::init(log_level, Config::default(), log_file).map_err(|e| format!("{:?}", e))
          }
        }
      }
    } else if no_term_log {
      SimpleLogger::init(log_level, Config::default()).map_err(|e| format!("{:?}", e))
    } else {
      TermLogger::init(log_level, Config::default()).map_err(|e| format!("{:?}", e))
    }
}

fn lookup_global_option<'a>(option: &str, matches: &'a ArgMatches<'a>) -> Option<&'a str> {
    let global_value = matches.value_of(option);
    let command_value = matches.subcommand().1.unwrap().value_of(option);
    match (global_value, command_value) {
        (Some(v), None) => Some(v),
        (None, Some(v)) => Some(v),
        (Some(_), Some(v)) => {
            println!("WARNING: The value for '{}' has been provided twice, defaulting to the second value '{}'",
                option, v);
            Some(v)
        },
        (None, None) => None
    }
}

fn global_option_present(option: &str, matches: &ArgMatches) -> bool {
  matches.is_present(option) || matches.subcommand().1.unwrap().is_present(option)
}

fn integer_value(v: String) -> Result<(), String> {
    v.parse::<u16>().map(|_| ()).map_err(|e| format!("'{}' is not a valid port value: {}", v, e) )
}

fn uuid_value(v: String) -> Result<(), String> {
    Uuid::parse_str(v.as_str()).map(|_| ()).map_err(|e| format!("'{}' is not a valid UUID value: {}", v, e) )
}

fn main() {
    match handle_command_args() {
        Ok(_) => (),
        Err(err) => std::process::exit(err)
    }
}

fn handle_command_args() -> Result<(), i32> {
    let args: Vec<String> = env::args().collect();
    let program = args[0].clone();

    let version = format!("v{}", crate_version!());
    let app = App::new(program)
        .version(version.as_str())
        .about("Standalone Pact mock server")
        .version_short("v")
        .setting(AppSettings::ArgRequiredElseHelp)
        .setting(AppSettings::SubcommandRequired)
        .setting(AppSettings::GlobalVersion)
        .setting(AppSettings::VersionlessSubcommands)
        .setting(AppSettings::ColoredHelp)
        .arg(Arg::with_name("port")
            .short("p")
            .long("port")
            .takes_value(true)
            .use_delimiter(false)
            .global(true)
            .help("port the master mock server runs on (defaults to 8080)"))
        .arg(Arg::with_name("host")
            .short("h")
            .long("host")
            .takes_value(true)
            .use_delimiter(false)
            .global(true)
            .help("hostname the master mock server runs on (defaults to localhost)"))
        .arg(Arg::with_name("loglevel")
            .short("l")
            .long("loglevel")
            .takes_value(true)
            .use_delimiter(false)
            .global(true)
            .possible_values(&["error", "warn", "info", "debug", "trace", "none"])
            .help("Log level for mock servers to write to the log file (defaults to info)"))
        .arg(Arg::with_name("no-term-log")
          .long("no-term-log")
          .global(true)
          .help("Use a simple logger instead of the term based one"))
        .arg(Arg::with_name("no-file-log")
          .long("no-file-log")
          .global(true)
          .help("Do not log to an output file"))
        .subcommand(SubCommand::with_name("start")
                .about("Starts the master mock server")
                .arg(Arg::with_name("output")
                      .short("o")
                      .long("output")
                      .takes_value(true)
                      .use_delimiter(false)
                      .help("the directory where to write files to (defaults to current directory)"))
                .arg(Arg::with_name("base-port")
                    .long("base-port")
                    .takes_value(true)
                    .use_delimiter(false)
                    .required(false)
                    .help("the base port number that mock server ports will be allocated from. If not specified, ports will be randomly assigned by the OS.")
                    .validator(integer_value))
                .setting(AppSettings::ColoredHelp))
        .subcommand(SubCommand::with_name("list")
                .about("Lists all the running mock servers")
                .setting(AppSettings::ColoredHelp))
        .subcommand(SubCommand::with_name("create")
                .about("Creates a new mock server from a pact file")
                .arg(Arg::with_name("file")
                    .short("f")
                    .long("file")
                    .takes_value(true)
                    .use_delimiter(false)
                    .required(true)
                    .help("the pact file to define the mock server"))
                .setting(AppSettings::ColoredHelp))
        .subcommand(SubCommand::with_name("verify")
                .about("Verify the mock server by id or port number, and generate a pact file if all ok")
                .arg(Arg::with_name("mock-server-id")
                    .short("i")
                    .long("mock-server-id")
                    .takes_value(true)
                    .use_delimiter(false)
                    .required_unless("mock-server-port")
                    .conflicts_with("mock-server-port")
                    .help("the ID of the mock server")
                    .validator(uuid_value))
                .arg(Arg::with_name("mock-server-port")
                    .short("m")
                    .long("mock-server-port")
                    .takes_value(true)
                    .use_delimiter(false)
                    .required_unless("mock-server-host")
                    .help("the port number of the mock server")
                    .validator(integer_value))
                .setting(AppSettings::ColoredHelp))
        .subcommand(SubCommand::with_name("shutdown")
                .about("Shutdown the mock server by id or port number, releasing all its resources")
                .arg(Arg::with_name("mock-server-id")
                    .short("i")
                    .long("mock-server-id")
                    .takes_value(true)
                    .use_delimiter(false)
                    .required_unless("mock-server-port")
                    .conflicts_with("mock-server-port")
                    .help("the ID of the mock server")
                    .validator(uuid_value))
                .arg(Arg::with_name("mock-server-port")
                    .short("m")
                    .long("mock-server-port")
                    .takes_value(true)
                    .use_delimiter(false)
                    .required_unless("mock-server-host")
                    .help("the port number of the mock server")
                    .validator(integer_value))
                .setting(AppSettings::ColoredHelp));

    let matches = app.get_matches_safe();
    match matches {
        Ok(ref matches) => {
            let log_level = lookup_global_option("loglevel", matches);
            if let Err(err) = setup_loggers(log_level.unwrap_or("info"),
                matches.subcommand_name().unwrap(),
                matches.subcommand().1.unwrap().value_of("output"),
                global_option_present("no-file-log", matches),
                global_option_present("no-term-log", matches)) {
                println!("WARN: Could not setup loggers: {}", err);
                println!();
            }
            let port = lookup_global_option("port", matches).unwrap_or("8080");
            let host = lookup_global_option("host", matches).unwrap_or("localhost");
            match port.parse::<u16>() {
                Ok(p) => {
                    match matches.subcommand() {
                        ("start", Some(sub_matches)) => server::start_server(p, sub_matches),
                        ("list", Some(sub_matches)) => list::list_mock_servers(host, p, sub_matches),
                        ("create", Some(sub_matches)) => create_mock::create_mock_server(host, p, sub_matches),
                        ("verify", Some(sub_matches)) => verify::verify_mock_server(host, p, sub_matches),
                        ("shutdown", Some(sub_matches)) => shutdown::shutdown_mock_server(host, p, sub_matches),
                        _ => Err(3)
                    }
                },
                Err(_) => display_error(format!("{} is not a valid port number", port), matches)
            }
        },
        Err(ref err) => {
            match err.kind {
                ErrorKind::HelpDisplayed => {
                    println!("{}", err.message);
                    Ok(())
                },
                ErrorKind::VersionDisplayed => {
                    print_version();
                    println!();
                    Ok(())
                },
                _ => {
                    println!("{}", err.message);
                    err.exit()
                }
            }
        }
    }
}

#[cfg(test)]
mod test {

    use quickcheck::{TestResult, quickcheck};
    use rand::Rng;
    use super::{integer_value, uuid_value};
    use expectest::prelude::*;

    #[test]
    fn validates_integer_value() {
        fn prop(s: String) -> TestResult {
            let mut rng = ::rand::thread_rng();
            if rng.gen() && s.chars().any(|ch| !ch.is_numeric()) {
                TestResult::discard()
            } else {
                let validation = integer_value(s.clone());
                match validation {
                    Ok(_) => TestResult::from_bool(!s.is_empty() && s.chars().all(|ch| ch.is_numeric() )),
                    Err(_) => TestResult::from_bool(s.is_empty() || s.chars().find(|ch| !ch.is_numeric() ).is_some())
                }
            }
        }
        quickcheck(prop as fn(_) -> _);

        expect!(integer_value(s!("1234"))).to(be_ok());
        expect!(integer_value(s!("1234x"))).to(be_err());
    }

    #[test]
    fn validates_uuid_value() {
        fn prop(s: String) -> TestResult {
            let mut rng = ::rand::thread_rng();
            if rng.gen() && s.chars().any(|ch| !ch.is_digit(16)) {
                TestResult::discard()
            } else {
                let validation = uuid_value(s.clone());
                match validation {
                    Ok(_) => TestResult::from_bool(!s.is_empty() && s.len() == 32 && s.chars().all(|ch| ch.is_digit(16) )),
                    Err(_) => TestResult::from_bool(s.is_empty() || s.len() != 32 || s.chars().find(|ch| !ch.is_digit(16) ).is_some())
                }
            }
        }
        quickcheck(prop as fn(_) -> _);

        expect!(uuid_value(s!("5159135ceb064af8a6baa447d81e4fbd"))).to(be_ok());
        expect!(uuid_value(s!("1234x"))).to(be_err());
    }

}
<|endoftext|>


<commit_before>// use util::{run_example, read_url};

use std::{};
use util::{run_example};

fn t(file: &str) {
 let  a= file;
  println!("hello: {:}", a);
    run_example()

}

#[test] fn non_macro() { t("hello_world") }

<commit_msg>Delete hello_world.rs<commit_after><|endoftext|>


<commit_before>use std::thread;
use std::sync::mpsc;
use websocket;
use websocket::{Message, Sender, Receiver};
use websocket::server::sender;
use websocket::stream::WebSocketStream;
use std::io::prelude::*;
use std::fs::{OpenOptions};
use std::path::Path;
use rustc_serialize::json::{Json, ToJson};
use cbor;
use std::env;

use value::Value;
use relation::Change;
use flow::{Changes, Flow};

// The server manages a single Eve program and handles communication with the editor and clients
// TODO needs review / refactoring, especially session handling

pub struct Server {
    pub flow: Flow,
    pub senders: Vec<sender::Sender<WebSocketStream>>,
}

#[derive(Debug, Clone)]
pub struct Event {
    pub changes: Changes,
    // commands that affect the whole program state have to go through this side-channel rather than being added to a view
    pub commands: Vec<Vec<String>>,
}

pub enum ServerEvent {
    Change(Vec<u8>),
    Sync(sender::Sender<WebSocketStream>),
}

// --- json encodings ---

pub trait FromJson {
    fn from_json(json: &Json) -> Self;
}

impl ToJson for Value {
    fn to_json(&self) -> Json {
        match *self {
            Value::Null => panic!("Cannot allow the client to see nulls"),
            Value::Bool(bool) => Json::Boolean(bool),
            Value::String(ref string) => Json::String(string.clone()),
            Value::Float(float) => Json::F64(float),
            Value::Column(ref column) => Json::Array(column.iter().map(|v| v.to_json()).collect()),
        }
    }
}

impl FromJson for Value {
    fn from_json(json: &Json) -> Self {
        match *json {
            Json::Boolean(bool) => Value::Bool(bool),
            Json::String(ref string) => Value::String(string.clone()),
            Json::F64(float) => Value::Float(float),
            Json::I64(int) => Value::Float(int as f64),
            Json::U64(uint) => Value::Float(uint as f64),
            Json::Array(ref array) => Value::Column(array.iter().map(FromJson::from_json).collect()),
            _ => panic!("Cannot decode {:?} as Value", json),
        }
    }
}

impl FromJson for String {
    fn from_json(json: &Json) -> Self {
        json.as_string().unwrap().to_owned()
    }
}

impl<T: FromJson> FromJson for Vec<T> {
    fn from_json(json: &Json) -> Self {
        json.as_array().unwrap().iter().map(|t| FromJson::from_json(t)).collect()
    }
}

impl ToJson for Event {
    fn to_json(&self) -> Json {
        Json::Object(vec![
            ("changes".to_string(), Json::Array(
                self.changes.iter().map(|&(ref view_id, ref view_changes)| {
                    Json::Array(vec![
                        view_id.to_json(),
                        view_changes.fields.to_json(),
                        view_changes.insert.to_json(),
                        view_changes.remove.to_json(),
                        ])
                }).collect()
                )
            ),
            ("commands".to_string(), self.commands.to_json()),
            ("session".to_string(), "super unique session id!".to_json()), // TODO restore session handling
        ].into_iter().collect())
    }
}

impl FromJson for Event {
    fn from_json(json: &Json) -> Self {
        Event{
            changes: json.as_object().unwrap()["changes"]
            .as_array().unwrap().iter().map(|change| {
                let change = change.as_array().unwrap();
                assert_eq!(change.len(), 4);
                let view_id = FromJson::from_json(&change[0]);
                let fields = FromJson::from_json(&change[1]);
                let insert = FromJson::from_json(&change[2]);
                let remove = FromJson::from_json(&change[3]);
                (view_id, Change{fields:fields, insert: insert, remove: remove})
            }).collect(),
            commands: FromJson::from_json(json.as_object().unwrap().get("commands").unwrap_or(&Json::Array(vec![]))),
        }
    }
}

// --- persistence ---

pub fn read_file<P: AsRef<Path>>(path: P) -> String {
    let mut file = OpenOptions::new().create(true).open(path).unwrap();
    let mut contents = String::new();
    file.read_to_string(&mut contents).unwrap();
    contents
}

pub fn write_file<P: AsRef<Path>>(path: P, contents: &str) {
    let mut file = OpenOptions::new().create(true).truncate(true).write(true).open(path).unwrap();
    file.write_all(contents.as_bytes()).unwrap();
}

pub fn load<P: AsRef<Path>>(flow: &mut Flow, path: P) {
    for line in read_file(path).lines() {
        let json = Json::from_str(&line).unwrap();
        let event: Event = FromJson::from_json(&json);
        flow.quiesce(event.changes);
    }
}

// TODO should probably just save changes, not the whole event
pub fn save<P: AsRef<Path>>(flow: &Flow, path: P) {
    let changes = flow.as_changes();
    let text = format!("{}", Event{changes: changes, commands: vec![]}.to_json());
    write_file(path, &text[..]);
}

// --- server ---

pub fn send_event(server: &mut Server, changes: &Changes, commands: &Vec<Vec<String>>) {
    for sender_ix in (0..server.senders.len()).rev() {
        let event = Event{changes: changes.clone(), commands: commands.clone()};
        let text = format!("{}", event.to_json());
        match server.senders[sender_ix].send_message(Message::Text(text)) {
            Ok(_) => (),
            Err(error) => {
                println!("Send error on event: {}", error);
                server.senders.swap_remove(sender_ix);
            }
        };
    }
}

// rust-websocket is blocking, so we have to spawn a thread per connection
// each thread sends events back to a central channel
pub fn server_events() -> mpsc::Receiver<ServerEvent> {
    let (event_sender, event_receiver) = mpsc::channel();
    thread::spawn(move || {
        let server = websocket::Server::bind("0.0.0.0:2794").unwrap();
        for connection in server {
            let event_sender = event_sender.clone();
            thread::spawn(move || {
                // accept request
                let request = connection.unwrap().read_request().unwrap();
                request.validate().unwrap();
                let response = request.accept();
                let (sender, mut receiver) = response.send().unwrap().split();

                // hand over the sender
                event_sender.send(ServerEvent::Sync(sender)).unwrap();

                // handle messages
                for message in receiver.incoming_messages() {
                    let message = match message {
                        Ok(m) => m,
                        Err(_) => return,
                    };
                    match message {
                        Message::Binary(bytes) => {
                            event_sender.send(ServerEvent::Change(bytes)).unwrap();
                        }
                        Message::Close(_) => (), // the sender will get dropped next time we try to send
                        _ => println!("Unknown message: {:?}", message)
                    }
                }
            });
        }
    });
    event_receiver
}

pub fn handle_event(server: &mut Server, event: Event, event_json: Json, saves_dir: &Path) {
    // save the event
    {
        let mut autosave = OpenOptions::new().write(true).append(true).open(saves_dir.join("autosave")).unwrap();
        autosave.write_all(format!("{}", event_json).as_bytes()).unwrap();
        autosave.write_all("\n".as_bytes()).unwrap();
        autosave.flush().unwrap();
    }

    // copy the old flow so we can compare to the new flow for changes
    let old_flow = server.flow.clone();

    // run the flow until it reaches fixpoint
    server.flow.quiesce(event.changes);

    // handle commands
    let mut response_commands = vec![];
    for command in event.commands.iter() {
        let borrowed_words = command.iter().map(|word| &word[..]).collect::<Vec<_>>();
        match &borrowed_words[..] {
            ["load", filename] => {
                server.flow = Flow::new();
                load(&mut server.flow, saves_dir.join(filename));
                save(&server.flow, saves_dir.join("autosave"));
                response_commands.push(vec!["loaded".to_owned(), saves_dir.join(filename).to_str().unwrap().to_owned()]);
            }
            ["save", filename] => {
                save(&server.flow, saves_dir.join(filename));
                response_commands.push(vec!["saved".to_owned(), saves_dir.join(filename).to_str().unwrap().to_owned()]);
            }
            ["get events", id] => {
                let events_string = read_file(saves_dir.join("autosave"));
                response_commands.push(vec!["events got".to_owned(), id.to_owned(), events_string]);
            }
            ["set events", id, events_string] => {
                write_file(saves_dir.join("autosave"), events_string);
                server.flow = Flow::new();
                load(&mut server.flow, saves_dir.join("autosave"));
                response_commands.push(vec!["events set".to_owned(), id.to_owned()]);
            }
            other => panic!("Unknown command: {:?}", other),
        }
    }

    let changes = &server.flow.changes_from(old_flow);
    send_event(server, changes, &response_commands);
}

pub fn run(saves_dir: &Path) {

    let mut flow = Flow::new();
    time!("reading saved state", {
        load(&mut flow, saves_dir.join("autosave"));
    });

    let senders: Vec<sender::Sender<WebSocketStream>> = Vec::new();

    let mut server = Server{flow: flow, senders: senders};

    for server_event in server_events() {
        match server_event {

            // a new client has connected and needs to sync its state
            ServerEvent::Sync(mut sender) => {
                let changes = server.flow.as_changes();
                let event = Event{changes: changes, commands: vec![]};
                let text = format!("{}", event.to_json());
                match sender.send_message(Message::Text(text)) {
                    Ok(_) => server.senders.push(sender),
                    Err(error) => println!("Send error on sync: {}", error),
                };
            }

            // an existing client has sent a new message
            ServerEvent::Change(input_bytes) => {
                // TODO we throw cbor in here to avoid https://github.com/rust-lang/rustc-serialize/issues/113
                let mut decoder = cbor::Decoder::from_bytes(&input_bytes[..]);
                let cbor = decoder.items().next().unwrap().unwrap();
                let json = cbor.to_json();
                let event = FromJson::from_json(&json);
                handle_event(&mut server, event, json, saves_dir);
            }
        }
    }
}<commit_msg>Fix warning<commit_after>use std::thread;
use std::sync::mpsc;
use websocket;
use websocket::{Message, Sender, Receiver};
use websocket::server::sender;
use websocket::stream::WebSocketStream;
use std::io::prelude::*;
use std::fs::{OpenOptions};
use std::path::Path;
use rustc_serialize::json::{Json, ToJson};
use cbor;

use value::Value;
use relation::Change;
use flow::{Changes, Flow};

// The server manages a single Eve program and handles communication with the editor and clients
// TODO needs review / refactoring, especially session handling

pub struct Server {
    pub flow: Flow,
    pub senders: Vec<sender::Sender<WebSocketStream>>,
}

#[derive(Debug, Clone)]
pub struct Event {
    pub changes: Changes,
    // commands that affect the whole program state have to go through this side-channel rather than being added to a view
    pub commands: Vec<Vec<String>>,
}

pub enum ServerEvent {
    Change(Vec<u8>),
    Sync(sender::Sender<WebSocketStream>),
}

// --- json encodings ---

pub trait FromJson {
    fn from_json(json: &Json) -> Self;
}

impl ToJson for Value {
    fn to_json(&self) -> Json {
        match *self {
            Value::Null => panic!("Cannot allow the client to see nulls"),
            Value::Bool(bool) => Json::Boolean(bool),
            Value::String(ref string) => Json::String(string.clone()),
            Value::Float(float) => Json::F64(float),
            Value::Column(ref column) => Json::Array(column.iter().map(|v| v.to_json()).collect()),
        }
    }
}

impl FromJson for Value {
    fn from_json(json: &Json) -> Self {
        match *json {
            Json::Boolean(bool) => Value::Bool(bool),
            Json::String(ref string) => Value::String(string.clone()),
            Json::F64(float) => Value::Float(float),
            Json::I64(int) => Value::Float(int as f64),
            Json::U64(uint) => Value::Float(uint as f64),
            Json::Array(ref array) => Value::Column(array.iter().map(FromJson::from_json).collect()),
            _ => panic!("Cannot decode {:?} as Value", json),
        }
    }
}

impl FromJson for String {
    fn from_json(json: &Json) -> Self {
        json.as_string().unwrap().to_owned()
    }
}

impl<T: FromJson> FromJson for Vec<T> {
    fn from_json(json: &Json) -> Self {
        json.as_array().unwrap().iter().map(|t| FromJson::from_json(t)).collect()
    }
}

impl ToJson for Event {
    fn to_json(&self) -> Json {
        Json::Object(vec![
            ("changes".to_string(), Json::Array(
                self.changes.iter().map(|&(ref view_id, ref view_changes)| {
                    Json::Array(vec![
                        view_id.to_json(),
                        view_changes.fields.to_json(),
                        view_changes.insert.to_json(),
                        view_changes.remove.to_json(),
                        ])
                }).collect()
                )
            ),
            ("commands".to_string(), self.commands.to_json()),
            ("session".to_string(), "super unique session id!".to_json()), // TODO restore session handling
        ].into_iter().collect())
    }
}

impl FromJson for Event {
    fn from_json(json: &Json) -> Self {
        Event{
            changes: json.as_object().unwrap()["changes"]
            .as_array().unwrap().iter().map(|change| {
                let change = change.as_array().unwrap();
                assert_eq!(change.len(), 4);
                let view_id = FromJson::from_json(&change[0]);
                let fields = FromJson::from_json(&change[1]);
                let insert = FromJson::from_json(&change[2]);
                let remove = FromJson::from_json(&change[3]);
                (view_id, Change{fields:fields, insert: insert, remove: remove})
            }).collect(),
            commands: FromJson::from_json(json.as_object().unwrap().get("commands").unwrap_or(&Json::Array(vec![]))),
        }
    }
}

// --- persistence ---

pub fn read_file<P: AsRef<Path>>(path: P) -> String {
    let mut file = OpenOptions::new().create(true).open(path).unwrap();
    let mut contents = String::new();
    file.read_to_string(&mut contents).unwrap();
    contents
}

pub fn write_file<P: AsRef<Path>>(path: P, contents: &str) {
    let mut file = OpenOptions::new().create(true).truncate(true).write(true).open(path).unwrap();
    file.write_all(contents.as_bytes()).unwrap();
}

pub fn load<P: AsRef<Path>>(flow: &mut Flow, path: P) {
    for line in read_file(path).lines() {
        let json = Json::from_str(&line).unwrap();
        let event: Event = FromJson::from_json(&json);
        flow.quiesce(event.changes);
    }
}

// TODO should probably just save changes, not the whole event
pub fn save<P: AsRef<Path>>(flow: &Flow, path: P) {
    let changes = flow.as_changes();
    let text = format!("{}", Event{changes: changes, commands: vec![]}.to_json());
    write_file(path, &text[..]);
}

// --- server ---

pub fn send_event(server: &mut Server, changes: &Changes, commands: &Vec<Vec<String>>) {
    for sender_ix in (0..server.senders.len()).rev() {
        let event = Event{changes: changes.clone(), commands: commands.clone()};
        let text = format!("{}", event.to_json());
        match server.senders[sender_ix].send_message(Message::Text(text)) {
            Ok(_) => (),
            Err(error) => {
                println!("Send error on event: {}", error);
                server.senders.swap_remove(sender_ix);
            }
        };
    }
}

// rust-websocket is blocking, so we have to spawn a thread per connection
// each thread sends events back to a central channel
pub fn server_events() -> mpsc::Receiver<ServerEvent> {
    let (event_sender, event_receiver) = mpsc::channel();
    thread::spawn(move || {
        let server = websocket::Server::bind("0.0.0.0:2794").unwrap();
        for connection in server {
            let event_sender = event_sender.clone();
            thread::spawn(move || {
                // accept request
                let request = connection.unwrap().read_request().unwrap();
                request.validate().unwrap();
                let response = request.accept();
                let (sender, mut receiver) = response.send().unwrap().split();

                // hand over the sender
                event_sender.send(ServerEvent::Sync(sender)).unwrap();

                // handle messages
                for message in receiver.incoming_messages() {
                    let message = match message {
                        Ok(m) => m,
                        Err(_) => return,
                    };
                    match message {
                        Message::Binary(bytes) => {
                            event_sender.send(ServerEvent::Change(bytes)).unwrap();
                        }
                        Message::Close(_) => (), // the sender will get dropped next time we try to send
                        _ => println!("Unknown message: {:?}", message)
                    }
                }
            });
        }
    });
    event_receiver
}

pub fn handle_event(server: &mut Server, event: Event, event_json: Json, saves_dir: &Path) {
    // save the event
    {
        let mut autosave = OpenOptions::new().write(true).append(true).open(saves_dir.join("autosave")).unwrap();
        autosave.write_all(format!("{}", event_json).as_bytes()).unwrap();
        autosave.write_all("\n".as_bytes()).unwrap();
        autosave.flush().unwrap();
    }

    // copy the old flow so we can compare to the new flow for changes
    let old_flow = server.flow.clone();

    // run the flow until it reaches fixpoint
    server.flow.quiesce(event.changes);

    // handle commands
    let mut response_commands = vec![];
    for command in event.commands.iter() {
        let borrowed_words = command.iter().map(|word| &word[..]).collect::<Vec<_>>();
        match &borrowed_words[..] {
            ["load", filename] => {
                server.flow = Flow::new();
                load(&mut server.flow, saves_dir.join(filename));
                save(&server.flow, saves_dir.join("autosave"));
                response_commands.push(vec!["loaded".to_owned(), saves_dir.join(filename).to_str().unwrap().to_owned()]);
            }
            ["save", filename] => {
                save(&server.flow, saves_dir.join(filename));
                response_commands.push(vec!["saved".to_owned(), saves_dir.join(filename).to_str().unwrap().to_owned()]);
            }
            ["get events", id] => {
                let events_string = read_file(saves_dir.join("autosave"));
                response_commands.push(vec!["events got".to_owned(), id.to_owned(), events_string]);
            }
            ["set events", id, events_string] => {
                write_file(saves_dir.join("autosave"), events_string);
                server.flow = Flow::new();
                load(&mut server.flow, saves_dir.join("autosave"));
                response_commands.push(vec!["events set".to_owned(), id.to_owned()]);
            }
            other => panic!("Unknown command: {:?}", other),
        }
    }

    let changes = &server.flow.changes_from(old_flow);
    send_event(server, changes, &response_commands);
}

pub fn run(saves_dir: &Path) {

    let mut flow = Flow::new();
    time!("reading saved state", {
        load(&mut flow, saves_dir.join("autosave"));
    });

    let senders: Vec<sender::Sender<WebSocketStream>> = Vec::new();

    let mut server = Server{flow: flow, senders: senders};

    for server_event in server_events() {
        match server_event {

            // a new client has connected and needs to sync its state
            ServerEvent::Sync(mut sender) => {
                let changes = server.flow.as_changes();
                let event = Event{changes: changes, commands: vec![]};
                let text = format!("{}", event.to_json());
                match sender.send_message(Message::Text(text)) {
                    Ok(_) => server.senders.push(sender),
                    Err(error) => println!("Send error on sync: {}", error),
                };
            }

            // an existing client has sent a new message
            ServerEvent::Change(input_bytes) => {
                // TODO we throw cbor in here to avoid https://github.com/rust-lang/rustc-serialize/issues/113
                let mut decoder = cbor::Decoder::from_bytes(&input_bytes[..]);
                let cbor = decoder.items().next().unwrap().unwrap();
                let json = cbor.to_json();
                let event = FromJson::from_json(&json);
                handle_event(&mut server, event, json, saves_dir);
            }
        }
    }
}<|endoftext|>


<commit_before>extern crate protobuf;

mod schemata;

use protobuf::Message;
use protobuf::CodedInputStream;
use protobuf::CodedOutputStream;

use schemata::addressbook::Person;

use std::fs::File;
use std::io::{self, Read, Write};

#[cfg(test)]
#[test]
fn decode_basic() {
    let mut _in_handle = io::stdin();
    let mut in_handle = _in_handle.lock();

    let mut _out_handle = std::io::stdout();
    let mut out_handle = _out_handle.lock();

    let mut person = Person::new();
    person.set_name("sevag".to_string());

    let mut outstream = CodedOutputStream::new(&mut out_handle);
    match person.write_to(&mut outstream) {
        Ok(x) => println!("Succesfully wrote to stdout: {:?}", x),
        Err(e) => panic!(e),
    }

    let mut buffer = Vec::new();

    match in_handle.read_to_end(&mut buffer) {
        Ok(_) => {
            let mut instream = CodedInputStream::from_bytes(&buffer);
            match Person::new().merge_from(&mut instream) {
                Ok(x) => println!("Decode result: {:?}", x),
                Err(e) => panic!(e),
            }
        },
        Err(e) => panic!(e),
    }
}
<commit_msg>Better include {self} syntax<commit_after>extern crate protobuf;

mod schemata;

use protobuf::Message;
use protobuf::CodedInputStream;
use protobuf::CodedOutputStream;

use schemata::addressbook::Person;

use std::io::{self, Read, Write};

#[cfg(test)]
#[test]
fn decode_basic() {
    let mut _in_handle = io::stdin();
    let mut in_handle = _in_handle.lock();

    let mut _out_handle = io::stdout();
    let mut out_handle = _out_handle.lock();

    let mut person = Person::new();
    person.set_name("sevag".to_string());

    let mut outstream = CodedOutputStream::new(&mut out_handle);
    match person.write_to_with_cached_sizes(&mut outstream) {
        Ok(x) => println!("Succesfully wrote to stdout: {:?}", x),
        Err(e) => panic!(e),
    }

    let mut buffer = Vec::new();

    match in_handle.read_to_end(&mut buffer) {
        Ok(_) => {
            let mut instream = CodedInputStream::from_bytes(&buffer);
            match Person::new().merge_from(&mut instream) {
                Ok(x) => println!("Decode result: {:?}", x),
                Err(e) => panic!(e),
            }
        },
        Err(e) => panic!(e),
    }
}
<|endoftext|>


<commit_before>use eyre::{bail, Result};
use graph_triples::{
    direction, relations, resources::ResourceEntry, Direction, Pairs, Relation, Resource, Triple,
};
use path_slash::PathExt;
use petgraph::{
    graph::NodeIndex,
    stable_graph::StableGraph,
    visit::{self, EdgeRef, IntoEdgeReferences, IntoNodeReferences},
    EdgeDirection::Incoming,
};
use schemars::{
    gen::SchemaGenerator,
    schema::{Schema, SchemaObject},
    schema_for, JsonSchema,
};
use serde::{ser::SerializeMap, Serialize};
use serde_json::json;
use serde_with::skip_serializing_none;
use std::{
    cmp::Ordering,
    collections::HashMap,
    path::{Path, PathBuf},
};
use strum::Display;
use utils::some_string;

/// A dependency graph for a project or document
#[derive(Debug, Default, Clone)]
pub struct Graph {
    /// The path of the project or document that this graph is for
    ///
    /// Primarily used to make file paths relative in visualizations and
    /// if ever persisting the graph.
    path: PathBuf,

    /// The graph itself
    ///
    /// Use a `petgraph::StableGraph` so that nodes can be added and removed
    /// without changing node indices.
    graph: StableGraph<Resource, Relation>,

    /// Indices of the nodes in the tree
    ///
    /// This is necessary to keep track of which resources
    /// are already in the graph and re-use their index if they are.
    indices: HashMap<Resource, NodeIndex>,
}

impl Serialize for Graph {
    /// Custom serialization to strip prefix from paths, add stable node indices,
    /// and exclude properties that are included by default by `petgraph` (e.g `node_holes`).
    ///
    /// Our general approach is to keep paths absolute whilst in memory and only convert to
    /// relative when necessary (e.g. visualizations). See also `Graph::to_dot`.
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::Serializer,
    {
        let nodes: Vec<serde_json::Value> = self
            .graph
            .node_references()
            .map(|(index, resource)| {
                let mut obj = serde_json::to_value(resource).expect("To be able to serialize");
                let obj = obj.as_object_mut().expect("To be an object");

                // Strip prefix from paths
                if let Some(path) = match resource {
                    Resource::Symbol(symbol) => Some(symbol.path.clone()),
                    Resource::Node(node) => Some(node.path.clone()),
                    Resource::File(file) => Some(file.path.clone()),
                    _ => None,
                } {
                    let path = path
                        .strip_prefix(&self.path)
                        .unwrap_or(&path)
                        .to_slash_lossy();
                    obj.insert("path".to_string(), json!(path));
                }

                obj.insert("index".to_string(), json!(index));
                json!(obj)
            })
            .collect();

        let edges: Vec<serde_json::Value> = self
            .graph
            .edge_references()
            .map(|edge| -> serde_json::Value {
                json!({
                    "from": edge.source(),
                    "to": edge.target(),
                    "relation": edge.weight()
                })
            })
            .collect();

        let mut map = serializer.serialize_map(Some(2))?;
        map.serialize_entry("nodes", &nodes)?;
        map.serialize_entry("edges", &edges)?;
        map.end()
    }
}

/// The available graph serialization formats
pub const FORMATS: [&str; 3] = ["dot", "json", "yaml"];

impl Graph {
    /// Create a new, empty graph
    pub fn new<P: AsRef<Path>>(path: P) -> Graph {
        Graph {
            path: PathBuf::from(path.as_ref()),
            indices: HashMap::new(),
            graph: StableGraph::new(),
        }
    }

    /// Create a graph from set of dependency relations
    pub fn from_relations<P: AsRef<Path>>(path: P, relations: &[(Resource, Pairs)]) -> Graph {
        let mut graph = Graph::new(path);
        graph.add_relations(relations);
        graph
    }

    /// Add a resource to the graph
    pub fn add_resource(&mut self, resource: Resource) {
        if self.indices.get(&resource).is_none() {
            let index = self.graph.add_node(resource.clone());
            self.indices.insert(resource, index);
        }
    }

    /// Add a triple to the graph
    pub fn add_triple(&mut self, (subject, relation, object): Triple) {
        let subject = if let Some(index) = self.indices.get(&subject) {
            *index
        } else {
            let index = self.graph.add_node(subject.clone());
            self.indices.insert(subject, index);
            index
        };

        let object = if let Some(index) = self.indices.get(&object) {
            *index
        } else {
            let index = self.graph.add_node(object.clone());
            self.indices.insert(object, index);
            index
        };

        let (from, to) = match direction(&relation) {
            Direction::From => (object, subject),
            Direction::To => (subject, object),
        };

        self.graph.add_edge(from, to, relation);
    }

    /// Add a set of triples to the graph
    pub fn add_triples(&mut self, triples: Vec<Triple>) {
        triples
            .into_iter()
            .for_each(|triple| self.add_triple(triple))
    }

    /// Add as set of relations to the graph
    pub fn add_relations(&mut self, relations: &[(Resource, Pairs)]) {
        for (subject, pairs) in relations {
            for (relation, object) in pairs {
                self.add_triple((subject.clone(), relation.clone(), object.clone()));
            }
        }
    }

    /// Perform a topological sort of the graph
    ///
    /// Returns a vector of [`ResourceEntry`] items in topological order.
    pub fn toposort(&self) -> Result<Vec<ResourceEntry>> {
        // Create the entries, with no dependencies or depth
        let mut entries: Vec<ResourceEntry> = self
            .graph
            .node_indices()
            .map(|index| {
                let resource = self.graph[index].clone();
                ResourceEntry {
                    id: resource.id(),
                    resource,
                    dependencies: Vec::new(),
                    depth: 0,
                }
            })
            .collect();

        // Walk over nodes in topological order, updating dependencies and depth
        // Using topological order ensures that dependencies of dependencies
        // have already been updated.
        let mut topo = visit::Topo::new(&self.graph);
        while let Some(node_index) = topo.next(&self.graph) {
            let mut dependencies: Vec<String> = Vec::new();
            let mut depth = 0;

            let incomings = self.graph.neighbors_directed(node_index, Incoming);
            for incoming_index in incomings {
                let dependency = &entries[incoming_index.index()];

                for other in &dependency.dependencies {
                    if !dependencies.contains(other) {
                        dependencies.push(other.clone())
                    }
                }
                if !dependencies.contains(&dependency.id) {
                    dependencies.push(dependency.id.clone());
                }

                if dependency.depth + 1 > depth {
                    depth = dependency.depth + 1
                }
            }

            let mut node = &mut entries[node_index.index()];
            node.dependencies = dependencies;
            node.depth = depth;
        }

        // Do topological / depth sort
        entries.sort_by(|a, b| {
            if b.dependencies.contains(&a.id) {
                Ordering::Less
            } else {
                a.depth.cmp(&b.depth)
            }
        });

        Ok(entries)
    }

    /// Convert the graph to some format
    pub fn to_format(&self, format: &str) -> Result<String> {
        Ok(match format {
            "dot" => self.to_dot(),
            "json" => serde_json::to_string_pretty(self)?,
            "yaml" => serde_yaml::to_string(self)?,
            _ => bail!("Unknown graph format '{}'", format),
        })
    }

    /// Convert the graph to a visualization nodes and edges
    pub fn to_dot(&self) -> String {
        let nodes = self
            .indices
            .iter()
            .map(|(resource, node)| {
                let index = node.index();

                let path = match resource {
                    Resource::Symbol(symbol) => symbol.path.clone(),
                    Resource::Node(node) => node.path.clone(),
                    Resource::File(file) => file.path.clone(),
                    _ => PathBuf::new(),
                };
                let path = path
                    .strip_prefix(&self.path)
                    .unwrap_or(&path)
                    .to_slash_lossy();

                let (shape, fill_color, label) = match resource {
                    Resource::Symbol(symbol) => (
                        "diamond",
                        "#efb8b8",
                        format!(
                            "{}{}",
                            if !symbol.kind.is_empty() {
                                format!("{}\\n", symbol.kind)
                            } else {
                                "".to_string()
                            },
                            symbol.name
                        ),
                    ),
                    Resource::Node(node) => {
                        let label = if !node.id.starts_with('_') {
                            format!("{}\\n{}", node.kind, node.id)
                        } else {
                            node.kind.clone()
                        };
                        ("box", "#efe0b8", label)
                    }
                    Resource::File(..) => ("note", "#d1efb8", path.clone()),
                    Resource::Source(source) => ("house", "#efb8d4", source.name.clone()),
                    Resource::Module(module) => ("invhouse", "#b8efed", module.name.clone()),
                    Resource::Url(url) => ("box", "#cab8ef", url.url.clone()),
                };

                let node = match resource {
                    Resource::File(..) => format!(
                        r#"  n{index} [shape="point", style="invis" label="{label}"]"#,
                        index = index,
                        label = label
                    ),
                    _ => format!(
                        r#"  n{index} [shape="{shape}" fillcolor="{fill_color}" label="{label}"]"#,
                        index = index,
                        shape = shape,
                        fill_color = fill_color,
                        label = label.replace('\"', "\\\"")
                    ),
                };

                (path, node)
            })
            .collect::<Vec<(String, String)>>();

        let mut clusters: HashMap<String, Vec<String>> = HashMap::new();
        for (path, node) in nodes {
            clusters.entry(path).or_default().push(node)
        }

        let path_to_cluster = |path: &Path| {
            clusters
                .keys()
                .position(|key| {
                    key == &path
                        .strip_prefix(&self.path)
                        .unwrap_or(path)
                        .to_slash_lossy()
                })
                .unwrap_or(0)
        };

        let subgraphs = clusters
            .iter()
            .enumerate()
            .map(|(index, (label, nodes))| {
                if label.is_empty() {
                    nodes.join("\n")
                } else {
                    [
                        &format!("  subgraph cluster{} {{\n", index),
                        &format!("    label=\"{}\" fillcolor=\"#d1efb8\"\n  ", label),
                        &nodes.join("\n  "),
                        "\n  }",
                    ]
                    .concat()
                }
            })
            .collect::<Vec<String>>()
            .join("\n");

        let edges = self
            .graph
            .edge_indices()
            .filter_map(|edge| {
                if let (Some((from, to)), Some(relation)) = (
                    self.graph.edge_endpoints(edge),
                    self.graph.edge_weight(edge),
                ) {
                    let (label, style) = match relation {
                        Relation::Convert(relations::Convert { auto: active })
                        | Relation::Import(relations::Import { auto: active }) => (
                            relation.to_string(),
                            if *active { "solid" } else { "dashed" },
                        ),
                        Relation::Assign(relations::Assign { range })
                        | Relation::Use(relations::Use { range })
                        | Relation::Read(relations::Read { range })
                        | Relation::Write(relations::Write { range }) => {
                            let label = if *range == relations::NULL_RANGE {
                                relation.to_string()
                            } else {
                                format!("{} (L{})", relation, range.0 + 1)
                            };
                            (label, "solid")
                        }
                        _ => (relation.to_string(), "solid"),
                    };

                    let ltail = if let Some(Resource::File(file)) = self.graph.node_weight(from) {
                        format!(" ltail=\"cluster{}\"", path_to_cluster(&file.path))
                    } else {
                        "".to_string()
                    };

                    let lhead = if let Some(Resource::File(file)) = self.graph.node_weight(to) {
                        format!(" lhead=\"cluster{}\"", path_to_cluster(&file.path))
                    } else {
                        "".to_string()
                    };

                    Some(format!(
                        r#"  n{from} -> n{to} [label="{label}" style="{style}"{ltail}{lhead}]"#,
                        from = from.index(),
                        to = to.index(),
                        label = label,
                        style = style,
                        ltail = ltail,
                        lhead = lhead,
                    ))
                } else {
                    None
                }
            })
            .collect::<Vec<String>>()
            .join("\n");

        format!(
            r#"digraph {{
  graph [rankdir=LR compound=true fontname=Helvetica fontsize=12 labeljust=l color=gray]
  node [style=filled fontname=Helvetica fontsize=11]
  edge [fontname=Helvetica fontsize=10]

{subgraphs}

{edges}
}}
"#,
            subgraphs = subgraphs,
            edges = edges
        )
    }
}

#[derive(Debug, Display, JsonSchema, Serialize)]
#[serde(rename_all = "lowercase")]
#[strum(serialize_all = "lowercase")]
pub enum GraphEventType {
    Updated,
}

#[skip_serializing_none]
#[derive(Debug, JsonSchema, Serialize)]
#[schemars(deny_unknown_fields)]
pub struct GraphEvent {
    /// The path of the project (absolute)
    project: PathBuf,

    /// The type of event
    #[serde(rename = "type")]
    type_: GraphEventType,

    /// The graph at the time of the event
    #[schemars(schema_with = "GraphEvent::graph_schema")]
    graph: Graph,
}

impl GraphEvent {
    /// Generate the JSON Schema for the `graph` property
    fn graph_schema(_generator: &mut SchemaGenerator) -> Schema {
        Schema::Object(SchemaObject {
            reference: some_string!("Graph"),
            ..Default::default()
        })
    }

    /// Publish a `GraphEvent`.
    ///
    /// Will publish an event under the `projects:<project>:graph` topic.
    pub fn publish(project: &Path, type_: GraphEventType, graph: &Graph) {
        let topic = &format!("projects:{}:graph", project.display());
        let event = GraphEvent {
            project: project.to_path_buf(),
            type_,
            graph: graph.clone(),
        };
        events::publish(topic, &event)
    }
}

/// Get JSON Schemas for this crate
pub fn schemas() -> Result<serde_json::Value> {
    Ok(json!([
        schema_for!(Resource),
        schema_for!(Relation),
        schema_for!(GraphEvent),
        serde_json::json!({
            "$id": "Triple",
            "title": "Triple",
            "description": "A subject-relation-object triple",
            "type" : "array",
            "items": [
                {
                    "tsType": "Resource"
                },
                {
                    "tsType": "Relation"
                },
                {
                    "tsType": "Resource"
                }
            ],
            "minItems": 3,
            "maxItems": 3
        }),
        serde_json::json!({
            "$id": "Graph",
            "title": "Graph",
            "description": "A project dependency graph",
            "type" : "object",
            "required": ["nodes", "edges"],
            "properties": {
                "nodes": {
                    "description": "The resources in the graph",
                    "type": "array",
                    "items": {
                        "tsType": "Resource"
                    },
                    "isRequired": true
                },
                "edges": {
                    "description": "The relations between resources in the graph",
                    "type": "array",
                    "items": {
                        "type": "object",
                        "required": ["from", "to", "relation"],
                        "properties": {
                            "from": "integer",
                            "to": "integer",
                            "relation" : {
                                "tsType": "Resource"
                            }
                        },
                        "additionalProperties": false
                    },
                    "isRequired": true
                }
            },
            "additionalProperties": false
        }),
    ]))
}
<commit_msg>chore(Graphs): Minor refactoring for brevity<commit_after>use eyre::{bail, Result};
use graph_triples::{
    direction, relations, resources::ResourceEntry, Direction, Pairs, Relation, Resource, Triple,
};
use path_slash::PathExt;
use petgraph::{
    graph::NodeIndex,
    stable_graph::StableGraph,
    visit::{self, EdgeRef, IntoEdgeReferences, IntoNodeReferences},
    EdgeDirection::Incoming,
};
use schemars::{
    gen::SchemaGenerator,
    schema::{Schema, SchemaObject},
    schema_for, JsonSchema,
};
use serde::{ser::SerializeMap, Serialize};
use serde_json::json;
use serde_with::skip_serializing_none;
use std::{
    cmp::Ordering,
    collections::HashMap,
    path::{Path, PathBuf},
};
use strum::Display;
use utils::some_string;

/// A dependency graph for a project or document
#[derive(Debug, Default, Clone)]
pub struct Graph {
    /// The path of the project or document that this graph is for
    ///
    /// Primarily used to make file paths relative in visualizations and
    /// if ever persisting the graph.
    path: PathBuf,

    /// The graph itself
    ///
    /// Use a `petgraph::StableGraph` so that nodes can be added and removed
    /// without changing node indices.
    graph: StableGraph<Resource, Relation>,

    /// Indices of the nodes in the tree
    ///
    /// This is necessary to keep track of which resources
    /// are already in the graph and re-use their index if they are.
    indices: HashMap<Resource, NodeIndex>,
}

impl Serialize for Graph {
    /// Custom serialization to strip prefix from paths, add stable node indices,
    /// and exclude properties that are included by default by `petgraph` (e.g `node_holes`).
    ///
    /// Our general approach is to keep paths absolute whilst in memory and only convert to
    /// relative when necessary (e.g. visualizations). See also `Graph::to_dot`.
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::Serializer,
    {
        let nodes: Vec<serde_json::Value> = self
            .graph
            .node_references()
            .map(|(index, resource)| {
                let mut obj = serde_json::to_value(resource).expect("To be able to serialize");
                let obj = obj.as_object_mut().expect("To be an object");

                // Strip prefix from paths
                if let Some(path) = match resource {
                    Resource::Symbol(symbol) => Some(symbol.path.clone()),
                    Resource::Node(node) => Some(node.path.clone()),
                    Resource::File(file) => Some(file.path.clone()),
                    _ => None,
                } {
                    let path = path
                        .strip_prefix(&self.path)
                        .unwrap_or(&path)
                        .to_slash_lossy();
                    obj.insert("path".to_string(), json!(path));
                }

                obj.insert("index".to_string(), json!(index));
                json!(obj)
            })
            .collect();

        let edges: Vec<serde_json::Value> = self
            .graph
            .edge_references()
            .map(|edge| -> serde_json::Value {
                json!({
                    "from": edge.source(),
                    "to": edge.target(),
                    "relation": edge.weight()
                })
            })
            .collect();

        let mut map = serializer.serialize_map(Some(2))?;
        map.serialize_entry("nodes", &nodes)?;
        map.serialize_entry("edges", &edges)?;
        map.end()
    }
}

/// The available graph serialization formats
pub const FORMATS: [&str; 3] = ["dot", "json", "yaml"];

impl Graph {
    /// Create a new, empty graph
    pub fn new<P: AsRef<Path>>(path: P) -> Graph {
        Graph {
            path: PathBuf::from(path.as_ref()),
            indices: HashMap::new(),
            graph: StableGraph::new(),
        }
    }

    /// Create a graph from set of dependency relations
    pub fn from_relations<P: AsRef<Path>>(path: P, relations: &[(Resource, Pairs)]) -> Graph {
        let mut graph = Graph::new(path);
        graph.add_relations(relations);
        graph
    }

    /// Add a resource to the graph
    pub fn add_resource(&mut self, resource: Resource) {
        if self.indices.get(&resource).is_none() {
            let index = self.graph.add_node(resource.clone());
            self.indices.insert(resource, index);
        }
    }

    /// Add a triple to the graph
    pub fn add_triple(&mut self, (subject, relation, object): Triple) {
        let subject = if let Some(index) = self.indices.get(&subject) {
            *index
        } else {
            let index = self.graph.add_node(subject.clone());
            self.indices.insert(subject, index);
            index
        };

        let object = if let Some(index) = self.indices.get(&object) {
            *index
        } else {
            let index = self.graph.add_node(object.clone());
            self.indices.insert(object, index);
            index
        };

        let (from, to) = match direction(&relation) {
            Direction::From => (object, subject),
            Direction::To => (subject, object),
        };

        self.graph.add_edge(from, to, relation);
    }

    /// Add a set of triples to the graph
    pub fn add_triples(&mut self, triples: Vec<Triple>) {
        triples
            .into_iter()
            .for_each(|triple| self.add_triple(triple))
    }

    /// Add as set of relations to the graph
    pub fn add_relations(&mut self, relations: &[(Resource, Pairs)]) {
        for (subject, pairs) in relations {
            for (relation, object) in pairs {
                self.add_triple((subject.clone(), relation.clone(), object.clone()));
            }
        }
    }

    /// Perform a topological sort of the graph
    ///
    /// Returns a vector of [`ResourceEntry`] items in topological order.
    pub fn toposort(&self) -> Result<Vec<ResourceEntry>> {
        let graph = &self.graph;

        // Create the entries, with no dependencies or depth
        let mut entries: Vec<ResourceEntry> = graph
            .node_indices()
            .map(|index| {
                let resource = graph[index].clone();
                ResourceEntry {
                    id: resource.id(),
                    resource,
                    dependencies: Vec::new(),
                    depth: 0,
                }
            })
            .collect();

        // Walk over nodes in topological order, updating dependencies and depth
        // Using topological order ensures that dependencies of dependencies
        // have already been updated.
        let mut topo = visit::Topo::new(&graph);
        while let Some(node_index) = topo.next(&graph) {
            let mut dependencies: Vec<String> = Vec::new();
            let mut depth = 0;

            let incomings = graph.neighbors_directed(node_index, Incoming);
            for incoming_index in incomings {
                let dependency = &entries[incoming_index.index()];

                for other in &dependency.dependencies {
                    if !dependencies.contains(other) {
                        dependencies.push(other.clone())
                    }
                }
                if !dependencies.contains(&dependency.id) {
                    dependencies.push(dependency.id.clone());
                }

                if dependency.depth + 1 > depth {
                    depth = dependency.depth + 1
                }
            }

            let mut node = &mut entries[node_index.index()];
            node.dependencies = dependencies;
            node.depth = depth;
        }

        // Do topological / depth sort
        entries.sort_by(|a, b| {
            if b.dependencies.contains(&a.id) {
                Ordering::Less
            } else {
                a.depth.cmp(&b.depth)
            }
        });

        Ok(entries)
    }

    /// Convert the graph to some format
    pub fn to_format(&self, format: &str) -> Result<String> {
        Ok(match format {
            "dot" => self.to_dot(),
            "json" => serde_json::to_string_pretty(self)?,
            "yaml" => serde_yaml::to_string(self)?,
            _ => bail!("Unknown graph format '{}'", format),
        })
    }

    /// Convert the graph to a visualization nodes and edges
    pub fn to_dot(&self) -> String {
        let nodes = self
            .indices
            .iter()
            .map(|(resource, node)| {
                let index = node.index();

                let path = match resource {
                    Resource::Symbol(symbol) => symbol.path.clone(),
                    Resource::Node(node) => node.path.clone(),
                    Resource::File(file) => file.path.clone(),
                    _ => PathBuf::new(),
                };
                let path = path
                    .strip_prefix(&self.path)
                    .unwrap_or(&path)
                    .to_slash_lossy();

                let (shape, fill_color, label) = match resource {
                    Resource::Symbol(symbol) => (
                        "diamond",
                        "#efb8b8",
                        format!(
                            "{}{}",
                            if !symbol.kind.is_empty() {
                                format!("{}\\n", symbol.kind)
                            } else {
                                "".to_string()
                            },
                            symbol.name
                        ),
                    ),
                    Resource::Node(node) => {
                        let label = if !node.id.starts_with('_') {
                            format!("{}\\n{}", node.kind, node.id)
                        } else {
                            node.kind.clone()
                        };
                        ("box", "#efe0b8", label)
                    }
                    Resource::File(..) => ("note", "#d1efb8", path.clone()),
                    Resource::Source(source) => ("house", "#efb8d4", source.name.clone()),
                    Resource::Module(module) => ("invhouse", "#b8efed", module.name.clone()),
                    Resource::Url(url) => ("box", "#cab8ef", url.url.clone()),
                };

                let node = match resource {
                    Resource::File(..) => format!(
                        r#"  n{index} [shape="point", style="invis" label="{label}"]"#,
                        index = index,
                        label = label
                    ),
                    _ => format!(
                        r#"  n{index} [shape="{shape}" fillcolor="{fill_color}" label="{label}"]"#,
                        index = index,
                        shape = shape,
                        fill_color = fill_color,
                        label = label.replace('\"', "\\\"")
                    ),
                };

                (path, node)
            })
            .collect::<Vec<(String, String)>>();

        let mut clusters: HashMap<String, Vec<String>> = HashMap::new();
        for (path, node) in nodes {
            clusters.entry(path).or_default().push(node)
        }

        let path_to_cluster = |path: &Path| {
            clusters
                .keys()
                .position(|key| {
                    key == &path
                        .strip_prefix(&self.path)
                        .unwrap_or(path)
                        .to_slash_lossy()
                })
                .unwrap_or(0)
        };

        let subgraphs = clusters
            .iter()
            .enumerate()
            .map(|(index, (label, nodes))| {
                if label.is_empty() {
                    nodes.join("\n")
                } else {
                    [
                        &format!("  subgraph cluster{} {{\n", index),
                        &format!("    label=\"{}\" fillcolor=\"#d1efb8\"\n  ", label),
                        &nodes.join("\n  "),
                        "\n  }",
                    ]
                    .concat()
                }
            })
            .collect::<Vec<String>>()
            .join("\n");

        let edges = self
            .graph
            .edge_indices()
            .filter_map(|edge| {
                if let (Some((from, to)), Some(relation)) = (
                    self.graph.edge_endpoints(edge),
                    self.graph.edge_weight(edge),
                ) {
                    let (label, style) = match relation {
                        Relation::Convert(relations::Convert { auto: active })
                        | Relation::Import(relations::Import { auto: active }) => (
                            relation.to_string(),
                            if *active { "solid" } else { "dashed" },
                        ),
                        Relation::Assign(relations::Assign { range })
                        | Relation::Use(relations::Use { range })
                        | Relation::Read(relations::Read { range })
                        | Relation::Write(relations::Write { range }) => {
                            let label = if *range == relations::NULL_RANGE {
                                relation.to_string()
                            } else {
                                format!("{} (L{})", relation, range.0 + 1)
                            };
                            (label, "solid")
                        }
                        _ => (relation.to_string(), "solid"),
                    };

                    let ltail = if let Some(Resource::File(file)) = self.graph.node_weight(from) {
                        format!(" ltail=\"cluster{}\"", path_to_cluster(&file.path))
                    } else {
                        "".to_string()
                    };

                    let lhead = if let Some(Resource::File(file)) = self.graph.node_weight(to) {
                        format!(" lhead=\"cluster{}\"", path_to_cluster(&file.path))
                    } else {
                        "".to_string()
                    };

                    Some(format!(
                        r#"  n{from} -> n{to} [label="{label}" style="{style}"{ltail}{lhead}]"#,
                        from = from.index(),
                        to = to.index(),
                        label = label,
                        style = style,
                        ltail = ltail,
                        lhead = lhead,
                    ))
                } else {
                    None
                }
            })
            .collect::<Vec<String>>()
            .join("\n");

        format!(
            r#"digraph {{
  graph [rankdir=LR compound=true fontname=Helvetica fontsize=12 labeljust=l color=gray]
  node [style=filled fontname=Helvetica fontsize=11]
  edge [fontname=Helvetica fontsize=10]

{subgraphs}

{edges}
}}
"#,
            subgraphs = subgraphs,
            edges = edges
        )
    }
}

#[derive(Debug, Display, JsonSchema, Serialize)]
#[serde(rename_all = "lowercase")]
#[strum(serialize_all = "lowercase")]
pub enum GraphEventType {
    Updated,
}

#[skip_serializing_none]
#[derive(Debug, JsonSchema, Serialize)]
#[schemars(deny_unknown_fields)]
pub struct GraphEvent {
    /// The path of the project (absolute)
    project: PathBuf,

    /// The type of event
    #[serde(rename = "type")]
    type_: GraphEventType,

    /// The graph at the time of the event
    #[schemars(schema_with = "GraphEvent::graph_schema")]
    graph: Graph,
}

impl GraphEvent {
    /// Generate the JSON Schema for the `graph` property
    fn graph_schema(_generator: &mut SchemaGenerator) -> Schema {
        Schema::Object(SchemaObject {
            reference: some_string!("Graph"),
            ..Default::default()
        })
    }

    /// Publish a `GraphEvent`.
    ///
    /// Will publish an event under the `projects:<project>:graph` topic.
    pub fn publish(project: &Path, type_: GraphEventType, graph: &Graph) {
        let topic = &format!("projects:{}:graph", project.display());
        let event = GraphEvent {
            project: project.to_path_buf(),
            type_,
            graph: graph.clone(),
        };
        events::publish(topic, &event)
    }
}

/// Get JSON Schemas for this crate
pub fn schemas() -> Result<serde_json::Value> {
    Ok(json!([
        schema_for!(Resource),
        schema_for!(Relation),
        schema_for!(GraphEvent),
        serde_json::json!({
            "$id": "Triple",
            "title": "Triple",
            "description": "A subject-relation-object triple",
            "type" : "array",
            "items": [
                {
                    "tsType": "Resource"
                },
                {
                    "tsType": "Relation"
                },
                {
                    "tsType": "Resource"
                }
            ],
            "minItems": 3,
            "maxItems": 3
        }),
        serde_json::json!({
            "$id": "Graph",
            "title": "Graph",
            "description": "A project dependency graph",
            "type" : "object",
            "required": ["nodes", "edges"],
            "properties": {
                "nodes": {
                    "description": "The resources in the graph",
                    "type": "array",
                    "items": {
                        "tsType": "Resource"
                    },
                    "isRequired": true
                },
                "edges": {
                    "description": "The relations between resources in the graph",
                    "type": "array",
                    "items": {
                        "type": "object",
                        "required": ["from", "to", "relation"],
                        "properties": {
                            "from": "integer",
                            "to": "integer",
                            "relation" : {
                                "tsType": "Resource"
                            }
                        },
                        "additionalProperties": false
                    },
                    "isRequired": true
                }
            },
            "additionalProperties": false
        }),
    ]))
}
<|endoftext|>