<commit_before>use consumer::*;
use producer::*;
use std::collections::hash_map::*;
use std::hash::Hash;
use std::marker::PhantomData;
use std::mem::replace;
use std::ops::{Add, Deref};
use std::rc::Rc;
use stream::*;

pub struct Group<K, V> {
    consumer: Option<Box<ConsumerBox<V>>>,
    key: K,
}

impl<K: Copy, V> Group<K, V> {
    fn new(key: K) -> Self {
        Group {
            consumer: None,
            key: key,
        }
    }

    pub fn get_key(&self) -> K {
        self.key
    }

    fn emit(&mut self, item: V) {
        if let Some(ref mut consumer) = self.consumer {
            consumer.emit(item);
        }
    }

    fn end(mut self) {
        if let Some(consumer) = replace(&mut self.consumer, None) {
            consumer.end();
        }
    }
}

pub struct GroupBy<C, F, K, V> {
    consumer: C,
    hashmap: HashMap<K, Group<K, V>>,
    key_selector: F,
}

impl<C, F, K, V> Consumer<V> for GroupBy<C, F, K, V>
    where C: ConsumerRef<Group<K, V>>,
          F: FnMut(&V) -> K,
          K: Hash + Eq + Copy
{
    fn init(&mut self, producer: Rc<Producer>) {
        self.consumer.init(producer);
    }

    fn emit(&mut self, item: V) {
        let key = (self.key_selector)(&item);
        let consumer = &mut self.consumer;
        let group = self.hashmap
                        .entry(key)
                        .or_insert_with(|| {
                            let g = Group::new(key);
                            consumer.emit(&g);
                            g
                        });

        group.emit(item);
    }

    fn end(mut self) {
        for (_, v) in self.hashmap.drain() {
            v.end();
        }

        self.consumer.end();
    }
}

pub struct GroupByFactory<F, K, S, V> {
    key_selector: F,
    marker_k: PhantomData<K>,
    marker_v: PhantomData<V>,
    stream: S,
}

impl<F, K, S, V> StreamRef<Group<K, V>> for GroupByFactory<F, K, S, V>
    where F: FnMut(&V) -> K,
          K: Copy + Hash + Eq,
          S: Stream<V>
{
    fn consume<C: ConsumerRef<Group<K, V>>>(self, consumer: C) {
        self.stream.consume(GroupBy {
            consumer: consumer,
            hashmap: HashMap::new(),
            key_selector: self.key_selector,
        });
    }
}

pub trait GroupByStream<T>: StreamRefMut<T> {
    fn group_by<F: FnMut(&V) -> K, K, V>(self, key_selector: F) -> GroupByFactory<F, K, Self, V>
        where Self: Sized
    {
        GroupByFactory {
            key_selector: key_selector,
            marker_k: PhantomData::<K>,
            marker_v: PhantomData::<V>,
            stream: self,
        }
    }
}

impl<S, T> GroupByStream<T> for S where S: StreamRefMut<T> {}<commit_msg>remove unused using<commit_after>use consumer::*;
use producer::*;
use std::collections::hash_map::*;
use std::hash::Hash;
use std::marker::PhantomData;
use std::mem::replace;
use std::rc::Rc;
use stream::*;

pub struct Group<K, V> {
    consumer: Option<Box<ConsumerBox<V>>>,
    key: K,
}

impl<K: Copy, V> Group<K, V> {
    fn new(key: K) -> Self {
        Group {
            consumer: None,
            key: key,
        }
    }

    pub fn get_key(&self) -> K {
        self.key
    }

    fn emit(&mut self, item: V) {
        if let Some(ref mut consumer) = self.consumer {
            consumer.emit(item);
        }
    }

    fn end(mut self) {
        if let Some(consumer) = replace(&mut self.consumer, None) {
            consumer.end();
        }
    }
}

pub struct GroupBy<C, F, K, V> {
    consumer: C,
    hashmap: HashMap<K, Group<K, V>>,
    key_selector: F,
}

impl<C, F, K, V> Consumer<V> for GroupBy<C, F, K, V>
    where C: ConsumerRef<Group<K, V>>,
          F: FnMut(&V) -> K,
          K: Hash + Eq + Copy
{
    fn init(&mut self, producer: Rc<Producer>) {
        self.consumer.init(producer);
    }

    fn emit(&mut self, item: V) {
        let key = (self.key_selector)(&item);
        let consumer = &mut self.consumer;
        let group = self.hashmap
                        .entry(key)
                        .or_insert_with(|| {
                            let g = Group::new(key);
                            consumer.emit(&g);
                            g
                        });

        group.emit(item);
    }

    fn end(mut self) {
        for (_, v) in self.hashmap.drain() {
            v.end();
        }

        self.consumer.end();
    }
}

pub struct GroupByFactory<F, K, S, V> {
    key_selector: F,
    marker_k: PhantomData<K>,
    marker_v: PhantomData<V>,
    stream: S,
}

impl<F, K, S, V> StreamRef<Group<K, V>> for GroupByFactory<F, K, S, V>
    where F: FnMut(&V) -> K,
          K: Copy + Hash + Eq,
          S: Stream<V>
{
    fn consume<C: ConsumerRef<Group<K, V>>>(self, consumer: C) {
        self.stream.consume(GroupBy {
            consumer: consumer,
            hashmap: HashMap::new(),
            key_selector: self.key_selector,
        });
    }
}

pub trait GroupByStream<T>: StreamRefMut<T> {
    fn group_by<F: FnMut(&V) -> K, K, V>(self, key_selector: F) -> GroupByFactory<F, K, Self, V>
        where Self: Sized
    {
        GroupByFactory {
            key_selector: key_selector,
            marker_k: PhantomData::<K>,
            marker_v: PhantomData::<V>,
            stream: self,
        }
    }
}

impl<S, T> GroupByStream<T> for S where S: StreamRefMut<T> {}<|endoftext|>


<commit_before>#![feature(proc_macro)]

extern crate hayaku_http;
extern crate hayaku_path;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;

use hayaku_http::{Http, Request, Response};
use hayaku_path::Router;

use std::sync::Arc;

fn main() {
    let addr = "127.0.0.1:3000".parse().unwrap();

    let mut router = Router::new();
    router.get("/", Arc::new(hello_handler)).unwrap();
    router.get("/plaintext", Arc::new(plain_handler)).unwrap();
    router.get("/json", Arc::new(json_handler)).unwrap();

    Http::new(router, ()).threads(4).listen_and_serve(addr);
}

fn hello_handler(_req: &Request, res: &mut Response, _ctx: &()) {
    res.body(b"hello, world!").unwrap();
}

fn plain_handler(_req: &Request, res: &mut Response, _ctx: &()) {
    res.add_header("Content-Type".to_string(), "text/plain".to_string());
    res.body(b"hello, world!").unwrap();
}

#[derive(Serialize, Deserialize)]
struct Message {
    message: String,
}

fn json_handler(_req: &Request, res: &mut Response, _ctx: &()) {
    let msg = Message { message: "Hello, World!".to_string() };
    let data = serde_json::to_vec(&msg).unwrap();

    res.add_header("Content-Type".to_string(), "application/json".to_string());
    res.body(&data).unwrap();
}
<commit_msg>Update examples for changes to httbloat<commit_after>#![feature(proc_macro)]

extern crate hayaku_http;
extern crate hayaku_path;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;

use hayaku_http::{Http, Request, Response};
use hayaku_path::Router;

use std::sync::Arc;

fn main() {
    let addr = "127.0.0.1:3000".parse().unwrap();

    let mut router = Router::new();
    router.get("/", Arc::new(hello_handler)).unwrap();
    router.get("/plaintext", Arc::new(plain_handler)).unwrap();
    router.get("/json", Arc::new(json_handler)).unwrap();

    Http::new(router, ()).threads(4).listen_and_serve(addr);
}

fn hello_handler(_req: &Request, res: &mut Response, _ctx: &()) {
    res.body(b"hello, world!");
}

fn plain_handler(_req: &Request, res: &mut Response, _ctx: &()) {
    res.add_header("Content-Type".to_string(), "text/plain".to_string());
    res.body(b"hello, world!");
}

#[derive(Serialize, Deserialize)]
struct Message {
    message: String,
}

fn json_handler(_req: &Request, res: &mut Response, _ctx: &()) {
    let msg = Message { message: "Hello, World!".to_string() };
    let data = serde_json::to_vec(&msg).unwrap();

    res.add_header("Content-Type".to_string(), "application/json".to_string());
    res.body(&data);
}
<|endoftext|>


<commit_before>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

#![forbid(unsafe_code)]

use crate::{
    cluster::Cluster,
    experiments::{Context, Experiment, ExperimentParam},
    instance,
    instance::Instance,
    tx_emitter::EmitJobRequest,
};
use async_trait::async_trait;
use futures::future::try_join_all;
use libra_logger::prelude::*;
use std::{
    collections::HashSet,
    fmt,
    iter::once,
    time::{Duration, Instant},
};
use structopt::StructOpt;
use tokio::time;

/// Reboot `updated_instance` with newer image tag
pub async fn update_batch_instance(
    context: &mut Context<'_>,
    updated_instance: &[Instance],
    updated_tag: String,
) -> anyhow::Result<()> {
    let deadline = Instant::now() + Duration::from_secs(2 * 60);

    info!("Stop Existing instances.");
    let futures: Vec<_> = updated_instance.iter().map(Instance::stop).collect();
    try_join_all(futures).await?;

    info!("Reinstantiate a set of new nodes.");
    let futures: Vec<_> = updated_instance
        .iter()
        .map(|instance| {
            let mut newer_config = instance.instance_config().clone();
            newer_config.replace_tag(updated_tag.clone()).unwrap();
            context
                .cluster_swarm
                .spawn_new_instance(newer_config, false)
        })
        .collect();
    let instances = try_join_all(futures).await?;

    info!("Wait for the instances to recover.");
    let futures: Vec<_> = instances
        .iter()
        .map(|instance| instance.wait_json_rpc(deadline))
        .collect();
    try_join_all(futures).await?;

    // Add a timeout to have wait for validators back to healthy mode.
    // TODO: Replace this with a blocking health check.
    info!("Wait for the instance to sync up with peers");
    time::delay_for(Duration::from_secs(20)).await;
    Ok(())
}

#[derive(StructOpt, Debug)]
pub struct CompatiblityTestParams {
    #[structopt(
        long,
        default_value = "15",
        help = "Number of nodes to update in the first batch"
    )]
    pub count: usize,
    #[structopt(long, help = "Image tag of newer validator software")]
    pub updated_image_tag: String,
}

pub struct CompatibilityTest {
    first_node: Instance,
    first_batch: Vec<Instance>,
    second_batch: Vec<Instance>,
    full_nodes: Vec<Instance>,
    updated_image_tag: String,
}

impl ExperimentParam for CompatiblityTestParams {
    type E = CompatibilityTest;
    fn build(self, cluster: &Cluster) -> Self::E {
        if self.count > cluster.validator_instances().len() || self.count == 0 {
            panic!(
                "Can not reboot {} validators in cluster with {} instances",
                self.count,
                cluster.validator_instances().len()
            );
        }
        let (first_batch, second_batch) = cluster.split_n_validators_random(self.count);
        let mut first_batch = first_batch.into_validator_instances();
        let first_node = first_batch
            .pop()
            .expect("Requires at least one validator in the first batch");

        Self::E {
            first_node,
            first_batch,
            second_batch: second_batch.into_validator_instances(),
            full_nodes: cluster.fullnode_instances().to_vec(),
            updated_image_tag: self.updated_image_tag,
        }
    }
}

#[async_trait]
impl Experiment for CompatibilityTest {
    fn affected_validators(&self) -> HashSet<String> {
        instance::instancelist_to_set(&self.first_batch)
            .union(&instance::instancelist_to_set(&self.second_batch))
            .cloned()
            .chain(once(self.first_node.peer_name().clone()))
            .collect()
    }

    async fn run(&mut self, context: &mut Context<'_>) -> anyhow::Result<()> {
        let validator_txn_job = EmitJobRequest::for_instances(
            context.cluster.validator_instances().to_vec(),
            context.global_emit_job_request,
        );
        let fullnode_txn_job = EmitJobRequest::for_instances(
            context.cluster.fullnode_instances().to_vec(),
            context.global_emit_job_request,
        );
        let job_duration = Duration::from_secs(3);

        // Generate some traffic
        context
            .tx_emitter
            .emit_txn_for(job_duration, fullnode_txn_job.clone())
            .await
            .map_err(|e| anyhow::format_err!("Failed to generate traffic: {}", e))?;

        info!("1. Changing the images for the first instance to validate storage");
        let first_node = vec![self.first_node.clone()];
        update_batch_instance(context, &first_node, self.updated_image_tag.clone()).await?;
        context
            .tx_emitter
            .emit_txn_for(
                job_duration,
                EmitJobRequest::for_instances(first_node, context.global_emit_job_request),
            )
            .await
            .map_err(|e| anyhow::format_err!("Storage backwards compat broken: {}", e))?;

        context
            .report
            .report_metric(self.to_string(), "storage_compat", 1.0);

        info!("2. Changing images for the first batch to test consensus");
        update_batch_instance(context, &self.first_batch, self.updated_image_tag.clone()).await?;
        context
            .tx_emitter
            .emit_txn_for(job_duration, validator_txn_job.clone())
            .await
            .map_err(|e| anyhow::format_err!("Consensus backwards compat broken: {}", e))?;

        context
            .report
            .report_metric(self.to_string(), "consensus_compat", 1.0);

        info!("3. Changing images for the rest of the nodes");
        update_batch_instance(context, &self.second_batch, self.updated_image_tag.clone()).await?;
        context
            .tx_emitter
            .emit_txn_for(job_duration, validator_txn_job)
            .await
            .map_err(|e| {
                anyhow::format_err!("Failed to upgrade rest of validator images: {}", e)
            })?;

        info!("4. Changing images for full nodes");
        update_batch_instance(context, &self.full_nodes, self.updated_image_tag.clone()).await?;
        context
            .tx_emitter
            .emit_txn_for(job_duration, fullnode_txn_job)
            .await
            .map_err(|e| anyhow::format_err!("Failed to upgrade full node images: {}", e))?;
        Ok(())
    }

    fn deadline(&self) -> Duration {
        Duration::from_secs(16 * 60)
    }
}

impl fmt::Display for CompatibilityTest {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Updating [")?;
        for instance in self.first_batch.iter() {
            write!(f, "{}, ", instance)?;
        }
        for instance in self.second_batch.iter() {
            write!(f, "{}, ", instance)?;
        }
        write!(f, "]")?;
        writeln!(f, "Updated Config: {:?}", self.updated_image_tag)
    }
}
<commit_msg>[cluster-test] improve compatibility_test report<commit_after>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

#![forbid(unsafe_code)]

use crate::{
    cluster::Cluster,
    experiments::{Context, Experiment, ExperimentParam},
    instance,
    instance::Instance,
    tx_emitter::EmitJobRequest,
};
use async_trait::async_trait;
use futures::future::try_join_all;
use libra_logger::prelude::*;
use std::{
    collections::HashSet,
    fmt,
    iter::once,
    time::{Duration, Instant},
};
use structopt::StructOpt;
use tokio::time;

/// Reboot `updated_instance` with newer image tag
pub async fn update_batch_instance(
    context: &mut Context<'_>,
    updated_instance: &[Instance],
    updated_tag: String,
) -> anyhow::Result<()> {
    let deadline = Instant::now() + Duration::from_secs(2 * 60);

    info!("Stop Existing instances.");
    let futures: Vec<_> = updated_instance.iter().map(Instance::stop).collect();
    try_join_all(futures).await?;

    info!("Reinstantiate a set of new nodes.");
    let futures: Vec<_> = updated_instance
        .iter()
        .map(|instance| {
            let mut newer_config = instance.instance_config().clone();
            newer_config.replace_tag(updated_tag.clone()).unwrap();
            context
                .cluster_swarm
                .spawn_new_instance(newer_config, false)
        })
        .collect();
    let instances = try_join_all(futures).await?;

    info!("Wait for the instances to recover.");
    let futures: Vec<_> = instances
        .iter()
        .map(|instance| instance.wait_json_rpc(deadline))
        .collect();
    try_join_all(futures).await?;

    // Add a timeout to have wait for validators back to healthy mode.
    // TODO: Replace this with a blocking health check.
    info!("Wait for the instance to sync up with peers");
    time::delay_for(Duration::from_secs(20)).await;
    Ok(())
}

pub fn get_instance_list_str(batch: &[Instance]) -> String {
    let mut nodes_list = String::from("");
    for instance in batch.iter() {
        nodes_list.push_str(&instance.to_string());
        nodes_list.push_str(", ")
    }
    nodes_list
}

#[derive(StructOpt, Debug)]
pub struct CompatiblityTestParams {
    #[structopt(
        long,
        default_value = "15",
        help = "Number of nodes to update in the first batch"
    )]
    pub count: usize,
    #[structopt(long, help = "Image tag of newer validator software")]
    pub updated_image_tag: String,
}

pub struct CompatibilityTest {
    first_node: Instance,
    first_batch: Vec<Instance>,
    second_batch: Vec<Instance>,
    full_nodes: Vec<Instance>,
    updated_image_tag: String,
}

impl ExperimentParam for CompatiblityTestParams {
    type E = CompatibilityTest;
    fn build(self, cluster: &Cluster) -> Self::E {
        if self.count > cluster.validator_instances().len() || self.count == 0 {
            panic!(
                "Can not reboot {} validators in cluster with {} instances",
                self.count,
                cluster.validator_instances().len()
            );
        }
        let (first_batch, second_batch) = cluster.split_n_validators_random(self.count);
        let mut first_batch = first_batch.into_validator_instances();
        let first_node = first_batch
            .pop()
            .expect("Requires at least one validator in the first batch");

        Self::E {
            first_node,
            first_batch,
            second_batch: second_batch.into_validator_instances(),
            full_nodes: cluster.fullnode_instances().to_vec(),
            updated_image_tag: self.updated_image_tag,
        }
    }
}

#[async_trait]
impl Experiment for CompatibilityTest {
    fn affected_validators(&self) -> HashSet<String> {
        instance::instancelist_to_set(&self.first_batch)
            .union(&instance::instancelist_to_set(&self.second_batch))
            .cloned()
            .chain(once(self.first_node.peer_name().clone()))
            .collect()
    }

    async fn run(&mut self, context: &mut Context<'_>) -> anyhow::Result<()> {
        let validator_txn_job = EmitJobRequest::for_instances(
            context.cluster.validator_instances().to_vec(),
            context.global_emit_job_request,
        );
        let fullnode_txn_job = EmitJobRequest::for_instances(
            context.cluster.fullnode_instances().to_vec(),
            context.global_emit_job_request,
        );
        let job_duration = Duration::from_secs(3);
        context.report.report_text(format!(
            "Compatibility test results for {} ==> {} (PR)",
            context.current_tag, self.updated_image_tag
        ));

        // Generate some traffic
        let msg = format!(
            "1. All instances running {}, generating some traffic on network",
            context.current_tag
        );
        info!("{}", msg);
        context.report.report_text(msg);
        context
            .tx_emitter
            .emit_txn_for(job_duration, fullnode_txn_job.clone())
            .await
            .map_err(|e| anyhow::format_err!("Failed to generate traffic: {}", e))?;

        let msg = format!(
            "2. First validator {} ==> {}, to validate storage",
            context.current_tag, self.updated_image_tag
        );
        info!("{}", msg);
        info!("Upgrading validator: {}", self.first_node);
        context.report.report_text(msg);
        let first_node = vec![self.first_node.clone()];
        update_batch_instance(context, &first_node, self.updated_image_tag.clone()).await?;
        context
            .tx_emitter
            .emit_txn_for(
                job_duration,
                EmitJobRequest::for_instances(first_node, context.global_emit_job_request),
            )
            .await
            .map_err(|e| anyhow::format_err!("Storage backwards compat broken: {}", e))?;

        let msg = format!(
            "3. First batch validators ({}) {} ==> {}, to test consensus",
            self.first_batch.len(),
            context.current_tag,
            self.updated_image_tag
        );
        info!("{}", msg);
        info!(
            "Upgrading validators: {}",
            get_instance_list_str(&self.first_batch)
        );
        context.report.report_text(msg);
        update_batch_instance(context, &self.first_batch, self.updated_image_tag.clone()).await?;
        context
            .tx_emitter
            .emit_txn_for(job_duration, validator_txn_job.clone())
            .await
            .map_err(|e| anyhow::format_err!("Consensus backwards compat broken: {}", e))?;

        let msg = format!(
            "4. Second batch validators ({}) {} ==> {}, to upgrade rest of the validators",
            self.second_batch.len(),
            context.current_tag,
            self.updated_image_tag
        );
        info!("{}", msg);
        info!(
            "Upgrading validators: {}",
            get_instance_list_str(&self.second_batch)
        );
        context.report.report_text(msg);
        update_batch_instance(context, &self.second_batch, self.updated_image_tag.clone()).await?;
        context
            .tx_emitter
            .emit_txn_for(job_duration, validator_txn_job)
            .await
            .map_err(|e| {
                anyhow::format_err!("Failed to upgrade rest of validator images: {}", e)
            })?;

        let msg = format!(
            "5. All full nodes ({}) {} ==> {}, to finish the network upgrade",
            self.full_nodes.len(),
            context.current_tag,
            self.updated_image_tag
        );
        info!("{}", msg);
        info!(
            "Upgrading full nodes: {}",
            get_instance_list_str(&self.full_nodes)
        );
        context.report.report_text(msg);
        update_batch_instance(context, &self.full_nodes, self.updated_image_tag.clone()).await?;
        context
            .tx_emitter
            .emit_txn_for(job_duration, fullnode_txn_job)
            .await
            .map_err(|e| anyhow::format_err!("Failed to upgrade full node images: {}", e))?;
        Ok(())
    }

    fn deadline(&self) -> Duration {
        Duration::from_secs(16 * 60)
    }
}

impl fmt::Display for CompatibilityTest {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "Compatibility test, phased upgrade to {} in batches of 1, {}, {}",
            self.updated_image_tag,
            self.first_batch.len(),
            self.second_batch.len()
        )
    }
}
<|endoftext|>


<commit_before>//TODO: implement styles
//TODO: return Results rather than panicing
//TODO: deal with empty strings

#![allow(dead_code)]

use std::io::prelude::*;
use std::fs::File;

use time;
use adapton::adapton_sigs::Adapton;
use adapton::engine::Engine;

pub const DEFAULT_STYLE: &'static str = "active";

pub trait GMLog<A:Adapton> {
  fn log_snapshot(self: &Self, st: &mut A, msg: Option<&str>) {
    startframe(st, &format!("Logged at {}", time::now().asctime()), msg)
  }
  fn log_comment(self: &Self, st:&mut A, msg: Option<&str>) {
    makecomment(st, &format!("Commented at {}", time::now().asctime()), msg)
  }
}

// pub trait GMAutoLog {
//   fn set_file(self: &mut Self, st: &mut File);
//   fn get_file(self: &Self) -> &mut File;
//   fn start(self: &Self, msg: Option<&str>);
//   fn snapshot(self: &Self, msg: Option<&str>);
//   fn comment(self: &Self, msg: Option<&str>);
//   fn end(self: &Self, msg: Option<&str>);
// }

trait CombineStr {
  fn join(self: &mut Self, sep: &str) -> String;
}

impl<'a,I> CombineStr for I where I: Iterator<Item=&'a str> {
  fn join(self: &mut Self, sep: &str) -> String {
    let mut result: String = "".to_string();
    if let Some(part) = self.next() {
      result = result + part;
      for part in self {
        result = result + sep + part;
      }
    }
    result
  }
}

fn addtitle<A:Adapton>(st: &mut A, title: &str, comment: Option<&str>) {
  if let Some(file) = st.gmlog() {
    let title = title.lines().join(" ");
    writeln!(file, "{}", title).unwrap();
    if let Some(comment) = comment {
      let comment = comment.lines().join("\n ");
      writeln!(file, " {}", comment).unwrap();
    }
  }
}

pub fn startframe<A:Adapton>(st: &mut A, title: &str, comment: Option<&str>) {
  if let Some(file) = st.gmlog() {write!(file, "[state]").unwrap(); }
  addtitle(st, title, comment);
}

pub fn startdframe<A:Adapton>(st: &mut A, title: &str, comment: Option<&str>) {
  if let Some(file) = st.gmlog() { write!(file, "[change]").unwrap(); }
  addtitle(st, title, comment);
}

pub fn addnode<A:Adapton>(st: &mut A, id: &str, style: &str, name: &str, comment: Option<&str>) {
    let id = id.split("\"").join("").split_whitespace().join("_");
    let style = style.split("\"").join("").split_whitespace().join("_");
    if let Some(file) = st.gmlog() { write!(file, "[node {} {}]", id, style).unwrap(); }
    addtitle(st, name, comment);
}

pub fn addedge<A:Adapton>(st: &mut A, from: &str, to: &str, tag: &str, style: &str, name: &str, comment: Option<&str>) {
  let from = from.split("\"").join("").split_whitespace().join("_");
  let to = to.split("\"").join("").split_whitespace().join("_");
  let tag = tag.split("\"").join("").split_whitespace().join("_");
  let style = style.split("\"").join("").split_whitespace().join("_");
  if let Some(file) = st.gmlog() { write!(file, "[edge {} {} {} {}]", from, to, tag, style).unwrap(); }
  addtitle(st, name, comment);
}

pub fn makecomment<A:Adapton>(st: &mut A, short: &str, long: Option<&str>) {
  if let Some(file) = st.gmlog() {
    write!(file, "[comment]").unwrap();
  }
  addtitle(st, short, long);
}

#[test]
fn it_works() {
  let f = &mut File::create("testlogging.txt").unwrap();
  startframe(f, &"first".to_string(), None);
  startdframe(f, "", Some("no title here?"));
  startframe(f, "", None);
  startdframe(f, "one\nmore", Some("then we're done"));
  startframe(f, "this is it!", Some(" the
    last
    one!!!
    "));
  startdframe(f, "done", None);
  addnode(f, "node 1", "blue", "a first node", None);
  addedge(f, " node 1", "node2", "", "green", "", Some("a green line!\nbeat that ;)"));

  impl<A:Adapton> GMLog<A> for String {};
  let l: String = String::from("a logger");

  let st = &mut Engine::new();
  l.log_snapshot(st, f, Some("first snap"));
  l.log_snapshot(st, f, None);
  l.log_comment(st, f, Some("some words"));
}
<commit_msg>commented out obsolete test<commit_after>//TODO: implement styles
//TODO: return Results rather than panicing
//TODO: deal with empty strings

#![allow(dead_code)]

use std::io::prelude::*;
use std::fs::File;

use time;
use adapton::adapton_sigs::Adapton;
use adapton::engine::Engine;

pub const DEFAULT_STYLE: &'static str = "active";

pub trait GMLog<A:Adapton> {
  fn log_snapshot(self: &Self, st: &mut A, msg: Option<&str>) {
    startframe(st, &format!("Logged at {}", time::now().asctime()), msg)
  }
  fn log_comment(self: &Self, st:&mut A, msg: Option<&str>) {
    makecomment(st, &format!("Commented at {}", time::now().asctime()), msg)
  }
}

// pub trait GMAutoLog {
//   fn set_file(self: &mut Self, st: &mut File);
//   fn get_file(self: &Self) -> &mut File;
//   fn start(self: &Self, msg: Option<&str>);
//   fn snapshot(self: &Self, msg: Option<&str>);
//   fn comment(self: &Self, msg: Option<&str>);
//   fn end(self: &Self, msg: Option<&str>);
// }

trait CombineStr {
  fn join(self: &mut Self, sep: &str) -> String;
}

impl<'a,I> CombineStr for I where I: Iterator<Item=&'a str> {
  fn join(self: &mut Self, sep: &str) -> String {
    let mut result: String = "".to_string();
    if let Some(part) = self.next() {
      result = result + part;
      for part in self {
        result = result + sep + part;
      }
    }
    result
  }
}

fn addtitle<A:Adapton>(st: &mut A, title: &str, comment: Option<&str>) {
  if let Some(file) = st.gmlog() {
    let title = title.lines().join(" ");
    writeln!(file, "{}", title).unwrap();
    if let Some(comment) = comment {
      let comment = comment.lines().join("\n ");
      writeln!(file, " {}", comment).unwrap();
    }
  }
}

pub fn startframe<A:Adapton>(st: &mut A, title: &str, comment: Option<&str>) {
  if let Some(file) = st.gmlog() {write!(file, "[state]").unwrap(); }
  addtitle(st, title, comment);
}

pub fn startdframe<A:Adapton>(st: &mut A, title: &str, comment: Option<&str>) {
  if let Some(file) = st.gmlog() { write!(file, "[change]").unwrap(); }
  addtitle(st, title, comment);
}

pub fn addnode<A:Adapton>(st: &mut A, id: &str, style: &str, name: &str, comment: Option<&str>) {
    let id = id.split("\"").join("").split_whitespace().join("_");
    let style = style.split("\"").join("").split_whitespace().join("_");
    if let Some(file) = st.gmlog() { write!(file, "[node {} {}]", id, style).unwrap(); }
    addtitle(st, name, comment);
}

pub fn addedge<A:Adapton>(st: &mut A, from: &str, to: &str, tag: &str, style: &str, name: &str, comment: Option<&str>) {
  let from = from.split("\"").join("").split_whitespace().join("_");
  let to = to.split("\"").join("").split_whitespace().join("_");
  let tag = tag.split("\"").join("").split_whitespace().join("_");
  let style = style.split("\"").join("").split_whitespace().join("_");
  if let Some(file) = st.gmlog() { write!(file, "[edge {} {} {} {}]", from, to, tag, style).unwrap(); }
  addtitle(st, name, comment);
}

pub fn makecomment<A:Adapton>(st: &mut A, short: &str, long: Option<&str>) {
  if let Some(file) = st.gmlog() {
    write!(file, "[comment]").unwrap();
  }
  addtitle(st, short, long);
}

// #[test]
// fn it_works() {
//   let f = &mut File::create("testlogging.txt").unwrap();
//   startframe(f, &"first".to_string(), None);
//   startdframe(f, "", Some("no title here?"));
//   startframe(f, "", None);
//   startdframe(f, "one\nmore", Some("then we're done"));
//   startframe(f, "this is it!", Some(" the
//     last
//     one!!!
//     "));
//   startdframe(f, "done", None);
//   addnode(f, "node 1", "blue", "a first node", None);
//   addedge(f, " node 1", "node2", "", "green", "", Some("a green line!\nbeat that ;)"));

//   impl<A:Adapton> GMLog<A> for String {};
//   let l: String = String::from("a logger");

//   let st = &mut Engine::new();
//   l.log_snapshot(st, f, Some("first snap"));
//   l.log_snapshot(st, f, None);
//   l.log_comment(st, f, Some("some words"));
// }
<|endoftext|>


<commit_before>#![feature(test)]

extern crate serde_yaml;
extern crate test;

extern crate liquid;

static TEXT_ONLY: &'static str = "Hello World";

#[bench]
fn bench_parse_text(b: &mut test::Bencher) {
    let parser = liquid::ParserBuilder::with_liquid().extra_filters().build();
    b.iter(|| parser.parse(TEXT_ONLY));
}

#[bench]
fn bench_render_text(b: &mut test::Bencher) {
    let parser = liquid::ParserBuilder::with_liquid().extra_filters().build();
    let template = parser
        .parse(TEXT_ONLY)
        .expect("Benchmark template parsing failed");

    let data = liquid::value::Object::new();

    b.iter(|| template.render(&data));
}

// Mirrors tera's VARIABLE_ONLY benchmark
static VARIABLE_ONLY: &'static str = "{{product.name}}";
static VARIABLE_ONLY_OBJECT: &'static str = "
username: bob
product:
  - name: Moto G
  - manufacturer: Motorola
  - summary: A phone
  - price: 100
";

#[bench]
fn bench_parse_variable(b: &mut test::Bencher) {
    let parser = liquid::ParserBuilder::with_liquid().extra_filters().build();
    b.iter(|| parser.parse(VARIABLE_ONLY));
}

#[bench]
fn bench_render_variable(b: &mut test::Bencher) {
    let parser = liquid::ParserBuilder::with_liquid().extra_filters().build();
    let template = parser
        .parse(VARIABLE_ONLY)
        .expect("Benchmark template parsing failed");

    let data: liquid::value::Object =
        serde_yaml::from_str(VARIABLE_ONLY_OBJECT).expect("Benchmark object parsing failed");

    b.iter(|| template.render(&data));
}

// Mirrors handlebars' benchmark
static ITERATE: &'static str = "

    <title>{{year}}</title>

CSL {{year}}

    {% for team in teams %}

      {{team.name}}: {{team.score}}

    {{/each}}

";
static ITERATE_OBJECT: &'static str = "
year: 2015
teams:
  - name: Jiangsu
    score: 43
  - name: Beijing
    score: 27
  - name: Guangzhou
    score: 22
  - name: Shandong
    score: 12
";

#[bench]
fn bench_parse_template(b: &mut test::Bencher) {
    let parser = liquid::ParserBuilder::with_liquid().extra_filters().build();
    b.iter(|| parser.parse(ITERATE));
}

#[bench]
fn bench_render_template(b: &mut test::Bencher) {
    let parser = liquid::ParserBuilder::with_liquid().extra_filters().build();
    let template = parser
        .parse(ITERATE)
        .expect("Benchmark template parsing failed");

    let data: liquid::value::Object =
        serde_yaml::from_str(ITERATE_OBJECT).expect("Benchmark object parsing failed");

    b.iter(|| template.render(&data));
}
<commit_msg>fix(bench): Benchmark what we intended<commit_after>#![feature(test)]

extern crate serde_yaml;
extern crate test;

extern crate liquid;

static TEXT_ONLY: &'static str = "Hello World";

#[bench]
fn bench_parse_text(b: &mut test::Bencher) {
    let parser = liquid::ParserBuilder::with_liquid().extra_filters().build();
    b.iter(|| parser.parse(TEXT_ONLY));
}

#[bench]
fn bench_render_text(b: &mut test::Bencher) {
    let parser = liquid::ParserBuilder::with_liquid().extra_filters().build();
    let template = parser
        .parse(TEXT_ONLY)
        .expect("Benchmark template parsing failed");

    let data = liquid::value::Object::new();

    b.iter(|| template.render(&data));
}

// Mirrors tera's VARIABLE_ONLY benchmark
static VARIABLE_ONLY: &'static str = "{{product.name}}";
static VARIABLE_ONLY_OBJECT: &'static str = "
username: bob
product:
  name: Moto G
  manufacturer: Motorola
  summary: A phone
  price: 100
";

#[bench]
fn bench_parse_variable(b: &mut test::Bencher) {
    let parser = liquid::ParserBuilder::with_liquid().extra_filters().build();
    b.iter(|| parser.parse(VARIABLE_ONLY));
}

#[bench]
fn bench_render_variable(b: &mut test::Bencher) {
    let parser = liquid::ParserBuilder::with_liquid().extra_filters().build();
    let template = parser
        .parse(VARIABLE_ONLY)
        .expect("Benchmark template parsing failed");

    let data: liquid::value::Object =
        serde_yaml::from_str(VARIABLE_ONLY_OBJECT).expect("Benchmark object parsing failed");

    b.iter(|| template.render(&data));
}

// Mirrors handlebars' benchmark
static ITERATE: &'static str = "

    <title>{{year}}</title>

CSL {{year}}

    {% for team in teams %}

      {{team.name}}: {{team.score}}

    {% endfor %}

";
static ITERATE_OBJECT: &'static str = "
year: 2015
teams:
  - name: Jiangsu
    score: 43
  - name: Beijing
    score: 27
  - name: Guangzhou
    score: 22
  - name: Shandong
    score: 12
";

#[bench]
fn bench_parse_template(b: &mut test::Bencher) {
    let parser = liquid::ParserBuilder::with_liquid().extra_filters().build();
    b.iter(|| parser.parse(ITERATE));
}

#[bench]
fn bench_render_template(b: &mut test::Bencher) {
    let parser = liquid::ParserBuilder::with_liquid().extra_filters().build();
    let template = parser
        .parse(ITERATE)
        .expect("Benchmark template parsing failed");

    let data: liquid::value::Object =
        serde_yaml::from_str(ITERATE_OBJECT).expect("Benchmark object parsing failed");

    b.iter(|| template.render(&data));
}
<|endoftext|>


<commit_before>#![feature(macro_rules)]

extern crate test;
extern crate utp;

use test::Bencher;
use std::io::test::next_test_ip4;
use utp::UtpStream;

macro_rules! iotry(
    ($e:expr) => (match $e { Ok(e) => e, Err(e) => panic!("{}", e) })
)

#[bench]
fn bench_connection_setup_and_teardown(b: &mut Bencher) {
    let server_addr = next_test_ip4();
    b.iter(|| {
        let mut server = iotry!(UtpStream::bind(server_addr));

        spawn(proc() {
            let mut client = iotry!(UtpStream::connect(server_addr));
            iotry!(client.close());
        });

        iotry!(server.read_to_end());
        iotry!(server.close());
    });
}

#[bench]
fn bench_transfer_one_megabyte(b: &mut Bencher) {
    let len = 1024 * 1024;
    let server_addr = next_test_ip4();
    b.iter(|| {
        let data = Vec::from_fn(len, |idx| idx as u8);
        let mut server = iotry!(UtpStream::bind(server_addr));

        spawn(proc() {
            let mut client = iotry!(UtpStream::connect(server_addr));
            iotry!(client.write(data.as_slice()));
            iotry!(client.close());
        });

        iotry!(server.read_to_end());
        iotry!(server.close());
    });
}
<commit_msg>Inform the Bencher about the transferred bytes.<commit_after>#![feature(macro_rules)]

extern crate test;
extern crate utp;

use test::Bencher;
use std::io::test::next_test_ip4;
use utp::UtpStream;

macro_rules! iotry(
    ($e:expr) => (match $e { Ok(e) => e, Err(e) => panic!("{}", e) })
)

#[bench]
fn bench_connection_setup_and_teardown(b: &mut Bencher) {
    let server_addr = next_test_ip4();
    b.iter(|| {
        let mut server = iotry!(UtpStream::bind(server_addr));

        spawn(proc() {
            let mut client = iotry!(UtpStream::connect(server_addr));
            iotry!(client.close());
        });

        iotry!(server.read_to_end());
        iotry!(server.close());
    });
}

#[bench]
fn bench_transfer_one_megabyte(b: &mut Bencher) {
    let len = 1024 * 1024;
    let server_addr = next_test_ip4();
    b.iter(|| {
        let data = Vec::from_fn(len, |idx| idx as u8);
        let mut server = iotry!(UtpStream::bind(server_addr));

        spawn(proc() {
            let mut client = iotry!(UtpStream::connect(server_addr));
            iotry!(client.write(data.as_slice()));
            iotry!(client.close());
        });

        iotry!(server.read_to_end());
        iotry!(server.close());
    });
    b.bytes = len as u64;
}
<|endoftext|>


<commit_before>extern crate http_parser;

use std::default::Default;
use std::str;

use self::http_parser::{HttpParser, HttpParserCallback, HttpParserType, HttpMethod, HttpErrno};

#[deriving(PartialEq, Eq )]
pub enum LastHeaderType {
    None,
    Field,
    Value,
}

pub struct Message {
    pub name: String,
    pub raw: String,
    pub tp: HttpParserType,
    pub strict: bool,
    pub method: HttpMethod,
    pub status_code: u16,
    pub response_status: Vec<u8>,
    pub request_path: String,
    pub request_url: String,
    pub fragment: String,
    pub query_string: String,
    pub body: String,
    pub body_size: uint, // maybe not necessary
    pub host: String,
    pub userinfo: String,
    pub port: u16,
    pub num_headers: int, // might be able to delete this
    pub last_header_element: LastHeaderType,
    pub headers: Vec<[String,..2]>,
    pub should_keep_alive: bool,

    pub upgrade: String,

    pub http_major: u8,
    pub http_minor: u8,

    pub message_begin_cb_called: bool,
    pub headers_complete_cb_called: bool,
    pub message_complete_cb_called: bool,
    pub message_complete_on_eof: bool,
    pub body_is_final: bool,
}

impl Default for Message {
    fn default() -> Message {
        Message {
            name: String::new() ,
            raw: String::new(),
            tp: HttpParserType::HttpBoth,
            strict: true,
            method: HttpMethod::Delete,
            status_code: 0,
            response_status: vec![],
            request_path: String::new(),
            request_url: String::new(),
            fragment: String::new(),
            query_string: String::new(),
            body: String::new(),
            body_size: 0,
            host: String::new(),
            userinfo: String::new(),
            port: 0,
            num_headers: 0,
            last_header_element: LastHeaderType::None,
            headers: vec![],
            should_keep_alive: false,

            upgrade: String::new(),

            http_major: 0,
            http_minor: 0,

            message_begin_cb_called: false,
            headers_complete_cb_called: false,
            message_complete_cb_called: false,
            message_complete_on_eof: false,
            body_is_final: false,
        }
    }
}

pub struct CallbackEmpty;

impl HttpParserCallback for CallbackEmpty {}

pub struct CallbackRegular {
    pub num_messages: uint, // maybe not necessary
    pub messages: Vec<Message>,
    pub currently_parsing_eof: bool,
}

impl Default for CallbackRegular {
    fn default() -> CallbackRegular {
        CallbackRegular {
            num_messages: 0,
            messages: Vec::new(),
            currently_parsing_eof: false,
        }
    }
}

impl HttpParserCallback for CallbackRegular {
    fn on_message_begin(&mut self, parser : &HttpParser) -> Result<i8, &str> {
        self.messages[self.num_messages].message_begin_cb_called = true;
        Ok(0)
    }

    fn on_url(&mut self, parser : &HttpParser, data : &[u8]) -> Result<i8, &str> {
        match str::from_utf8(data) {
            Result::Ok(data_str) => {
                self.messages[self.num_messages].request_url.push_str(
                    data_str);
            },
            _ => panic!("on_url: data is not in utf8 encoding"),
        }
        Ok(0)
    }

    fn on_status(&mut self, parser : &HttpParser, data : &[u8]) -> Result<i8, &str> {
        self.messages[self.num_messages].response_status.push_all(data);
        Ok(0)
    }

    fn on_header_field(&mut self, parser : &HttpParser, data : &[u8]) -> Result<i8, &str> {
        let m : &mut Message = &mut self.messages[self.num_messages];

        if m.last_header_element != LastHeaderType::Field {
            m.num_headers += 1;
            m.headers.push([String::new(), String::new()]);
        }

        match str::from_utf8(data) {
            Result::Ok(data_str) => {
                let i = m.headers.len()-1;
                m.headers[i][0].push_str(data_str);
            },
            _ => panic!("on_header_field: data is not in utf8 encoding"),
        }

        m.last_header_element = LastHeaderType::Field;

        Ok(0)
    }

    fn on_header_value(&mut self, parser : &HttpParser, data : &[u8]) -> Result<i8, &str> {
        let m : &mut Message = &mut self.messages[self.num_messages];

        match str::from_utf8(data) {
            Result::Ok(data_str) => {
                let i = m.headers.len()-1;
                m.headers[i][1].push_str(data_str);
            },
            _ => panic!("on_header_value: data is not in utf8 encoding"),
        }

        m.last_header_element = LastHeaderType::Value;

        Ok(0)
    }

    fn on_headers_complete(&mut self, parser : &HttpParser) -> Result<i8, &str> {
        let m : &mut Message = &mut self.messages[self.num_messages];
        m.method = parser.method;
        m.status_code = parser.status_code;
        m.http_major = parser.http_major;
        m.http_minor = parser.http_minor;
        m.headers_complete_cb_called = true;
        m.should_keep_alive = parser.http_should_keep_alive();
        Ok(0)
    }

    fn on_body(&mut self, parser : &HttpParser, data : &[u8]) -> Result<i8, &str> {
        let m : &mut Message = &mut self.messages[self.num_messages];

        match str::from_utf8(data) {
            Result::Ok(data_str) => {
                m.body.push_str(data_str);
            },
            _ => panic!("on_body: data is not in utf8 encoding"),
        }
        m.body_size += data.len();

        if m.body_is_final {
            panic!("\n\n ** Error http_body_is_final() should return 1 \
                    on last on_body callback call \
                    but it doesn't! **\n\n");
        }

        m.body_is_final = parser.http_body_is_final();
        Ok(0)
    }

    fn on_message_complete(&mut self, parser : &HttpParser) -> Result<i8, &str> {
        {
            let m : &mut Message = &mut self.messages[self.num_messages];

            if m.should_keep_alive != parser.http_should_keep_alive() {
                panic!("\n\n ** Error http_should_keep_alive() should have same \
                        value in both on_message_complete and on_headers_complet \
                        but it doesn't! **\n\n");
            }

            if m.body_size > 0 && parser.http_body_is_final()
                && !m.body_is_final {
                panic!("\n\n ** Error http_body_is_final() should return 1 \
                        on last on_body callback call \
                        but it doesn't! **\n\n");
            }

            m.message_complete_cb_called = true;
            m.message_complete_on_eof = self.currently_parsing_eof;
        }

        self.messages.push(Message{..Default::default()});
        self.num_messages += 1;

        Ok(0)
    }
}

pub fn print_error(errno: HttpErrno, raw: &str, error_location: u64) {
    println!("\n*** {} ***\n", errno.to_string());

    let len = raw.len();
    let mut this_line = false;
    let mut char_len: u64 = 0;
    let mut error_location_line = 0;
    let mut eof = true;
    for i in range(0, len) {
        if i == (error_location as uint) { this_line = true; }
        match raw.char_at(i) {
            '\r' => {
                char_len = 2;
                print!("\\r");
            },
            '\n' => {
                println!("\\n");

                if this_line {
                    eof = false;
                    break;
                }

                error_location_line = 0;
                continue;
            },
            _ => {
                char_len = 1;
                print!("{}", raw.char_at(i));
            },
        }
        if !this_line { error_location_line += char_len; }
    }

    if eof {
        println!("[eof]");
    }

    for i in range(0, error_location_line as u64) {
        print!(" ");
    }
    println!("^\n\nerror location: {}", error_location);
}

pub fn assert_eq_message(actual: &Message, expected: &Message) {
    assert_eq!(actual.http_major, expected.http_major);
    assert_eq!(actual.http_minor, expected.http_minor);

    if expected.tp == HttpParserType::HttpRequest {
        assert!(actual.method == expected.method);
    } else {
        assert_eq!(actual.status_code, expected.status_code);
        assert_eq!(actual.response_status, expected.response_status);
    }

    assert_eq!(actual.should_keep_alive, expected.should_keep_alive);
    assert_eq!(actual.message_complete_on_eof, expected.message_complete_on_eof);

    assert!(actual.message_begin_cb_called);
    assert!(actual.headers_complete_cb_called);
    assert!(actual.message_complete_cb_called);

    assert_eq!(actual.request_url, expected.request_url);

    // Check URL components; we can't do this w/ CONNECT since it doesn't
    // send us a well-formed URL.
    // TODO add after implementing http_parser_parse_url()

    if expected.body_size > 0 {
        assert_eq!(actual.body_size, expected.body_size);
    } else {
        assert_eq!(actual.body, expected.body);
    }

    assert_eq!(actual.num_headers, expected.num_headers);

    for i in range(0, actual.num_headers) {
        assert_eq!(actual.headers[i as uint][0], expected.headers[i as uint][0]);
        assert_eq!(actual.headers[i as uint][1], expected.headers[i as uint][1]);
    }

    assert_eq!(actual.upgrade, expected.upgrade);
}
<commit_msg>add CallbackDontCall<commit_after>extern crate http_parser;

use std::default::Default;
use std::str;

use self::http_parser::{HttpParser, HttpParserCallback, HttpParserType, HttpMethod, HttpErrno};

#[deriving(PartialEq, Eq )]
pub enum LastHeaderType {
    None,
    Field,
    Value,
}

pub struct Message {
    pub name: String,
    pub raw: String,
    pub tp: HttpParserType,
    pub strict: bool,
    pub method: HttpMethod,
    pub status_code: u16,
    pub response_status: Vec<u8>,
    pub request_path: String,
    pub request_url: String,
    pub fragment: String,
    pub query_string: String,
    pub body: String,
    pub body_size: uint, // maybe not necessary
    pub host: String,
    pub userinfo: String,
    pub port: u16,
    pub num_headers: int, // might be able to delete this
    pub last_header_element: LastHeaderType,
    pub headers: Vec<[String,..2]>,
    pub should_keep_alive: bool,

    pub upgrade: String,

    pub http_major: u8,
    pub http_minor: u8,

    pub message_begin_cb_called: bool,
    pub headers_complete_cb_called: bool,
    pub message_complete_cb_called: bool,
    pub message_complete_on_eof: bool,
    pub body_is_final: bool,
}

impl Default for Message {
    fn default() -> Message {
        Message {
            name: String::new() ,
            raw: String::new(),
            tp: HttpParserType::HttpBoth,
            strict: true,
            method: HttpMethod::Delete,
            status_code: 0,
            response_status: vec![],
            request_path: String::new(),
            request_url: String::new(),
            fragment: String::new(),
            query_string: String::new(),
            body: String::new(),
            body_size: 0,
            host: String::new(),
            userinfo: String::new(),
            port: 0,
            num_headers: 0,
            last_header_element: LastHeaderType::None,
            headers: vec![],
            should_keep_alive: false,

            upgrade: String::new(),

            http_major: 0,
            http_minor: 0,

            message_begin_cb_called: false,
            headers_complete_cb_called: false,
            message_complete_cb_called: false,
            message_complete_on_eof: false,
            body_is_final: false,
        }
    }
}

pub struct CallbackEmpty;

impl HttpParserCallback for CallbackEmpty {}

pub struct CallbackRegular {
    pub num_messages: uint, // maybe not necessary
    pub messages: Vec<Message>,
    pub currently_parsing_eof: bool,
}

impl Default for CallbackRegular {
    fn default() -> CallbackRegular {
        CallbackRegular {
            num_messages: 0,
            messages: Vec::new(),
            currently_parsing_eof: false,
        }
    }
}

impl HttpParserCallback for CallbackRegular {
    fn on_message_begin(&mut self, parser : &HttpParser) -> Result<i8, &str> {
        self.messages[self.num_messages].message_begin_cb_called = true;
        Ok(0)
    }

    fn on_url(&mut self, parser : &HttpParser, data : &[u8]) -> Result<i8, &str> {
        match str::from_utf8(data) {
            Result::Ok(data_str) => {
                self.messages[self.num_messages].request_url.push_str(
                    data_str);
            },
            _ => panic!("on_url: data is not in utf8 encoding"),
        }
        Ok(0)
    }

    fn on_status(&mut self, parser : &HttpParser, data : &[u8]) -> Result<i8, &str> {
        self.messages[self.num_messages].response_status.push_all(data);
        Ok(0)
    }

    fn on_header_field(&mut self, parser : &HttpParser, data : &[u8]) -> Result<i8, &str> {
        let m : &mut Message = &mut self.messages[self.num_messages];

        if m.last_header_element != LastHeaderType::Field {
            m.num_headers += 1;
            m.headers.push([String::new(), String::new()]);
        }

        match str::from_utf8(data) {
            Result::Ok(data_str) => {
                let i = m.headers.len()-1;
                m.headers[i][0].push_str(data_str);
            },
            _ => panic!("on_header_field: data is not in utf8 encoding"),
        }

        m.last_header_element = LastHeaderType::Field;

        Ok(0)
    }

    fn on_header_value(&mut self, parser : &HttpParser, data : &[u8]) -> Result<i8, &str> {
        let m : &mut Message = &mut self.messages[self.num_messages];

        match str::from_utf8(data) {
            Result::Ok(data_str) => {
                let i = m.headers.len()-1;
                m.headers[i][1].push_str(data_str);
            },
            _ => panic!("on_header_value: data is not in utf8 encoding"),
        }

        m.last_header_element = LastHeaderType::Value;

        Ok(0)
    }

    fn on_headers_complete(&mut self, parser : &HttpParser) -> Result<i8, &str> {
        let m : &mut Message = &mut self.messages[self.num_messages];
        m.method = parser.method;
        m.status_code = parser.status_code;
        m.http_major = parser.http_major;
        m.http_minor = parser.http_minor;
        m.headers_complete_cb_called = true;
        m.should_keep_alive = parser.http_should_keep_alive();
        Ok(0)
    }

    fn on_body(&mut self, parser : &HttpParser, data : &[u8]) -> Result<i8, &str> {
        let m : &mut Message = &mut self.messages[self.num_messages];

        match str::from_utf8(data) {
            Result::Ok(data_str) => {
                m.body.push_str(data_str);
            },
            _ => panic!("on_body: data is not in utf8 encoding"),
        }
        m.body_size += data.len();

        if m.body_is_final {
            panic!("\n\n ** Error http_body_is_final() should return 1 \
                    on last on_body callback call \
                    but it doesn't! **\n\n");
        }

        m.body_is_final = parser.http_body_is_final();
        Ok(0)
    }

    fn on_message_complete(&mut self, parser : &HttpParser) -> Result<i8, &str> {
        {
            let m : &mut Message = &mut self.messages[self.num_messages];

            if m.should_keep_alive != parser.http_should_keep_alive() {
                panic!("\n\n ** Error http_should_keep_alive() should have same \
                        value in both on_message_complete and on_headers_complet \
                        but it doesn't! **\n\n");
            }

            if m.body_size > 0 && parser.http_body_is_final()
                && !m.body_is_final {
                panic!("\n\n ** Error http_body_is_final() should return 1 \
                        on last on_body callback call \
                        but it doesn't! **\n\n");
            }

            m.message_complete_cb_called = true;
            m.message_complete_on_eof = self.currently_parsing_eof;
        }

        self.messages.push(Message{..Default::default()});
        self.num_messages += 1;

        Ok(0)
    }
}

pub struct CallbackDontCall;

impl HttpParserCallback for CallbackDontCall {
    fn on_message_begin(&mut self, parser : &HttpParser) -> Result<i8, &str> {
        panic!("\n\n*** on_message_begin() called on paused parser ***\n\n");
        Ok(0)
    }

    #[allow(unused_variables)]
    fn on_url(&mut self, parser : &HttpParser, data : &[u8],) -> Result<i8, &str> {
        panic!("\n\n*** on_url() called on paused parser ***\n\n");
        Ok(0)
    }

    #[allow(unused_variables)]
    fn on_status(&mut self, parser : &HttpParser, data : &[u8]) -> Result<i8, &str> {
        panic!("\n\n*** on_status() called on paused parser ***\n\n");
        Ok(0)
    }

    #[allow(unused_variables)]
    fn on_header_field(&mut self, parser : &HttpParser, data : &[u8]) -> Result<i8, &str> {
        panic!("\n\n*** on_header_field() called on paused parser ***\n\n");
        Ok(0)
    }

    #[allow(unused_variables)]
    fn on_header_value(&mut self, parser : &HttpParser, data : &[u8]) -> Result<i8, &str> {
        panic!("\n\n*** on_header_value() called on paused parser ***\n\n");
        Ok(0)
    }

    fn on_headers_complete(&mut self, parser : &HttpParser) -> Result<i8, &str> {
        panic!("\n\n*** on_headers_complete() called on paused parser ***\n\n");
        Ok(0)
    }

    #[allow(unused_variables)]
    fn on_body(&mut self, parser : &HttpParser, data : &[u8]) -> Result<i8, &str> {
        panic!("\n\n*** on_body() called on paused parser ***\n\n");
        Ok(0)
    }

    fn on_message_complete(&mut self, parser : &HttpParser) -> Result<i8, &str> {
        panic!("\n\n*** on_message_complete() called on paused parser ***\n\n");
        Ok(0)
    }
}

pub fn print_error(errno: HttpErrno, raw: &str, error_location: u64) {
    println!("\n*** {} ***\n", errno.to_string());

    let len = raw.len();
    let mut this_line = false;
    let mut char_len: u64 = 0;
    let mut error_location_line = 0;
    let mut eof = true;
    for i in range(0, len) {
        if i == (error_location as uint) { this_line = true; }
        match raw.char_at(i) {
            '\r' => {
                char_len = 2;
                print!("\\r");
            },
            '\n' => {
                println!("\\n");

                if this_line {
                    eof = false;
                    break;
                }

                error_location_line = 0;
                continue;
            },
            _ => {
                char_len = 1;
                print!("{}", raw.char_at(i));
            },
        }
        if !this_line { error_location_line += char_len; }
    }

    if eof {
        println!("[eof]");
    }

    for i in range(0, error_location_line as u64) {
        print!(" ");
    }
    println!("^\n\nerror location: {}", error_location);
}

pub fn assert_eq_message(actual: &Message, expected: &Message) {
    assert_eq!(actual.http_major, expected.http_major);
    assert_eq!(actual.http_minor, expected.http_minor);

    if expected.tp == HttpParserType::HttpRequest {
        assert!(actual.method == expected.method);
    } else {
        assert_eq!(actual.status_code, expected.status_code);
        assert_eq!(actual.response_status, expected.response_status);
    }

    assert_eq!(actual.should_keep_alive, expected.should_keep_alive);
    assert_eq!(actual.message_complete_on_eof, expected.message_complete_on_eof);

    assert!(actual.message_begin_cb_called);
    assert!(actual.headers_complete_cb_called);
    assert!(actual.message_complete_cb_called);

    assert_eq!(actual.request_url, expected.request_url);

    // Check URL components; we can't do this w/ CONNECT since it doesn't
    // send us a well-formed URL.
    // TODO add after implementing http_parser_parse_url()

    if expected.body_size > 0 {
        assert_eq!(actual.body_size, expected.body_size);
    } else {
        assert_eq!(actual.body, expected.body);
    }

    assert_eq!(actual.num_headers, expected.num_headers);

    for i in range(0, actual.num_headers) {
        assert_eq!(actual.headers[i as uint][0], expected.headers[i as uint][0]);
        assert_eq!(actual.headers[i as uint][1], expected.headers[i as uint][1]);
    }

    assert_eq!(actual.upgrade, expected.upgrade);
}
<|endoftext|>


<commit_before>use std::cell::RefCell;
use std::collections::HashMap;
use std::fs::copy;
use std::path::Path;
use std::str::FromStr;
use std::rc::Rc;

use iup;
use iup::prelude::*;
use iup::control::{Button, Label, List, Text};
use iup::dialog::{FileDlg};
use iup::element::{Handle};
use iup::led;

use time;
use xml;

use parser::{read_path, write_path};
use property::{Property, PropertyMap};

// Since we need to share mutable state with 'static ui callbacks,
// we clone a refcounted cell for moving into each callback.
type PropertyMapRc = Rc<RefCell<PropertyMap>>;

// LED dialog specification.
static DIALOG: &'static str = include_str!("../resources/ui.led");

// Get an element from handle
fn from_handle<E>(handle: iup::element::Handle) -> E where E: Element {
    E::from_handle(handle).unwrap()
}

// Get an element from named handle
fn from_name<E>(name: &str) -> E where E: Element {
    E::from_handle(E::from_name(name).unwrap()).unwrap()
}

// Data-bind an element to a numeric property value.
//
// Value of the element is set to the current value of the property, and
// changes to element value are written back to the property map.
//
// @param props {PropertyMapRc} a cloned refcounted property map.
//
fn bind<T, E>(elem: &mut E, props: PropertyMapRc, key: &'static str)
    where Property: From<T>,
          T: FromStr + Default,
          E: Element + ValueChangedCb {

    // Remove previous bindings, if any.
    elem.remove_valuechanged_cb();

    if let Some(ref prop) = props.borrow().get(key) {
        elem.set_attrib("VALUE", prop.to_string());
    }
    elem.set_valuechanged_cb(move |(elem,): (E,)| {
        if let Some(ref value) = elem.attrib("VALUE") {
            if let Ok(v) = T::from_str(value) {
                props.borrow_mut().insert(key.to_string(), Property::from(v));
            } else {
                props.borrow_mut().insert(key.to_string(), Property::from(T::default()));
            }
        }
    });
}

// Data-bind an element to a list property value, at given index.
//
// Value of the element is set to the current value of the property at index,
// and changes to element value are written back to the property map.
//
// @param props {PropertyMapRc} a cloned refcounted property map.
//
fn bind_list<E>(elem: &mut E, props: PropertyMapRc, key: &'static str, index: usize)
    where E: Element + ValueChangedCb {

    // Remove previous bindings, if any.
    elem.remove_valuechanged_cb();

    if let Some(&Property::List(ref v)) = props.borrow().get(key) {
        elem.set_attrib("VALUE", v[index].to_string());
    }
    elem.set_valuechanged_cb(move |(elem,): (E,)| {
        if let Some(ref value) = elem.attrib("VALUE") {
            if let Some(&mut Property::List(ref mut v)) = props.borrow_mut().get_mut(key) {
                v[index] = value.to_string();
            }
        }
    });
}

macro_rules! bind_stat {
    ($i:ident, $p:expr, $e:expr) => {
        bind::<f32,_>(&mut from_name::<Text>(stringify!($i)), $p.clone(), $e);
    }
}

macro_rules! bind_skill {
    ($i:ident, $p:expr, $n:expr) => {
        bind_list::<_>(&mut from_handle::<Text>($i), $p.clone(), "SkillPoints", $n);
    }
}

// Creates a pair of (label, text) for inputting numeric values.
// @param title if specified, it is used for the label, otherwise the controls are disabled.
fn make_control_pair(title: Option<&String>) -> (Label, Text) {
    let mut label = Label::new()
        .set_attrib("SIZE", "92x12".to_string())
        .set_attrib("TITLE", "(empty)".to_string());
    let mut text = Text::new_spin()
        .set_attrib("SIZE", "36x12".to_string())
        .set_attrib("SPINMAX", "99".to_string())
        .set_attrib("MASKINT", "0:99".to_string())
        .set_attrib("ALIGNMENT", "ARIGHT".to_string());
    if let Some(ref s) = title {
        label.set_attrib("TITLE", s.to_string());
    } else {
        label.set_attrib("ACTIVE", "NO");
        text.set_attrib("ACTIVE", "NO");
    }
    (label, text)
}

// Data-bind all elements relevant to a party member.
//
// @param props {PropertyMapRc} a cloned refcounted property map.
//
fn bind_member(props: PropertyMapRc) {
    bind_stat!(text_int, props, "Int");
    bind_stat!(text_dex, props, "Dex");
    bind_stat!(text_str, props, "Str");
    bind_stat!(text_occ, props, "Occ");
    bind_stat!(text_per, props, "Per");

    bind_stat!(text_wpn_sword,  props, "WpnSword");
    bind_stat!(text_wpn_short,  props, "WpnShortSword");
    bind_stat!(text_wpn_blunt,  props, "WpnSceptor");
    bind_stat!(text_wpn_cleave, props, "WpnAxe");
    bind_stat!(text_wpn_whip,   props, "WpnWhip");
    bind_stat!(text_wpn_bow,    props, "WpnBow");
    bind_stat!(text_wpn_xbow,   props, "WpnXbow");
    bind_stat!(text_wpn_elixir, props, "WpnElixir");

    if let Some(&mut Property::List(ref mut v)) = props.borrow_mut().get_mut("SkillPoints") {
        while v.len() < 115 {
            v.push("0".to_string())
        }
    }
    if let Some(apt_grid) = Handle::from_named("apt_grid") {
        for i in 1..7 {
            if let Some(child) = apt_grid.child((i - 1) * 2 + 1) {
                bind_skill!(child, props, i);
            }
        }
    }
    if let Some(skill_grid) = Handle::from_named("skill_grid") {
        for i in 7..115 {
            if let Some(child) = skill_grid.child((i - 7) * 2 + 1) {
                bind_skill!(child, props, i);
            }
        }
    }
}

/// Ui entry point.
///
/// Starts by showing a direction selection dialog; after the user selects a directory,
/// the game is loaded from that directory and values bound to the ui elements.
///
pub fn ui_loop() -> Result<(), String> {
    match iup::with_iup(|| {
        // See also led::load(path) to load from a file
        led::load_buffer(DIALOG).unwrap();

        // Select saved game location
        let mut dlg_open = from_name::<FileDlg>("dlg_open");
        let dir = match dlg_open.popup(DialogPos::CenterParent, DialogPos::CenterParent) {
            Ok(..) => match dlg_open.attrib("STATUS") {
                Some(ref s) if s == "0" => {
                    dlg_open.attrib("VALUE").unwrap()
                },
                _ => return Err("File selection cancelled.".to_string())
            },
            _ => return Err("File selection failed.".to_string())
        };

        // Read game and party member files
        let game = Rc::new(RefCell::new({
            let path = Path::new(&dir).join("Game.txt");
            match read_path(path.as_path()) {
                Ok(v) => v,
                Err(e) => {
                    return Err(format!("Cannot read {:?}: {}", path, e))
                }
            }
        }));
        let party = Rc::new(RefCell::new({
            let mut members: Vec<PropertyMapRc> = Vec::new();
            if let Some(&Property::String(ref ids)) = game.borrow().get("PartyIDs") {
                for id in ids.split(",") {
                    if id == "0" {
                        continue;
                    }
                    let path = Path::new(&dir).join("Party".to_string() + id + ".txt");
                    match read_path(path.as_path()) {
                        Ok(v) => members.push(Rc::new(RefCell::new(v))),
                        Err(e) => {
                            return Err(format!("Cannot read {:?}: {}", path, e))
                        }
                    };
                }
            }
            members
        }));

        let mut text_emeralds = from_name::<Text>("text_emeralds");
        bind::<u32,_>(&mut text_emeralds, game.clone(), "Emeralds");

        let mut list_party = from_name::<List>("list_party");
        let mut list_party_items: Vec<String> = Vec::new();
        for member in party.borrow().iter() {
            if let Some(&Property::String(ref name)) = member.borrow().get("Name") {
                if let Some(&Property::Float(level)) = member.borrow().get("Level") {
                    list_party_items.push(format!("{} ({})", name, level));
                }
            }
        }

        let skills = load_skills();
        if let Some(mut apt_grid) = Handle::from_named("apt_grid") {
            while let Some(mut child) = apt_grid.child(0) {
                child.detach().destroy();
            }
            for i in 1..7 {
                let (label, text) = make_control_pair(skills.get(&i).map(|ref x| &(x.name)));
                apt_grid.append(label).unwrap();
                apt_grid.append(text).unwrap();
            }
        }
        if let Some(mut skill_grid) = Handle::from_named("skill_grid") {
            while let Some(mut child) = skill_grid.child(0) {
                child.detach().destroy();
            }
            for i in 7..115 {
                let (label, text) = make_control_pair(skills.get(&i).map(|ref x| &(x.name)));
                skill_grid.append(label).unwrap();
                skill_grid.append(text).unwrap();
            }
        }

        let party_clone = party.clone();
        list_party.set_items(list_party_items);
        list_party.set_action(move |(_, _, i, _)| {
            let member = party_clone.borrow()[i as usize - 1].clone();
            bind_member(member);
        });
        if let Some(&ref member) = party.borrow().first() {
            bind_member(member.clone());
        }

        // Write game and party member files on save
        let mut button_save = from_name::<Button>("button_save");
        {
            let game_clone = game.clone();
            let party_clone = party.clone();
            button_save.set_action(move |_| {
                let timestamp = time::strftime("%FT%H.%M.%SZ.txt", &time::now_utc()).unwrap();
                let path = Path::new(&dir).join("Game.txt");
                copy(path.as_path(), path.with_extension(&timestamp)).unwrap();
                write_path(path.as_path(), &game_clone.borrow()).unwrap();
                for member in party_clone.borrow().iter() {

                    // Validate
                    let mut combat_skills: Vec<String> = Vec::new();
                    let mut spell_skills: Vec<String> = Vec::new();
                    if let Some(&Property::List(ref v)) = member.borrow().get("SkillPoints") {
                        let spell_grade = {
                            if let Some(&Property::Float(int_mod)) = member.borrow().get("Int") {
                                if let Some(&Property::Float(occ_mod)) = member.borrow().get("Occ") {
                                    int_mod + occ_mod + 20f32
                                } else {
                                    0f32
                                }
                            } else {
                                0f32
                            }
                        };
                        for (i, ref val) in v.iter().enumerate() {
                            match val.parse::<u32>() {
                                // Combat skills (1-3)
                                Ok(n) if i >= 7 && i <= 61 && n > 0 => {
                                    if let Some(ref skill) = skills.get(&i) {
                                        combat_skills.push(skill.internal.to_string())
                                    }
                                }
                                // Spell skills (1)
                                Ok(n) if i >= 62 && i <= 72 && (n > 0 || spell_grade >= 21f32) => {
                                    if let Some(ref skill) = skills.get(&i) {
                                        spell_skills.push(skill.internal.to_string())
                                    }
                                }
                                // Spell skills (2)
                                Ok(n) if i >= 73 && i <= 86 && (n > 0 || spell_grade >= 26f32) => {
                                    if let Some(ref skill) = skills.get(&i) {
                                        spell_skills.push(skill.internal.to_string())
                                    }
                                }
                                // Spell skills (3)
                                Ok(n) if i >= 87 && i <= 114 && (n > 0 || spell_grade >= 32f32) => {
                                    if let Some(ref skill) = skills.get(&i) {
                                        spell_skills.push(skill.internal.to_string())
                                    }
                                }
                                _ => {}
                            }
                        }
                    }
                    if let Some(&mut Property::List(ref mut v)) = member.borrow_mut().get_mut("CombatSelects") {
                        v.retain(|ref x| combat_skills.contains(&x));
                    }
                    if let Some(&mut Property::List(ref mut v)) = member.borrow_mut().get_mut("SpellFavorites") {
                        v.retain(|ref x| spell_skills.contains(&x));
                    }
                    member.borrow_mut().insert("CombatSkills".to_string(), Property::List(combat_skills));
                    member.borrow_mut().insert("SpellSkills".to_string(), Property::List(spell_skills));

                    if let Some(&Property::String(ref id)) = member.borrow().get("PartyID") {
                        let path = Path::new(&dir).join("Party".to_string() + id + ".txt");
                        copy(path.as_path(), path.with_extension(&timestamp)).unwrap();
                        write_path(path.as_path(), &member.borrow()).unwrap();
                    }
                }
            });
        }
        let mut button_close = from_name::<Button>("button_close");
        button_close.set_action(|_| {
            CallbackReturn::Close
        });

        let mut dlg = from_name::<Dialog>("dlg");
        dlg.show()

    }) {
        Err(iup::InitError::UserError(s)) => Err(s),
        Err(e) => Err(format!("{:?}", e)),
        _ => Ok(())
    }
}

struct Skill {
    name: String,
    internal: String,
}

fn load_skills() -> HashMap<usize, Skill> {
    let mut skills: HashMap<usize, Skill> = HashMap::with_capacity(115);
    if let Ok(elem) = include_str!("../resources/skills.xml").parse::<xml::Element>() {
        for child in elem.get_children("skill", None) {
            let name = child.get_children("name", None).nth(0).map(|ref e| e.content_str());
            let desc = child.get_children("description", None).nth(0).map(|ref e| e.content_str());
            match name {
                Some(ref name) if !name.is_empty() => {
                    match desc {
                        Some(ref desc) if !desc.is_empty() => {
                            let id = child.attributes
                                .get(&("number".to_string(), None)).unwrap()
                                .parse::<usize>()
                                .unwrap();
                            let internal = child.attributes
                                .get(&("spritename".to_string(), None)).unwrap_or(name);
                            skills.insert(id, Skill {
                                name: name.to_owned(),
                                internal: internal.to_owned(),
                            });
                        }
                        _ => {}
                    }
                }
                _ => {}
            }
        }
    }
    skills
}
<commit_msg>Fix skills not being selectable<commit_after>use std::cell::RefCell;
use std::collections::HashMap;
use std::fs::copy;
use std::path::Path;
use std::str::FromStr;
use std::rc::Rc;

use iup;
use iup::prelude::*;
use iup::control::{Button, Label, List, Text};
use iup::dialog::{FileDlg};
use iup::element::{Handle};
use iup::led;

use time;
use xml;

use parser::{read_path, write_path};
use property::{Property, PropertyMap};

// Since we need to share mutable state with 'static ui callbacks,
// we clone a refcounted cell for moving into each callback.
type PropertyMapRc = Rc<RefCell<PropertyMap>>;

// LED dialog specification.
static DIALOG: &'static str = include_str!("../resources/ui.led");

// Get an element from handle
fn from_handle<E>(handle: iup::element::Handle) -> E where E: Element {
    E::from_handle(handle).unwrap()
}

// Get an element from named handle
fn from_name<E>(name: &str) -> E where E: Element {
    E::from_handle(E::from_name(name).unwrap()).unwrap()
}

// Data-bind an element to a numeric property value.
//
// Value of the element is set to the current value of the property, and
// changes to element value are written back to the property map.
//
// @param props {PropertyMapRc} a cloned refcounted property map.
//
fn bind<T, E>(elem: &mut E, props: PropertyMapRc, key: &'static str)
    where Property: From<T>,
          T: FromStr + Default,
          E: Element + ValueChangedCb {

    // Remove previous bindings, if any.
    elem.remove_valuechanged_cb();

    if let Some(ref prop) = props.borrow().get(key) {
        elem.set_attrib("VALUE", prop.to_string());
    }
    elem.set_valuechanged_cb(move |(elem,): (E,)| {
        if let Some(ref value) = elem.attrib("VALUE") {
            if let Ok(v) = T::from_str(value) {
                props.borrow_mut().insert(key.to_string(), Property::from(v));
            } else {
                props.borrow_mut().insert(key.to_string(), Property::from(T::default()));
            }
        }
    });
}

// Data-bind an element to a list property value, at given index.
//
// Value of the element is set to the current value of the property at index,
// and changes to element value are written back to the property map.
//
// @param props {PropertyMapRc} a cloned refcounted property map.
//
fn bind_list<E>(elem: &mut E, props: PropertyMapRc, key: &'static str, index: usize)
    where E: Element + ValueChangedCb {

    // Remove previous bindings, if any.
    elem.remove_valuechanged_cb();

    if let Some(&Property::List(ref v)) = props.borrow().get(key) {
        elem.set_attrib("VALUE", v[index].to_string());
    }
    elem.set_valuechanged_cb(move |(elem,): (E,)| {
        if let Some(ref value) = elem.attrib("VALUE") {
            if let Some(&mut Property::List(ref mut v)) = props.borrow_mut().get_mut(key) {
                v[index] = value.to_string();
            }
        }
    });
}

macro_rules! bind_stat {
    ($i:ident, $p:expr, $e:expr) => {
        bind::<f32,_>(&mut from_name::<Text>(stringify!($i)), $p.clone(), $e);
    }
}

macro_rules! bind_skill {
    ($i:ident, $p:expr, $n:expr) => {
        bind_list::<_>(&mut from_handle::<Text>($i), $p.clone(), "SkillPoints", $n);
    }
}

// Creates a pair of (label, text) for inputting numeric values.
// @param title if specified, it is used for the label, otherwise the controls are disabled.
fn make_control_pair(title: Option<&String>) -> (Label, Text) {
    let mut label = Label::new()
        .set_attrib("SIZE", "92x12".to_string())
        .set_attrib("TITLE", "(empty)".to_string());
    let mut text = Text::new_spin()
        .set_attrib("SIZE", "36x12".to_string())
        .set_attrib("SPINMAX", "99".to_string())
        .set_attrib("MASKINT", "0:99".to_string())
        .set_attrib("ALIGNMENT", "ARIGHT".to_string());
    if let Some(ref s) = title {
        label.set_attrib("TITLE", s.to_string());
    } else {
        label.set_attrib("ACTIVE", "NO");
        text.set_attrib("ACTIVE", "NO");
    }
    (label, text)
}

// Data-bind all elements relevant to a party member.
//
// @param props {PropertyMapRc} a cloned refcounted property map.
//
fn bind_member(props: PropertyMapRc) {
    bind_stat!(text_int, props, "Int");
    bind_stat!(text_dex, props, "Dex");
    bind_stat!(text_str, props, "Str");
    bind_stat!(text_occ, props, "Occ");
    bind_stat!(text_per, props, "Per");

    bind_stat!(text_wpn_sword,  props, "WpnSword");
    bind_stat!(text_wpn_short,  props, "WpnShortSword");
    bind_stat!(text_wpn_blunt,  props, "WpnSceptor");
    bind_stat!(text_wpn_cleave, props, "WpnAxe");
    bind_stat!(text_wpn_whip,   props, "WpnWhip");
    bind_stat!(text_wpn_bow,    props, "WpnBow");
    bind_stat!(text_wpn_xbow,   props, "WpnXbow");
    bind_stat!(text_wpn_elixir, props, "WpnElixir");

    if let Some(&mut Property::List(ref mut v)) = props.borrow_mut().get_mut("SkillPoints") {
        while v.len() < 115 {
            v.push("0".to_string())
        }
    }
    if let Some(apt_grid) = Handle::from_named("apt_grid") {
        for i in 1..7 {
            if let Some(child) = apt_grid.child((i - 1) * 2 + 1) {
                bind_skill!(child, props, i);
            }
        }
    }
    if let Some(skill_grid) = Handle::from_named("skill_grid") {
        for i in 7..115 {
            if let Some(child) = skill_grid.child((i - 7) * 2 + 1) {
                bind_skill!(child, props, i);
            }
        }
    }
}

/// Ui entry point.
///
/// Starts by showing a direction selection dialog; after the user selects a directory,
/// the game is loaded from that directory and values bound to the ui elements.
///
pub fn ui_loop() -> Result<(), String> {
    match iup::with_iup(|| {
        // See also led::load(path) to load from a file
        led::load_buffer(DIALOG).unwrap();

        // Select saved game location
        let mut dlg_open = from_name::<FileDlg>("dlg_open");
        let dir = match dlg_open.popup(DialogPos::CenterParent, DialogPos::CenterParent) {
            Ok(..) => match dlg_open.attrib("STATUS") {
                Some(ref s) if s == "0" => {
                    dlg_open.attrib("VALUE").unwrap()
                },
                _ => return Err("File selection cancelled.".to_string())
            },
            _ => return Err("File selection failed.".to_string())
        };

        // Read game and party member files
        let game = Rc::new(RefCell::new({
            let path = Path::new(&dir).join("Game.txt");
            match read_path(path.as_path()) {
                Ok(v) => v,
                Err(e) => {
                    return Err(format!("Cannot read {:?}: {}", path, e))
                }
            }
        }));
        let party = Rc::new(RefCell::new({
            let mut members: Vec<PropertyMapRc> = Vec::new();
            if let Some(&Property::String(ref ids)) = game.borrow().get("PartyIDs") {
                for id in ids.split(",") {
                    if id == "0" {
                        continue;
                    }
                    let path = Path::new(&dir).join("Party".to_string() + id + ".txt");
                    match read_path(path.as_path()) {
                        Ok(v) => members.push(Rc::new(RefCell::new(v))),
                        Err(e) => {
                            return Err(format!("Cannot read {:?}: {}", path, e))
                        }
                    };
                }
            }
            members
        }));

        let mut text_emeralds = from_name::<Text>("text_emeralds");
        bind::<u32,_>(&mut text_emeralds, game.clone(), "Emeralds");

        let mut list_party = from_name::<List>("list_party");
        let mut list_party_items: Vec<String> = Vec::new();
        for member in party.borrow().iter() {
            if let Some(&Property::String(ref name)) = member.borrow().get("Name") {
                if let Some(&Property::Float(level)) = member.borrow().get("Level") {
                    list_party_items.push(format!("{} ({})", name, level));
                }
            }
        }

        let skills = load_skills();
        if let Some(mut apt_grid) = Handle::from_named("apt_grid") {
            while let Some(mut child) = apt_grid.child(0) {
                child.detach().destroy();
            }
            for i in 1..7 {
                let (label, text) = make_control_pair(skills.get(&i).map(|ref x| &(x.name)));
                apt_grid.append(label).unwrap();
                apt_grid.append(text).unwrap();
            }
        }
        if let Some(mut skill_grid) = Handle::from_named("skill_grid") {
            while let Some(mut child) = skill_grid.child(0) {
                child.detach().destroy();
            }
            for i in 7..115 {
                let (label, text) = make_control_pair(skills.get(&i).map(|ref x| &(x.name)));
                skill_grid.append(label).unwrap();
                skill_grid.append(text).unwrap();
            }
        }

        let party_clone = party.clone();
        list_party.set_items(list_party_items);
        list_party.set_action(move |(_, _, i, _)| {
            let member = party_clone.borrow()[i as usize - 1].clone();
            bind_member(member);
        });
        if let Some(&ref member) = party.borrow().first() {
            bind_member(member.clone());
        }

        // Write game and party member files on save
        let mut button_save = from_name::<Button>("button_save");
        {
            let game_clone = game.clone();
            let party_clone = party.clone();
            button_save.set_action(move |_| {
                let timestamp = time::strftime("%FT%H.%M.%SZ.txt", &time::now_utc()).unwrap();
                let path = Path::new(&dir).join("Game.txt");
                copy(path.as_path(), path.with_extension(&timestamp)).unwrap();
                write_path(path.as_path(), &game_clone.borrow()).unwrap();
                for member in party_clone.borrow().iter() {

                    // Validate
                    let mut combat_skills: Vec<String> = Vec::new();
                    let mut spell_skills: Vec<String> = Vec::new();
                    if let Some(&Property::List(ref v)) = member.borrow().get("SkillPoints") {
                        let spell_grade = {
                            if let Some(&Property::Float(int_mod)) = member.borrow().get("Int") {
                                if let Some(&Property::Float(occ_mod)) = member.borrow().get("Occ") {
                                    int_mod + occ_mod + 20f32
                                } else {
                                    0f32
                                }
                            } else {
                                0f32
                            }
                        };
                        for (i, ref val) in v.iter().enumerate() {
                            match val.parse::<u32>() {
                                // Combat skills (1-3)
                                Ok(n) if i >= 7 && i <= 61 && n > 0 => {
                                    if let Some(ref skill) = skills.get(&i) {
                                        combat_skills.push(skill.internal.to_string())
                                    }
                                }
                                // Spell skills (1)
                                Ok(n) if i >= 62 && i <= 72 && (n > 0 || spell_grade >= 21f32) => {
                                    if let Some(ref skill) = skills.get(&i) {
                                        spell_skills.push(skill.internal.to_string())
                                    }
                                }
                                // Spell skills (2)
                                Ok(n) if i >= 73 && i <= 86 && (n > 0 || spell_grade >= 26f32) => {
                                    if let Some(ref skill) = skills.get(&i) {
                                        spell_skills.push(skill.internal.to_string())
                                    }
                                }
                                // Spell skills (3)
                                Ok(n) if i >= 87 && i <= 114 && (n > 0 || spell_grade >= 32f32) => {
                                    if let Some(ref skill) = skills.get(&i) {
                                        spell_skills.push(skill.internal.to_string())
                                    }
                                }
                                _ => {}
                            }
                        }
                    }
                    if let Some(&mut Property::List(ref mut v)) = member.borrow_mut().get_mut("CombatSelects") {
                        v.retain(|ref x| combat_skills.contains(&x));
                        while v.len() < 3 {
                            v.push("Empty".to_string());
                        }
                    }
                    if let Some(&mut Property::List(ref mut v)) = member.borrow_mut().get_mut("SpellFavorites") {
                        v.retain(|ref x| spell_skills.contains(&x));
                        while v.len() < 10 {
                            v.push("".to_string());
                        }
                    }
                    member.borrow_mut().insert("CombatSkills".to_string(), Property::List(combat_skills));
                    member.borrow_mut().insert("SpellSkills".to_string(), Property::List(spell_skills));

                    if let Some(&Property::String(ref id)) = member.borrow().get("PartyID") {
                        let path = Path::new(&dir).join("Party".to_string() + id + ".txt");
                        copy(path.as_path(), path.with_extension(&timestamp)).unwrap();
                        write_path(path.as_path(), &member.borrow()).unwrap();
                    }
                }
            });
        }
        let mut button_close = from_name::<Button>("button_close");
        button_close.set_action(|_| {
            CallbackReturn::Close
        });

        let mut dlg = from_name::<Dialog>("dlg");
        dlg.show()

    }) {
        Err(iup::InitError::UserError(s)) => Err(s),
        Err(e) => Err(format!("{:?}", e)),
        _ => Ok(())
    }
}

struct Skill {
    name: String,
    internal: String,
}

fn load_skills() -> HashMap<usize, Skill> {
    let mut skills: HashMap<usize, Skill> = HashMap::with_capacity(115);
    if let Ok(elem) = include_str!("../resources/skills.xml").parse::<xml::Element>() {
        for child in elem.get_children("skill", None) {
            let name = child.get_children("name", None).nth(0).map(|ref e| e.content_str());
            let desc = child.get_children("description", None).nth(0).map(|ref e| e.content_str());
            match name {
                Some(ref name) if !name.is_empty() => {
                    match desc {
                        Some(ref desc) if !desc.is_empty() => {
                            let id = child.attributes
                                .get(&("number".to_string(), None)).unwrap()
                                .parse::<usize>()
                                .unwrap();
                            let internal = child.attributes
                                .get(&("spritename".to_string(), None)).unwrap_or(name);
                            skills.insert(id, Skill {
                                name: name.to_owned(),
                                internal: internal.to_owned(),
                            });
                        }
                        _ => {}
                    }
                }
                _ => {}
            }
        }
    }
    skills
}
<|endoftext|>