<commit_before>use slack_api as slack;
use tokio_tungstenite as tungstenite;

use futures_util::{SinkExt, StreamExt};

use tokio::sync::mpsc;

use serde::Deserialize;
use serde_json::json;

use atomic_counter::AtomicCounter;
use atomic_counter::RelaxedCounter;

use std::sync::Arc;
use std::default::Default;
use std::env;
use std::result::Result;

use chrono::prelude::{Utc, DateTime};
use std::time::Duration;
use humantime::format_duration;

// SQlite worker thread
use std::thread;
use futures::executor::block_on_stream;

use rusqlite as rs;
use std::path::Path;
use tokio::sync::oneshot;

// Use of a mod or pub mod is not actually necessary.
pub mod build_info {
   // The file has been placed there by the build script.
   include!(concat!(env!("OUT_DIR"), "/built.rs"));
}

// Type alias for msg_id
type MsgId = Arc<RelaxedCounter>;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Shared integer counter for message ids
    let msg_id = Arc::new(RelaxedCounter::new(0));

    // Uptime of program start
    let start_time: DateTime<Utc> = Utc::now();

    let token = env::var("SLACK_API_TOKEN").map_err(|_| "SLACK_API_TOKEN env var must be set")?;
    let client = slack::default_client().map_err(|e| format!("Could not get default_client, {:?}", e))?;

    // Post a message
    let msg = slack::chat::PostMessageRequest {
        channel: "karmator-devs",
        text: "Pogger, i'm starting up!",
        as_user: Some(true),
        ..Default::default()
    };
    slack::chat::post_message(&client, &token, &msg).await;

    // Work to establish the WS connection
    let response = slack::rtm::connect(&client, &token).await.map_err(|e| format!("Control - {:?}", e))?;
    println!("Control - Got an ok reply");

    let ws_url = response.url.ok_or(format!("Control - \tNo Ws url"))?;
    println!("Control - \tGot a WS url: {:?}", ws_url);

    let (ws_stream, _) = tungstenite::connect_async(ws_url).await.map_err(|e| format!("Control - \t\t{:?}", e))?;
    println!("Control - \t\tWS connection established");

    // Setup the mpsc channel
    let (tx, mut rx) = mpsc::channel(32);

    // Split the stream
    let (mut ws_write, mut ws_read) = ws_stream.split();

    // Setup the mpsc channel for sqlite and spawn the sqlite worker thread
    // TODO: define a data format for asking the db to query something on your behalf, for now
    // pass in the raw query string?
    let (sql_tx, sql_rx) = mpsc::channel(32);

    let sql_worker = thread::spawn(move || {
        println!("Sql Worker - Launching");
        process_queries(sql_rx);
        println!("Sql Worker - Exiting");
    });

    // Spawn the inbound ws stream processor
    let inbound = tokio::spawn(async move {
        loop {
            if let Some(Ok(ws_msg)) = ws_read.next().await {
                let msg_id = msg_id.clone();
                let tx2 = tx.clone();
                let sql_tx2 = sql_tx.clone();

                tokio::spawn(async move {
                    process_inbound_message(msg_id, ws_msg, tx2, sql_tx2, start_time).await;
                });

            }
        }
    });

    // Spawn outbound ws processor
    // TODO: need to make sure to wait on sending till we recieve the hello event
    // could be a oneshot or some sort of flag which it waits till inbound has
    // gotten the hello
    let outbound = tokio::spawn(async move {
        loop {
            while let Some(message) = rx.recv().await {
                println!("Outbound - {:?}", message);

                // TODO: present some way to do plain vs fancy message, and if
                // fancy do a webapi post, otherwise dump into the WS
                //
                // TODO: look into tracking the sent message with a confirmation
                // that it was sent (via msg id) and if it fails, resend with
                // backoff
                //
                // TODO: find a way to handle the ping/pong cycle and monitor
                ws_write.send(message).await;
            }
        }
    });

    // Wait till either exits (error) then begin recovery
    match tokio::try_join!(inbound, outbound) {
        Ok((first, second)) => println!("Control - \t\t\tBoth exited fine"),
        Err(err) => println!("Control - \t\t\tSomething failed: {:?}", err),
    }

    // The sql_tx got moved into the inbound tokio async, so when that dies....
    let res = sql_worker.join();
    println!("Control - \t\t\tSql worker: {:?}", res);

    Ok(())
}

fn process_queries(
    sql_rx: mpsc::Receiver<(runQuery, Option<oneshot::Sender<resQuery>>)>
) {
    let mut block_sql_rx = block_on_stream(sql_rx);

    //let filename = "db.sqlite";
    //let conn = rs::Connection::open_with_flags(
    //    Path::new(filename),
    //    rs::OpenFlags::SQLITE_OPEN_READ_WRITE
    //).expect(&format!("Connection error: {}", filename));
    let conn = rs::Connection::open_in_memory().expect("Failure");

    // Just create a db table
    let res = conn.execute(
        "CREATE TABLE votes (
            for TEXT NOT NULL,
            what TEXT NOT NULL
        )",
        rs::params![],
    );
    println!("Sql Worker - Table - {:?}", res);

    // Listen for inbound query
    while let Some((query, res_tx)) = block_sql_rx.next() {
        match query {
            runQuery::Query => {
                println!("Sql Worker - Query");
                let mut stmt = conn.prepare("SELECT * FROM votes").unwrap();

                let mut vote_iter = stmt.query_map(rs::params![], |row| {
                    let a: String = row.get(0).unwrap();
                    let b: String = row.get(1).unwrap();

                    Ok(format!("{} {}", a, b))
                }).unwrap();

                // Result
                if let Some(Ok(res)) = vote_iter.next() {
                    println!("Sql Worker - Query - {:?}", res);
                    res_tx.unwrap().send(resQuery::Query(res));
                } else {
                    println!("Sql Worker - Query - Fail");
                }
            },
            runQuery::Insert(f, w) => {
                println!("Sql Worker - Insert");
                let mut stmt = conn.prepare("INSERT INTO votes (for, what) VALUES (?, ?)").unwrap();
                let res = stmt.execute(&[f, w]);

                println!("Sql Worker - Insert - {:?}", res);
                res_tx.unwrap().send(resQuery::Insert(res.unwrap()));
            }
        }
    }
}

#[derive(Debug)]
enum runQuery {
    Insert(String, String),
    Query,
}

#[derive(Debug)]
enum resQuery {
    Insert(usize),
    Query(String),
}

async fn process_inbound_message(
    msg_id: MsgId,
    msg: tungstenite::tungstenite::Message,
    mut tx: mpsc::Sender<tungstenite::tungstenite::Message>,
    mut sql_tx: mpsc::Sender<(runQuery, Option<oneshot::Sender<resQuery>>)>,
    start_time: DateTime<Utc>
) -> Result<(), Box<dyn std::error::Error>>
{
    let id = msg_id.inc();

    println!("Inbound - id = {:?}", id);

    // Parse incoming message
    let raw_msg = match msg {
        tungstenite::tungstenite::Message::Text(x) => Some(x),
        _ => {
            println!("Inbound - \tUnsupported ws message type - {:?}", msg);
            None
        },
    };
    println!("Inbound - raw event = {:?}", raw_msg);

    if let Some(e) = raw_msg.and_then(parse_event) {
        match e {
            Event::UserEvent(event) => {
                // Check if its a message/certain string, if so, reply
                match event {
                    // !version
                    UserEvent::Message {
                        channel: Some(c),
                        text: t,
                        subtype: _,
                        hidden: _,
                        user: u,
                        ts: _,
                    } if c == "CAF6S4TRT".to_string() => {
                        if t == "!version".to_string() {
                            let ver = build_info::PKG_VERSION;
                            let dat = build_info::BUILT_TIME_UTC;
                            let sha = build_info::GIT_COMMIT_HASH.unwrap_or("Unknown");

                            let ws_msg = json!({
                                "id": id,
                                "type": "message",
                                "channel": "CAF6S4TRT",
                                "text": format!("<@{}>: Cargo Version: {} - Build Date: {}, - Build SHA: {}", u, ver, dat, sha),
                            }).to_string();

                            tx.send(tungstenite::tungstenite::Message::from(ws_msg)).await;
                            println!("Inbound - \t\t!version");

                        } else if t == "!uptime".to_string() {
                            let end_time: DateTime<Utc> = Utc::now();
                            let durt = end_time.signed_duration_since(start_time).to_std().unwrap_or(
                                Duration::from_secs(3122064000));

                            let ws_msg = json!({
                                "id": id,
                                "type": "message",
                                "channel": "CAF6S4TRT",
                                "text": format!("<@{}>: {}", u, format_duration(durt).to_string()),
                            }).to_string();

                            tx.send(tungstenite::tungstenite::Message::from(ws_msg)).await;
                            println!("Inbound - \t\t!uptime");

                        } else if t == "!help".to_string() {
                            let help = "Available commands: !uptime !version !github !sidevotes !karma !givers !rank !ranksidevote";
                            let ws_msg = json!({
                                "id": id,
                                "type": "message",
                                "channel": "CAF6S4TRT",
                                "text": format!("<@{}>: {}", u, help),
                            }).to_string();

                            tx.send(tungstenite::tungstenite::Message::from(ws_msg)).await;
                            println!("Inbound - \t\t!help");

                        } else if t == "!github".to_string() {
                            let github = "Github repo: https://github.com/pharaun/Karmator";
                            let ws_msg = json!({
                                "id": id,
                                "type": "message",
                                "channel": "CAF6S4TRT",
                                "text": format!("<@{}>: {}", u, github),
                            }).to_string();

                            tx.send(tungstenite::tungstenite::Message::from(ws_msg)).await;
                            println!("Inbound - \t\t!github");

                        } else if t == "!run_query".to_string() {
                            let ws_msg = json!({
                                "id": id,
                                "type": "message",
                                "channel": "CAF6S4TRT",
                                "text": format!("<@{}>: Ok running query", u),
                            }).to_string();
                            tx.send(tungstenite::tungstenite::Message::from(ws_msg)).await;

                            // Dummy get/send stuff
                            let (tx1, rx1) = oneshot::channel();
                            sql_tx.send((
                                runQuery::Insert("hi".to_string(), "bye".to_string()),
                                Some(tx1)
                            )).await;

                            let (tx2, rx2) = oneshot::channel();
                            sql_tx.send((
                                runQuery::Query,
                                Some(tx2)
                            )).await;

                            // Await for the results
                            let res1 = rx1.await;
                            let ws_msg = json!({
                                "id": id,
                                "type": "message",
                                "channel": "CAF6S4TRT",
                                "text": format!("<@{}>: insert: {:?}", u, res1),
                            }).to_string();
                            tx.send(tungstenite::tungstenite::Message::from(ws_msg)).await;

                            let res2 = rx2.await;
                            let ws_msg = json!({
                                "id": id,
                                "type": "message",
                                "channel": "CAF6S4TRT",
                                "text": format!("<@{}>: query: {:?}", u, res2),
                            }).to_string();
                            tx.send(tungstenite::tungstenite::Message::from(ws_msg)).await;

                            println!("Inbound - \t\t!run_sql_query");
                        }
                    },

                    _ => println!("Inbound - \t\tNo action taken"),
                }
            },

            Event::SystemControl(sc) => (),
            Event::MessageControl(mc) => (),
        }
    }
    Ok(())
}

fn parse_event(s: String) -> Option<Event> {
    // Return User Events first, then the other two
    serde_json::from_str(&s).and_then(|ue| {
        println!("Inbound - \tUser Event = {:?}", ue);
        Ok(Event::UserEvent(ue))
    }).or_else(|_| {
        serde_json::from_str(&s).and_then(|sc| {
            println!("Inbound - \tSystem Control = {:?}", sc);
            Ok(Event::SystemControl(sc))
        }).or_else(|_| {
            serde_json::from_str(&s).and_then(|mc| {
                println!("Inbound - \tMessage Control = {:?}", mc);
                Ok(Event::MessageControl(mc))
            }).or_else(|x| {
                // TODO: for now print to stderr what didn't get deserialized
                // later can have config option to log to file the error or not
                eprintln!("Inbound - \tFail parse = {:?}", s);
                Err(x)
            })
        })
    }).ok()
}

#[derive(Debug)]
enum Event {
    UserEvent(UserEvent),
    SystemControl(SystemControl),
    MessageControl(MessageControl),
}

#[derive(Debug, Deserialize)]
#[serde(tag = "type")]
#[serde(rename_all = "snake_case")]
enum UserEvent {
    // These events are what plugins will care for:
    //
    // Slack messages
    // TODO: more involved here for now basics
    Message {
        subtype: Option<String>,
        hidden: Option<bool>,
        channel: Option<String>,
        user: String,
        text: String,
        ts: String,
    },

    // TODO: consider looking at slack_api for types to reuse here
    ReactionAdded {
        user: String,
        reaction: String,
        item_user: Option<String>,
        // item: ListResponseItem
        event_ts: String,
    },
    ReactionRemoved {
        user: String,
        reaction: String,
        item_user: Option<String>,
        // item: ListResponseItem
        event_ts: String,
    },
}

#[derive(Debug, Deserialize)]
#[serde(tag = "type")]
#[serde(rename_all = "snake_case")]
enum SystemControl {
    // First message upon successful connection establishment
    Hello,

    // Client should reconnect after getting such message
    Goodbye,

    // Reply to Ping = { type: ping, id: num }
    Pong { reply_to: usize },
}

#[derive(Debug, Deserialize)]
#[serde(untagged)]
enum MessageControl {
    // Reply to message sent
    MessageSent {
        ok: bool,
        reply_to: usize,
        text: String,
        ts: String
    },

    // Reply to failed message sent
    MessageError {
        ok: bool,
        reply_to: usize,
        error: ErrorDetail,
    },
}

#[derive(Debug, Deserialize)]
struct ErrorDetail {
    code: usize,
    msg: String,
}
<commit_msg>Cleanup the logging a little more since the raw events are pretty big for some so prune em for now<commit_after>use slack_api as slack;
use tokio_tungstenite as tungstenite;

use futures_util::{SinkExt, StreamExt};

use tokio::sync::mpsc;

use serde::Deserialize;
use serde_json::json;

use atomic_counter::AtomicCounter;
use atomic_counter::RelaxedCounter;

use std::sync::Arc;
use std::default::Default;
use std::env;
use std::result::Result;

use chrono::prelude::{Utc, DateTime};
use std::time::Duration;
use humantime::format_duration;

// SQlite worker thread
use std::thread;
use futures::executor::block_on_stream;

use rusqlite as rs;
use std::path::Path;
use tokio::sync::oneshot;

// Use of a mod or pub mod is not actually necessary.
pub mod build_info {
   // The file has been placed there by the build script.
   include!(concat!(env!("OUT_DIR"), "/built.rs"));
}

// Type alias for msg_id
type MsgId = Arc<RelaxedCounter>;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Shared integer counter for message ids
    let msg_id = Arc::new(RelaxedCounter::new(0));

    // Uptime of program start
    let start_time: DateTime<Utc> = Utc::now();

    let token = env::var("SLACK_API_TOKEN").map_err(|_| "SLACK_API_TOKEN env var must be set")?;
    let client = slack::default_client().map_err(|e| format!("Could not get default_client, {:?}", e))?;

    // Post a message
    let msg = slack::chat::PostMessageRequest {
        channel: "karmator-devs",
        text: "Pogger, i'm starting up!",
        as_user: Some(true),
        ..Default::default()
    };
    slack::chat::post_message(&client, &token, &msg).await;

    // Work to establish the WS connection
    let response = slack::rtm::connect(&client, &token).await.map_err(|e| format!("Control - {:?}", e))?;
    println!("Control - Got an ok reply");

    let ws_url = response.url.ok_or(format!("Control - \tNo Ws url"))?;
    println!("Control - \tGot a WS url: {:?}", ws_url);

    let (ws_stream, _) = tungstenite::connect_async(ws_url).await.map_err(|e| format!("Control - \t\t{:?}", e))?;
    println!("Control - \t\tWS connection established");

    // Setup the mpsc channel
    let (tx, mut rx) = mpsc::channel(32);

    // Split the stream
    let (mut ws_write, mut ws_read) = ws_stream.split();

    // Setup the mpsc channel for sqlite and spawn the sqlite worker thread
    // TODO: define a data format for asking the db to query something on your behalf, for now
    // pass in the raw query string?
    let (sql_tx, sql_rx) = mpsc::channel(32);

    let sql_worker = thread::spawn(move || {
        println!("Sql Worker - Launching");
        process_queries(sql_rx);
        println!("Sql Worker - Exiting");
    });

    // Spawn the inbound ws stream processor
    let inbound = tokio::spawn(async move {
        loop {
            if let Some(Ok(ws_msg)) = ws_read.next().await {
                let msg_id = msg_id.clone();
                let tx2 = tx.clone();
                let sql_tx2 = sql_tx.clone();

                tokio::spawn(async move {
                    process_inbound_message(msg_id, ws_msg, tx2, sql_tx2, start_time).await;
                });

            }
        }
    });

    // Spawn outbound ws processor
    // TODO: need to make sure to wait on sending till we recieve the hello event
    // could be a oneshot or some sort of flag which it waits till inbound has
    // gotten the hello
    let outbound = tokio::spawn(async move {
        loop {
            while let Some(message) = rx.recv().await {
                println!("Outbound - {:?}", message);

                // TODO: present some way to do plain vs fancy message, and if
                // fancy do a webapi post, otherwise dump into the WS
                //
                // TODO: look into tracking the sent message with a confirmation
                // that it was sent (via msg id) and if it fails, resend with
                // backoff
                //
                // TODO: find a way to handle the ping/pong cycle and monitor
                ws_write.send(message).await;
            }
        }
    });

    // Wait till either exits (error) then begin recovery
    match tokio::try_join!(inbound, outbound) {
        Ok((first, second)) => println!("Control - \t\t\tBoth exited fine"),
        Err(err) => println!("Control - \t\t\tSomething failed: {:?}", err),
    }

    // The sql_tx got moved into the inbound tokio async, so when that dies....
    let res = sql_worker.join();
    println!("Control - \t\t\tSql worker: {:?}", res);

    Ok(())
}

fn process_queries(
    sql_rx: mpsc::Receiver<(runQuery, Option<oneshot::Sender<resQuery>>)>
) {
    let mut block_sql_rx = block_on_stream(sql_rx);

    //let filename = "db.sqlite";
    //let conn = rs::Connection::open_with_flags(
    //    Path::new(filename),
    //    rs::OpenFlags::SQLITE_OPEN_READ_WRITE
    //).expect(&format!("Connection error: {}", filename));
    let conn = rs::Connection::open_in_memory().expect("Failure");

    // Just create a db table
    let res = conn.execute(
        "CREATE TABLE votes (
            for TEXT NOT NULL,
            what TEXT NOT NULL
        )",
        rs::params![],
    );
    println!("Sql Worker - Table - {:?}", res);

    // Listen for inbound query
    while let Some((query, res_tx)) = block_sql_rx.next() {
        match query {
            runQuery::Query => {
                println!("Sql Worker - Query");
                let mut stmt = conn.prepare("SELECT * FROM votes").unwrap();

                let mut vote_iter = stmt.query_map(rs::params![], |row| {
                    let a: String = row.get(0).unwrap();
                    let b: String = row.get(1).unwrap();

                    Ok(format!("{} {}", a, b))
                }).unwrap();

                // Result
                if let Some(Ok(res)) = vote_iter.next() {
                    println!("Sql Worker - Query - {:?}", res);
                    res_tx.unwrap().send(resQuery::Query(res));
                } else {
                    println!("Sql Worker - Query - Fail");
                }
            },
            runQuery::Insert(f, w) => {
                println!("Sql Worker - Insert");
                let mut stmt = conn.prepare("INSERT INTO votes (for, what) VALUES (?, ?)").unwrap();
                let res = stmt.execute(&[f, w]);

                println!("Sql Worker - Insert - {:?}", res);
                res_tx.unwrap().send(resQuery::Insert(res.unwrap()));
            }
        }
    }
}

// TODO: should be able to replace all of these stuff maybe by some trait or some other tricks
// so that we can have a extensible query where i define the query/process/result and then send it
// to the query engine and get the result back in a good format
#[derive(Debug)]
enum runQuery {
    Insert(String, String),
    Query,
}

#[derive(Debug)]
enum resQuery {
    Insert(usize),
    Query(String),
}

async fn process_inbound_message(
    msg_id: MsgId,
    msg: tungstenite::tungstenite::Message,
    mut tx: mpsc::Sender<tungstenite::tungstenite::Message>,
    mut sql_tx: mpsc::Sender<(runQuery, Option<oneshot::Sender<resQuery>>)>,
    start_time: DateTime<Utc>
) -> Result<(), Box<dyn std::error::Error>>
{
    // TODO: this should really go into the 'message sender' not here
    let id = msg_id.inc();
    println!("Inbound - id = {:?}", id);

    // Parse incoming message
    let raw_msg = match msg {
        tungstenite::tungstenite::Message::Text(x) => Some(x),
        _ => {
            println!("Inbound - \tUnsupported ws message type - {:?}", msg);
            None
        },
    };
    //println!("Inbound - raw event = {:?}", raw_msg);

    if let Some(e) = raw_msg.and_then(parse_event) {
        match e {
            Event::UserEvent(event) => {
                // Check if its a message/certain string, if so, reply
                match event {
                    // !version
                    UserEvent::Message {
                        channel: Some(c),
                        text: t,
                        subtype: _,
                        hidden: _,
                        user: u,
                        ts: _,
                    } if c == "CAF6S4TRT".to_string() => {
                        if t == "!version".to_string() {
                            let ver = build_info::PKG_VERSION;
                            let dat = build_info::BUILT_TIME_UTC;
                            let sha = build_info::GIT_COMMIT_HASH.unwrap_or("Unknown");

                            let ws_msg = json!({
                                "id": id,
                                "type": "message",
                                "channel": "CAF6S4TRT",
                                "text": format!("<@{}>: Cargo Version: {} - Build Date: {}, - Build SHA: {}", u, ver, dat, sha),
                            }).to_string();

                            tx.send(tungstenite::tungstenite::Message::from(ws_msg)).await;
                            println!("Inbound - \t\t!version");

                        } else if t == "!uptime".to_string() {
                            let end_time: DateTime<Utc> = Utc::now();
                            let durt = end_time.signed_duration_since(start_time).to_std().unwrap_or(
                                Duration::from_secs(3122064000));

                            let ws_msg = json!({
                                "id": id,
                                "type": "message",
                                "channel": "CAF6S4TRT",
                                "text": format!("<@{}>: {}", u, format_duration(durt).to_string()),
                            }).to_string();

                            tx.send(tungstenite::tungstenite::Message::from(ws_msg)).await;
                            println!("Inbound - \t\t!uptime");

                        } else if t == "!help".to_string() {
                            let help = "Available commands: !uptime !version !github !sidevotes !karma !givers !rank !ranksidevote";
                            let ws_msg = json!({
                                "id": id,
                                "type": "message",
                                "channel": "CAF6S4TRT",
                                "text": format!("<@{}>: {}", u, help),
                            }).to_string();

                            tx.send(tungstenite::tungstenite::Message::from(ws_msg)).await;
                            println!("Inbound - \t\t!help");

                        } else if t == "!github".to_string() {
                            let github = "Github repo: https://github.com/pharaun/Karmator";
                            let ws_msg = json!({
                                "id": id,
                                "type": "message",
                                "channel": "CAF6S4TRT",
                                "text": format!("<@{}>: {}", u, github),
                            }).to_string();

                            tx.send(tungstenite::tungstenite::Message::from(ws_msg)).await;
                            println!("Inbound - \t\t!github");

                        } else if t == "!run_query".to_string() {
                            let ws_msg = json!({
                                "id": id,
                                "type": "message",
                                "channel": "CAF6S4TRT",
                                "text": format!("<@{}>: Ok running query", u),
                            }).to_string();
                            tx.send(tungstenite::tungstenite::Message::from(ws_msg)).await;

                            // Dummy get/send stuff
                            let (tx1, rx1) = oneshot::channel();
                            sql_tx.send((
                                runQuery::Insert("hi".to_string(), "bye".to_string()),
                                Some(tx1)
                            )).await;

                            let (tx2, rx2) = oneshot::channel();
                            sql_tx.send((
                                runQuery::Query,
                                Some(tx2)
                            )).await;

                            // Await for the results
                            let res1 = rx1.await;
                            let ws_msg = json!({
                                "id": id,
                                "type": "message",
                                "channel": "CAF6S4TRT",
                                "text": format!("<@{}>: insert: {:?}", u, res1),
                            }).to_string();
                            tx.send(tungstenite::tungstenite::Message::from(ws_msg)).await;

                            let res2 = rx2.await;
                            let ws_msg = json!({
                                "id": id,
                                "type": "message",
                                "channel": "CAF6S4TRT",
                                "text": format!("<@{}>: query: {:?}", u, res2),
                            }).to_string();
                            tx.send(tungstenite::tungstenite::Message::from(ws_msg)).await;

                            println!("Inbound - \t\t!run_sql_query");
                        }
                    },

                    _ => println!("Inbound - \t\tNo action taken"),
                }
            },

            Event::SystemControl(sc) => (),
            Event::MessageControl(mc) => (),
        }
    }
    Ok(())
}

fn parse_event(s: String) -> Option<Event> {
    // Return User Events first, then the other two
    serde_json::from_str(&s).and_then(|ue| {
        println!("Inbound - \tUser Event = {:?}", ue);
        Ok(Event::UserEvent(ue))
    }).or_else(|_| {
        serde_json::from_str(&s).and_then(|sc| {
            println!("Inbound - \tSystem Control = {:?}", sc);
            Ok(Event::SystemControl(sc))
        }).or_else(|_| {
            serde_json::from_str(&s).and_then(|mc| {
                println!("Inbound - \tMessage Control = {:?}", mc);
                Ok(Event::MessageControl(mc))
            }).or_else(|x| {
                // TODO: for now print to stderr what didn't get deserialized
                // later can have config option to log to file the error or not
                //eprintln!("Inbound - \tFail parse = {:?}", s);
                Err(x)
            })
        })
    }).ok()
}

#[derive(Debug)]
enum Event {
    UserEvent(UserEvent),
    SystemControl(SystemControl),
    MessageControl(MessageControl),
}

#[derive(Debug, Deserialize)]
#[serde(tag = "type")]
#[serde(rename_all = "snake_case")]
enum UserEvent {
    // These events are what plugins will care for:
    //
    // Slack messages
    // TODO: more involved here for now basics
    Message {
        subtype: Option<String>,
        hidden: Option<bool>,
        channel: Option<String>,
        user: String,
        text: String,
        ts: String,
    },

    // TODO: consider looking at slack_api for types to reuse here
    ReactionAdded {
        user: String,
        reaction: String,
        item_user: Option<String>,
        // item: ListResponseItem
        event_ts: String,
    },
    ReactionRemoved {
        user: String,
        reaction: String,
        item_user: Option<String>,
        // item: ListResponseItem
        event_ts: String,
    },
}

#[derive(Debug, Deserialize)]
#[serde(tag = "type")]
#[serde(rename_all = "snake_case")]
enum SystemControl {
    // First message upon successful connection establishment
    Hello,

    // Client should reconnect after getting such message
    Goodbye,

    // Reply to Ping = { type: ping, id: num }
    Pong { reply_to: usize },
}

#[derive(Debug, Deserialize)]
#[serde(untagged)]
enum MessageControl {
    // Reply to message sent
    MessageSent {
        ok: bool,
        reply_to: usize,
        text: String,
        ts: String
    },

    // Reply to failed message sent
    MessageError {
        ok: bool,
        reply_to: usize,
        error: ErrorDetail,
    },
}

#[derive(Debug, Deserialize)]
struct ErrorDetail {
    code: usize,
    msg: String,
}
<|endoftext|>


<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 serde_json;
use std::collections::HashMap;
pub struct Microdata {}

impl Microdata {
    //[Pref="dom.microdata.testing.enabled"]
    pub fn parse() {
        println!("Hello");
        let mut book_reviews = HashMap::new();
        let mut rating = HashMap::new();

        rating.insert("a", "1");
        rating.insert("b", "2");
        rating.insert("c", "3");
        rating.insert("d", "4");

        book_reviews.insert("Adventures of Huckleberry Finn", rating);

        let j = serde_json::to_string(&book_reviews);

        // Print, write to a file, or send to an HTTP server.
        println!("printing json from Microdata module");
        println!("{:?}", j);
    }
}
<commit_msg>vCard working<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::Bindings::DocumentBinding::DocumentBinding::DocumentMethods;
use dom::bindings::codegen::Bindings::ElementBinding::ElementBinding::ElementMethods;
use dom::bindings::inheritance::Castable;
use dom::bindings::root::DomRoot;
use dom::document::Document;
use dom::element::Element;
use dom::htmlelement::HTMLElement;
use dom::node::Node;
use std::borrow::Cow;
use std::collections::HashMap;

pub struct Microdata {}

impl Microdata {
    //[Pref="dom.microdata.testing.enabled"]
    pub fn parse(doc: &Document) -> String {

        let ele = doc.upcast::<Node>();
        let mut start_vcard = false;
        let mut result : String = String::new();
        let mut master_map : HashMap<String, HashMap<String, String>> = HashMap::new();
        let mut master_key : String = String::new();

        result += "BEGIN:VCARD\nPROFILE:VCARD\nVERSION:4.0\nSOURCE:";
        result += doc.url().as_str();
        result += "\nNAME:";
        result += doc.Title().trim();
        result += "\n";

        for element in ele.traverse_preorder().filter_map(DomRoot::downcast::<Element>){
            if element.is::<HTMLElement>() {
                if element.has_attribute(&local_name!("itemtype")){
                    let mut atoms = element.get_tokenlist_attribute(&local_name!("itemtype"), );
                    if !atoms.is_empty() {
                        let val = atoms.remove(0);
                        if val.trim() == "http://microformats.org/profile/hcard"{
                            if !start_vcard {
                                start_vcard = true;
                            } else {
                                break;
                            }
                        }
                    }
                }
                if start_vcard {
                    let mut atoms = element.get_tokenlist_attribute(&local_name!("itemprop"), );
                    if !atoms.is_empty() {
                        let temp_key = atoms.remove(0);
                        if element.has_attribute(&local_name!("itemscope")){
                            master_key = String::from(temp_key.trim()).to_owned();
                            let dup_master_key = Cow::Borrowed(&master_key);
                            master_map.entry(dup_master_key.to_string()).or_insert(HashMap::new());
                        } else {
                            let temp = String::from(temp_key.trim()).to_owned();
                            let dup_key = Cow::Borrowed(&temp);
                            let data = String::from(element.GetInnerHTML().unwrap());
                            let dup_master_key = Cow::Borrowed(&master_key);
                            let temp_map = master_map.entry(dup_master_key.to_string()).or_insert(HashMap::new());
                            temp_map.insert(dup_key.to_string(), String::from(data));
                        }
                    }
                }
            }
        }
        //println!("{:?}", master_map);
        for (info_type, detail_map) in &master_map {
            //println!("{} -> {:?}", info_type, detail_map);
            match info_type.as_str() {
                "n" => {
                    let mut given_name = "";
                    let mut family_name = "";
                    if detail_map.contains_key("family-name") {
                        family_name = detail_map.get("family-name").unwrap();
                    }
                    if detail_map.contains_key("given-name") {
                        given_name = detail_map.get("given-name").unwrap();
                    }
                    result += format!("N:{};{}\n", family_name, given_name).as_str();
                    result += format!("FN:{} {}\n", given_name, family_name).as_str();
                },
                "org" => {
                    let mut organization_unit = "";
                    let mut organization_name = "";
                    if detail_map.contains_key("organization-unit"){
                        organization_unit = detail_map.get("organization-unit").unwrap();
                    }
                    if detail_map.contains_key("organization-name"){
                        organization_name = detail_map.get("organization-name").unwrap();
                    }
                    result += format!("ORG:{};{}\n", organization_name, organization_unit).as_str();
                },
                "tel" => {

                },
                "adr" => {

                },
                _ => {},
            }
        }
        result += "END:VCARD";
        println!("{}", result);
        return result;
    }
}
<|endoftext|>


<commit_before>extern crate ultra;

use ultra::decrypt;

#[test]
fn basic_decryption() {
	let expected = "THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG";
    let ciphertext = "NTZ NTQLZ JMWLL ART BBNOW WZQK KEQ IEVK LZO";
    let (plaintext, rotor, _, _) = decrypt(ciphertext);

    assert_eq!(plaintext, expected);
    assert_eq!(rotor, "142");
}
<commit_msg>Convert indentation to spaces<commit_after>extern crate ultra;

use ultra::decrypt;

#[test]
fn basic_decryption() {
    let expected = "THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG";
    let ciphertext = "NTZ NTQLZ JMWLL ART BBNOW WZQK KEQ IEVK LZO";
    let (plaintext, rotor, _, _) = decrypt(ciphertext);

    assert_eq!(plaintext, expected);
    assert_eq!(rotor, "142");
}
<|endoftext|>


<commit_before>use std::{io, convert};
use std::io::{Read, Write, Cursor};
use super::{packet, data_rw, state};
use super::data_rw::{ReadPacketData, WritePacketData};
use super::state::State;
use super::packet::*;

#[derive(Debug)]
pub enum Error {
    DataRWError(data_rw::Error),
    IOError(io::Error),
    PacketError(packet::Error),
    StateError(state::Error),
    PacketHasNegativeLength,
}

impl_convert_for_error!(data_rw::Error, Error::DataRWError);
impl_convert_for_error!(io::Error, Error::IOError);
impl_convert_for_error!(packet::Error, Error::PacketError);
impl_convert_for_error!(state::Error, Error::StateError);

pub trait WritePacket {
    fn write_general_packet(&mut self, packet: &GeneralPacket) -> Result<(), Error>;
    fn write_packet<P>(&mut self, packet: &P) -> Result<(), Error> where P: ToGeneralPacket;
}

pub trait ReadPacket {
    fn read_general_packet(&mut self, state: State) -> Result<GeneralPacket, Error>;
    fn read_packet<P>(&mut self, state: State) -> Result<P, Error> where P: FromGeneralPacket;
}

impl<T> WritePacket for T
    where T: Write
{
    fn write_general_packet(&mut self, packet: &GeneralPacket) -> Result<(), Error> {
        let mut packet_id_buff = Cursor::new(Vec::with_capacity(5));
        packet_id_buff.write_varint(packet.packet_id.into())?;

        let packet_id = packet_id_buff.get_ref();
        let body = packet.body.get_ref();
        let len = (body.len() + packet_id.len()) as i32;

        self.write_varint(len)?;
        self.write(packet_id)?;
        self.write(body)?;

        Ok(())
    }

    fn write_packet<P>(&mut self, packet: &P) -> Result<(), Error>
        where P: ToGeneralPacket
    {
        self.write_general_packet(&packet.to_general_packet()?)?;

        Ok(())
    }
}

impl<T> ReadPacket for T
    where T: Read
{
    fn read_general_packet(&mut self, state: State) -> Result<GeneralPacket, Error> {
        // Length
        let len_container = self.read_varint()?;
        let len = len_container.content;

        if len < 0 {
            return Err(Error::PacketHasNegativeLength);
        }

        // Packet ID
        let packet_id_container = self.read_varint()?;
        let packet_id = packet_id_container.content;

        // Body
        let body_len = (len as usize) - packet_id_container.read_len;
        let mut body = vec![0_u8; body_len];
        self.read(body.as_mut_slice())?;

        // Construct
        let packet = GeneralPacket::with_body_vec(state.detect_packet_type(packet_id)?, body);

        Ok(packet)
    }

    fn read_packet<P>(&mut self, state: State) -> Result<P, Error>
        where P: FromGeneralPacket
    {
        let mut general_packet = self.read_general_packet(state)?;
        return P::from_general_packet(&mut general_packet).map_err(|e| Error::from(e));
    }
}

#[cfg(test)]
mod tests {}
<commit_msg>Fix a bug reading a packet from Read<commit_after>use std::{io, convert};
use std::io::{Read, Write, Cursor};
use super::{packet, data_rw, state};
use super::data_rw::{ReadPacketData, WritePacketData};
use super::state::State;
use super::packet::*;

#[derive(Debug)]
pub enum Error {
    DataRWError(data_rw::Error),
    IOError(io::Error),
    PacketError(packet::Error),
    StateError(state::Error),
    PacketHasNegativeLength,
}

impl_convert_for_error!(data_rw::Error, Error::DataRWError);
impl_convert_for_error!(io::Error, Error::IOError);
impl_convert_for_error!(packet::Error, Error::PacketError);
impl_convert_for_error!(state::Error, Error::StateError);

pub trait WritePacket {
    fn write_general_packet(&mut self, packet: &GeneralPacket) -> Result<(), Error>;
    fn write_packet<P>(&mut self, packet: &P) -> Result<(), Error> where P: ToGeneralPacket;
}

pub trait ReadPacket {
    fn read_general_packet(&mut self, state: State) -> Result<GeneralPacket, Error>;
    fn read_packet<P>(&mut self, state: State) -> Result<P, Error> where P: FromGeneralPacket;
}

impl<T> WritePacket for T
    where T: Write
{
    fn write_general_packet(&mut self, packet: &GeneralPacket) -> Result<(), Error> {
        let mut packet_id_buff = Cursor::new(Vec::with_capacity(5));
        packet_id_buff.write_varint(packet.packet_id.into())?;

        let packet_id = packet_id_buff.get_ref();
        let body = packet.body.get_ref();
        let len = (body.len() + packet_id.len()) as i32;

        self.write_varint(len)?;
        self.write(packet_id)?;
        self.write(body)?;

        Ok(())
    }

    fn write_packet<P>(&mut self, packet: &P) -> Result<(), Error>
        where P: ToGeneralPacket
    {
        self.write_general_packet(&packet.to_general_packet()?)?;

        Ok(())
    }
}

impl<T> ReadPacket for T
    where T: Read
{
    fn read_general_packet(&mut self, state: State) -> Result<GeneralPacket, Error> {
        // Length
        let len_container = self.read_varint()?;
        let len = len_container.content;

        if len < 0 {
            return Err(Error::PacketHasNegativeLength);
        }

        // Packet ID
        let packet_id_container = self.read_varint()?;
        let packet_id = packet_id_container.content;

        // Body
        let body_len = (len as usize) - packet_id_container.read_len;
        let mut body = vec![0_u8; body_len];
        self.read_exact(body.as_mut_slice())?;

        // Construct
        let packet = GeneralPacket::with_body_vec(state.detect_packet_type(packet_id)?, body);

        Ok(packet)
    }

    fn read_packet<P>(&mut self, state: State) -> Result<P, Error>
        where P: FromGeneralPacket
    {
        let mut general_packet = self.read_general_packet(state)?;
        return P::from_general_packet(&mut general_packet).map_err(|e| Error::from(e));
    }
}

#[cfg(test)]
mod tests {}
<|endoftext|>


<commit_before>extern crate futures;

use std::sync::Arc;
use std::sync::atomic::*;

use futures::{Future, Async, Stream, Sink};
use futures::future::result;
use futures::executor;
use futures::sync::mpsc;

mod support;
use support::*;

#[test]
fn sequence() {
    let (tx, mut rx) = mpsc::channel(1);

    sassert_empty(&mut rx);
    sassert_empty(&mut rx);

    let amt = 20;
    send(amt, tx).forget();
    let mut rx = rx.wait();
    for i in (1..amt + 1).rev() {
        assert_eq!(rx.next(), Some(Ok(i)));
    }
    assert_eq!(rx.next(), None);

    fn send(n: u32, sender: mpsc::Sender<u32>)
            -> Box<Future<Item=(), Error=()> + Send> {
        if n == 0 {
            return result(Ok(())).boxed()
        }
        sender.send(n).map_err(|_| ()).and_then(move |sender| {
            send(n - 1, sender)
        }).boxed()
    }
}

#[test]
fn drop_sender() {
    let (tx, mut rx) = mpsc::channel::<u32>(1);
    drop(tx);
    sassert_done(&mut rx);
}

#[test]
fn drop_rx() {
    let (tx, rx) = mpsc::channel::<u32>(1);
    let tx = tx.send(1).wait().ok().unwrap();
    drop(rx);
    assert!(tx.send(1).wait().is_err());
}

struct Unpark;

impl executor::Unpark for Unpark {
    fn unpark(&self) {
    }
}

#[test]
fn drop_order() {
    static DROPS: AtomicUsize = ATOMIC_USIZE_INIT;
    let (tx, rx) = mpsc::channel(1);

    struct A;

    impl Drop for A {
        fn drop(&mut self) {
            DROPS.fetch_add(1, Ordering::SeqCst);
        }
    }

    let tx = tx.send(A).wait().unwrap();
    assert_eq!(DROPS.load(Ordering::SeqCst), 0);
    drop(rx);
    assert_eq!(DROPS.load(Ordering::SeqCst), 1);
    assert!(tx.send(A).wait().is_err());
    assert_eq!(DROPS.load(Ordering::SeqCst), 2);
}
<commit_msg>Clean up test warnings<commit_after>extern crate futures;

use std::sync::atomic::*;

use futures::{Future, Stream, Sink};
use futures::future::result;
use futures::sync::mpsc;

mod support;
use support::*;

#[test]
fn sequence() {
    let (tx, mut rx) = mpsc::channel(1);

    sassert_empty(&mut rx);
    sassert_empty(&mut rx);

    let amt = 20;
    send(amt, tx).forget();
    let mut rx = rx.wait();
    for i in (1..amt + 1).rev() {
        assert_eq!(rx.next(), Some(Ok(i)));
    }
    assert_eq!(rx.next(), None);

    fn send(n: u32, sender: mpsc::Sender<u32>)
            -> Box<Future<Item=(), Error=()> + Send> {
        if n == 0 {
            return result(Ok(())).boxed()
        }
        sender.send(n).map_err(|_| ()).and_then(move |sender| {
            send(n - 1, sender)
        }).boxed()
    }
}

#[test]
fn drop_sender() {
    let (tx, mut rx) = mpsc::channel::<u32>(1);
    drop(tx);
    sassert_done(&mut rx);
}

#[test]
fn drop_rx() {
    let (tx, rx) = mpsc::channel::<u32>(1);
    let tx = tx.send(1).wait().ok().unwrap();
    drop(rx);
    assert!(tx.send(1).wait().is_err());
}

#[test]
fn drop_order() {
    static DROPS: AtomicUsize = ATOMIC_USIZE_INIT;
    let (tx, rx) = mpsc::channel(1);

    struct A;

    impl Drop for A {
        fn drop(&mut self) {
            DROPS.fetch_add(1, Ordering::SeqCst);
        }
    }

    let tx = tx.send(A).wait().unwrap();
    assert_eq!(DROPS.load(Ordering::SeqCst), 0);
    drop(rx);
    assert_eq!(DROPS.load(Ordering::SeqCst), 1);
    assert!(tx.send(A).wait().is_err());
    assert_eq!(DROPS.load(Ordering::SeqCst), 2);
}
<|endoftext|>


<commit_before>
#[macro_use]
extern crate nom;

use nom::IResult;

use std::collections::HashMap;

fn is_alphabetic(chr:char) -> bool {
  (chr as u8 >= 0x41 && chr as u8 <= 0x5A) || (chr as u8 >= 0x61 && chr as u8 <= 0x7A)
}

fn is_digit(chr: char) -> bool {
  chr as u8 >= 0x30 && chr as u8 <= 0x39
}

fn is_alphanumeric(chr: char) -> bool {
  is_alphabetic(chr) || is_digit(chr)
}

fn is_space(chr:char) -> bool {
  chr == ' ' || chr == '\t'
}

fn is_line_ending_or_comment(chr:char) -> bool {
  chr == ';' || chr == '\n'
}

named!(alphanumeric<&str,&str>,         take_while_s!(is_alphanumeric));
named!(not_line_ending<&str,&str>,      is_not_s!("\r\n"));
named!(space<&str,&str>,                take_while_s!(is_space));
named!(space_or_line_ending<&str,&str>, is_a_s!(" \r\n"));

fn right_bracket(c:char) -> bool {
  c == ']'
}

named!(category     <&str, &str>,
  chain!(
          tag_s!("[")                 ~
    name: take_till_s!(right_bracket) ~
          tag_s!("]")                 ~
          space_or_line_ending?       ,
    ||{ name }
  )
);

named!(key_value    <&str,(&str,&str)>,
  chain!(
    key: alphanumeric                            ~
         space?                                  ~
         tag_s!("=")                             ~
         space?                                  ~
    val: take_till_s!(is_line_ending_or_comment) ~
         space?                                  ~
         pair!(tag_s!(";"), not_line_ending)?    ~
         space_or_line_ending?                   ,
    ||{(key, val)}
  )
);

named!(keys_and_values_aggregator<&str, Vec<(&str,&str)> >, many0!(key_value));

fn keys_and_values(input:&str) -> IResult<&str, HashMap<&str, &str> > {
  let mut h: HashMap<&str, &str> = HashMap::new();

  match keys_and_values_aggregator(input) {
    IResult::Done(i,tuple_vec) => {
      for &(k,v) in &tuple_vec {
        h.insert(k, v);
      }
      IResult::Done(i, h)
    },
    IResult::Incomplete(a)     => IResult::Incomplete(a),
    IResult::Error(a)          => IResult::Error(a)
  }
}

named!(category_and_keys<&str,(&str,HashMap<&str,&str>)>,
  pair!(category, keys_and_values)
);

named!(categories_aggregator<&str, Vec<(&str, HashMap<&str,&str>)> >, many0!(category_and_keys));

fn categories(input: &str) -> IResult<&str, HashMap<&str, HashMap<&str, &str> > > {
  let mut h: HashMap<&str, HashMap<&str, &str>> = HashMap::new();

  match categories_aggregator(input) {
    IResult::Done(i,tuple_vec) => {
      for &(k,ref v) in &tuple_vec {
        h.insert(k, v.clone());
      }
      IResult::Done(i, h)
    },
    IResult::Incomplete(a)     => IResult::Incomplete(a),
    IResult::Error(a)          => IResult::Error(a)
  }
}

#[test]
fn parse_category_test() {
  let ini_file = "[category]

parameter=value
key = value2";

  let ini_without_category = "parameter=value
key = value2";

  let res = category(ini_file);
  println!("{:?}", res);
  match res {
    IResult::Done(i, o) => println!("i: {} | o: {:?}", i, o),
    _ => println!("error")
  }

  assert_eq!(res, IResult::Done(ini_without_category, "category"));
}

#[test]
fn parse_key_value_test() {
  let ini_file = "parameter=value
key = value2";

  let ini_without_key_value = "key = value2";

  let res = key_value(ini_file);
  println!("{:?}", res);
  match res {
    IResult::Done(i, (o1, o2)) => println!("i: {} | o: ({:?},{:?})", i, o1, o2),
    _ => println!("error")
  }

  assert_eq!(res, IResult::Done(ini_without_key_value, ("parameter", "value")));
}

#[test]
fn parse_key_value_with_space_test() {
  let ini_file = "parameter = value
key = value2";

  let ini_without_key_value = "key = value2";

  let res = key_value(ini_file);
  println!("{:?}", res);
  match res {
    IResult::Done(i, (o1, o2)) => println!("i: {} | o: ({:?},{:?})", i, o1, o2),
    _ => println!("error")
  }

  assert_eq!(res, IResult::Done(ini_without_key_value, ("parameter", "value")));
}

#[test]
fn parse_key_value_with_comment_test() {
  let ini_file = "parameter=value;abc
key = value2";

  let ini_without_key_value = "key = value2";

  let res = key_value(ini_file);
  println!("{:?}", res);
  match res {
    IResult::Done(i, (o1, o2)) => println!("i: {} | o: ({:?},{:?})", i, o1, o2),
    _ => println!("error")
  }

  assert_eq!(res, IResult::Done(ini_without_key_value, ("parameter", "value")));
}

#[test]
fn parse_multiple_keys_and_values_test() {
  let ini_file = "parameter=value;abc

key = value2

[category]";

  let ini_without_key_value = "[category]";

  let res = keys_and_values(ini_file);
  println!("{:?}", res);
  match res {
    IResult::Done(i, ref o) => println!("i: {} | o: {:?}", i, o),
    _ => println!("error")
  }

  let mut expected: HashMap<&str, &str> = HashMap::new();
  expected.insert("parameter", "value");
  expected.insert("key", "value2");
  assert_eq!(res, IResult::Done(ini_without_key_value, expected));
}

#[test]
fn parse_category_then_multiple_keys_and_values_test() {
  //FIXME: there can be an empty line or a comment line after a category
  let ini_file = "[abcd]
parameter=value;abc

key = value2

[category]";

  let ini_after_parser = "[category]";

  let res = category_and_keys(ini_file);
  println!("{:?}", res);
  match res {
    IResult::Done(i, ref o) => println!("i: {} | o: {:?}", i, o),
    _ => println!("error")
  }

  let mut expected_h: HashMap<&str, &str> = HashMap::new();
  expected_h.insert("parameter", "value");
  expected_h.insert("key", "value2");
  assert_eq!(res, IResult::Done(ini_after_parser, ("abcd", expected_h)));
}

#[test]
fn parse_multiple_categories_test() {
  let ini_file = "[abcd]

parameter=value;abc

key = value2

[category]
parameter3=value3
key4 = value4
";

  let res = categories(ini_file);
  //println!("{:?}", res);
  match res {
    IResult::Done(i, ref o) => println!("i: {} | o: {:?}", i, o),
    _ => println!("error")
  }

  let mut expected_1: HashMap<&str, &str> = HashMap::new();
  expected_1.insert("parameter", "value");
  expected_1.insert("key", "value2");
  let mut expected_2: HashMap<&str, &str> = HashMap::new();
  expected_2.insert("parameter3", "value3");
  expected_2.insert("key4", "value4");
  let mut expected_h: HashMap<&str, HashMap<&str, &str>> = HashMap::new();
  expected_h.insert("abcd",     expected_1);
  expected_h.insert("category", expected_2);
  assert_eq!(res, IResult::Done("", expected_h));
}
<commit_msg>tests/ini_str: don't use chain!<commit_after>
#[macro_use]
extern crate nom;

use nom::IResult;

use std::collections::HashMap;

fn is_alphabetic(chr:char) -> bool {
  (chr as u8 >= 0x41 && chr as u8 <= 0x5A) || (chr as u8 >= 0x61 && chr as u8 <= 0x7A)
}

fn is_digit(chr: char) -> bool {
  chr as u8 >= 0x30 && chr as u8 <= 0x39
}

fn is_alphanumeric(chr: char) -> bool {
  is_alphabetic(chr) || is_digit(chr)
}

fn is_space(chr:char) -> bool {
  chr == ' ' || chr == '\t'
}

fn is_line_ending_or_comment(chr:char) -> bool {
  chr == ';' || chr == '\n'
}

named!(alphanumeric<&str,&str>,         take_while_s!(is_alphanumeric));
named!(not_line_ending<&str,&str>,      is_not_s!("\r\n"));
named!(space<&str,&str>,                take_while_s!(is_space));
named!(space_or_line_ending<&str,&str>, is_a_s!(" \r\n"));

fn right_bracket(c:char) -> bool {
  c == ']'
}

named!(category     <&str, &str>,
  do_parse!(
          tag_s!("[")                 >>
    name: take_till_s!(right_bracket) >>
          tag_s!("]")                 >>
          opt!(space_or_line_ending)  >>
    (name)
  )
);

named!(key_value    <&str,(&str,&str)>,
  do_parse!(
    key: alphanumeric                              >>
         opt!(space)                               >>
         tag_s!("=")                               >>
         opt!(space)                               >>
    val: take_till_s!(is_line_ending_or_comment)   >>
         opt!(space)                               >>
         opt!(pair!(tag_s!(";"), not_line_ending)) >>
         opt!(space_or_line_ending)                >>
    (key, val)
  )
);

named!(keys_and_values_aggregator<&str, Vec<(&str,&str)> >, many0!(key_value));

fn keys_and_values(input:&str) -> IResult<&str, HashMap<&str, &str> > {
  let mut h: HashMap<&str, &str> = HashMap::new();

  match keys_and_values_aggregator(input) {
    IResult::Done(i,tuple_vec) => {
      for &(k,v) in &tuple_vec {
        h.insert(k, v);
      }
      IResult::Done(i, h)
    },
    IResult::Incomplete(a)     => IResult::Incomplete(a),
    IResult::Error(a)          => IResult::Error(a)
  }
}

named!(category_and_keys<&str,(&str,HashMap<&str,&str>)>,
  pair!(category, keys_and_values)
);

named!(categories_aggregator<&str, Vec<(&str, HashMap<&str,&str>)> >, many0!(category_and_keys));

fn categories(input: &str) -> IResult<&str, HashMap<&str, HashMap<&str, &str> > > {
  let mut h: HashMap<&str, HashMap<&str, &str>> = HashMap::new();

  match categories_aggregator(input) {
    IResult::Done(i,tuple_vec) => {
      for &(k,ref v) in &tuple_vec {
        h.insert(k, v.clone());
      }
      IResult::Done(i, h)
    },
    IResult::Incomplete(a)     => IResult::Incomplete(a),
    IResult::Error(a)          => IResult::Error(a)
  }
}

#[test]
fn parse_category_test() {
  let ini_file = "[category]

parameter=value
key = value2";

  let ini_without_category = "parameter=value
key = value2";

  let res = category(ini_file);
  println!("{:?}", res);
  match res {
    IResult::Done(i, o) => println!("i: {} | o: {:?}", i, o),
    _ => println!("error")
  }

  assert_eq!(res, IResult::Done(ini_without_category, "category"));
}

#[test]
fn parse_key_value_test() {
  let ini_file = "parameter=value
key = value2";

  let ini_without_key_value = "key = value2";

  let res = key_value(ini_file);
  println!("{:?}", res);
  match res {
    IResult::Done(i, (o1, o2)) => println!("i: {} | o: ({:?},{:?})", i, o1, o2),
    _ => println!("error")
  }

  assert_eq!(res, IResult::Done(ini_without_key_value, ("parameter", "value")));
}

#[test]
fn parse_key_value_with_space_test() {
  let ini_file = "parameter = value
key = value2";

  let ini_without_key_value = "key = value2";

  let res = key_value(ini_file);
  println!("{:?}", res);
  match res {
    IResult::Done(i, (o1, o2)) => println!("i: {} | o: ({:?},{:?})", i, o1, o2),
    _ => println!("error")
  }

  assert_eq!(res, IResult::Done(ini_without_key_value, ("parameter", "value")));
}

#[test]
fn parse_key_value_with_comment_test() {
  let ini_file = "parameter=value;abc
key = value2";

  let ini_without_key_value = "key = value2";

  let res = key_value(ini_file);
  println!("{:?}", res);
  match res {
    IResult::Done(i, (o1, o2)) => println!("i: {} | o: ({:?},{:?})", i, o1, o2),
    _ => println!("error")
  }

  assert_eq!(res, IResult::Done(ini_without_key_value, ("parameter", "value")));
}

#[test]
fn parse_multiple_keys_and_values_test() {
  let ini_file = "parameter=value;abc

key = value2

[category]";

  let ini_without_key_value = "[category]";

  let res = keys_and_values(ini_file);
  println!("{:?}", res);
  match res {
    IResult::Done(i, ref o) => println!("i: {} | o: {:?}", i, o),
    _ => println!("error")
  }

  let mut expected: HashMap<&str, &str> = HashMap::new();
  expected.insert("parameter", "value");
  expected.insert("key", "value2");
  assert_eq!(res, IResult::Done(ini_without_key_value, expected));
}

#[test]
fn parse_category_then_multiple_keys_and_values_test() {
  //FIXME: there can be an empty line or a comment line after a category
  let ini_file = "[abcd]
parameter=value;abc

key = value2

[category]";

  let ini_after_parser = "[category]";

  let res = category_and_keys(ini_file);
  println!("{:?}", res);
  match res {
    IResult::Done(i, ref o) => println!("i: {} | o: {:?}", i, o),
    _ => println!("error")
  }

  let mut expected_h: HashMap<&str, &str> = HashMap::new();
  expected_h.insert("parameter", "value");
  expected_h.insert("key", "value2");
  assert_eq!(res, IResult::Done(ini_after_parser, ("abcd", expected_h)));
}

#[test]
fn parse_multiple_categories_test() {
  let ini_file = "[abcd]

parameter=value;abc

key = value2

[category]
parameter3=value3
key4 = value4
";

  let res = categories(ini_file);
  //println!("{:?}", res);
  match res {
    IResult::Done(i, ref o) => println!("i: {} | o: {:?}", i, o),
    _ => println!("error")
  }

  let mut expected_1: HashMap<&str, &str> = HashMap::new();
  expected_1.insert("parameter", "value");
  expected_1.insert("key", "value2");
  let mut expected_2: HashMap<&str, &str> = HashMap::new();
  expected_2.insert("parameter3", "value3");
  expected_2.insert("key4", "value4");
  let mut expected_h: HashMap<&str, HashMap<&str, &str>> = HashMap::new();
  expected_h.insert("abcd",     expected_1);
  expected_h.insert("category", expected_2);
  assert_eq!(res, IResult::Done("", expected_h));
}
<|endoftext|>


<commit_before>use gtk::{WidgetExt, DrawingArea};
use cassowary;
use cassowary::math::variables::{new_var, new_const};
use cassowary::math::relationships::Relationship;
use cassowary::math::expressions::Expression;
use cassowary::objective::functions::Function;
use cassowary::objective::problems::ProblemType;
use cassowary::objective::constraints::{new_reg_con, SystemOfConstraints};
use pen::PenStream;
use visualiser::DrawCommand;

pub fn cal_demo1(da_width: f32, da_height: f32, pen: &mut PenStream) {
    let x_loc = 30.0;
    let y_loc = 20.0;
    let side_margin = 30.0;
    let top_margin = 20.0;
    let exp1 = Expression::new(vec![new_var("P", 1.0)],
                               Relationship::EQ,
                               vec![new_var("width", 1.0), new_var("height", 1.0)]);
    let exp2 = Expression::new(vec![new_var("width", 1.0)],
                               Relationship::LEQ,
                               vec![new_const("con1", da_width - 2.0 * side_margin)]);
    let exp3 = Expression::new(vec![new_var("height", 1.0)],
                               Relationship::LEQ,
                               vec![new_const("con2", da_height - 2.0 * top_margin)]);
    let mut objective_func = Function::new(exp1, ProblemType::MAX);
    let c1 = new_reg_con(exp2);
    let c2 = new_reg_con(exp3);
    let subject_to = SystemOfConstraints::new(vec![c1, c2]);
    let solution = cassowary::optimise(&mut objective_func, &subject_to);
    let width = solution.iter()
        .find(|&entry| entry.0 == "width")
        .unwrap()
        .1;
    let height = solution.iter()
        .find(|&entry| entry.0 == "height")
        .unwrap()
        .1;
    println!("width = {}\nheight = {}", width, height);
    pen.add_rec_to_draw(x_loc as f64, y_loc as f64, width as f64, height as f64);
}

pub fn cal_demo2(x_loc1: f32,
                 y_loc1: f32,
                 x_loc2: f32,
                 y_loc2: f32,
                 side_margin: f32,
                 mid_margin: f32,
                 top_margin: f32,
                 da_width: f32,
                 da_height: f32,
                 pen: &mut PenStream) {
    let exp1 = Expression::new(vec![new_var("P", 1.0)],
                               Relationship::EQ,
                               vec![new_var("width1", 0.5),
                                    new_var("height1", 1.0),
                                    new_var("width2", 1.0),
                                    new_var("height2", 1.0)]);
    let exp2 = Expression::new(vec![new_var("width1", 1.0), new_var("width2", 1.0)],
                               Relationship::LEQ,
                               vec![new_const("con1",
                                              da_width - (2.0 * side_margin) - mid_margin)]);
    let exp3 = Expression::new(vec![new_var("height1", 1.0)],
                               Relationship::LEQ,
                               vec![new_const("con2", da_height - 2.0 * top_margin)]);
    let exp4 = Expression::new(vec![new_var("height2", 1.0)],
                               Relationship::LEQ,
                               vec![new_const("con3", da_height - 2.0 * top_margin)]);
    let exp5 = Expression::new(vec![new_var("width1", 1.0)],
                               Relationship::LEQ,
                               vec![new_const("con4", da_width - x_loc1)]);
    let exp6 = Expression::new(vec![new_var("width1", 1.0)],
                               Relationship::LEQ,
                               vec![new_const("con4", x_loc2 - x_loc1 - mid_margin)]);
    let exp7 = Expression::new(vec![new_var("width2", 1.0)],
                               Relationship::LEQ,
                               vec![new_const("con4", da_width - (x_loc2 + side_margin))]);
    let exp8 = Expression::new(vec![new_var("height1", 1.0)],
                               Relationship::LEQ,
                               vec![new_const("con5", da_height - y_loc1)]);
    let exp9 = Expression::new(vec![new_var("width2", 1.0)],
                               Relationship::LEQ,
                               vec![new_const("con6", da_width - x_loc2)]);
    let exp10 = Expression::new(vec![new_var("height2", 1.0)],
                                Relationship::LEQ,
                                vec![new_const("con7", da_height - y_loc2)]);
    let mut objective_func = Function::new(exp1, ProblemType::MAX);
    let c1 = new_reg_con(exp2);
    let c2 = new_reg_con(exp3);
    let c3 = new_reg_con(exp4);
    let c4 = new_reg_con(exp5);
    let c5 = new_reg_con(exp6);
    let c6 = new_reg_con(exp7);
    let c7 = new_reg_con(exp8);
    let c8 = new_reg_con(exp9);
    let c9 = new_reg_con(exp10);
    let subject_to = SystemOfConstraints::new(vec![c1, c2, c3, c4, c5, c6, c7, c8, c9]);
    let solution = cassowary::optimise(&mut objective_func, &subject_to);
    let width1 = solution.iter()
        .find(|&entry| entry.0 == "width1")
        .unwrap()
        .1;
    let height1 = solution.iter()
        .find(|&entry| entry.0 == "height1")
        .unwrap()
        .1;
    let width2 = solution.iter()
        .find(|&entry| entry.0 == "width2")
        .unwrap()
        .1;
    let height2 = solution.iter()
        .find(|&entry| entry.0 == "height2")
        .unwrap()
        .1;
    println!("width1 = {}\nheight1 = {}\n", width1, height1);
    println!("width2 = {}\nheight2 = {}", width2, height2);
    pen.add_rec_to_draw(x_loc1 as f64, y_loc1 as f64, width1 as f64, height1 as f64);
    pen.add_rec_to_draw(x_loc2 as f64, y_loc2 as f64, width2 as f64, height2 as f64);
}

pub fn demo2_key_release(left: bool,
                         ri: usize,
                         pen: &mut PenStream,
                         da: &DrawingArea,
                         cs: &mut Vec<DrawCommand>) {
    let (x, y) = match left {
        true => {
            let rec_info = pen.rectangle_info(ri);
            (rec_info.0 - 5, rec_info.1)
        }
        false => {
            let rec_info = pen.rectangle_info(ri);
            (rec_info.0 + 5, rec_info.1)
        }
    };

    match ri {
        0 => {
            let (da_width, da_height) = drawing_area_height_width(da);
            let (x2, y2, _, _) = pen.rectangle_info(1);
            pen.clear_all_recs();
            cal_demo2(x as f32,
                      y as f32,
                      x2 as f32,
                      y2 as f32,
                      30.0,
                      20.0,
                      20.0,
                      da_width as f32,
                      da_height as f32,
                      pen);
            draw_demo2_key_release(x - 5, y, da_width, da_height, da, cs);
        }
        _ => {
            let (da_width, da_height) = drawing_area_height_width(da);
            let (x1, y1, _, _) = pen.rectangle_info(0);
            pen.clear_all_recs();
            cal_demo2(x1 as f32,
                      y1 as f32,
                      x as f32,
                      y as f32,
                      30.0,
                      20.0,
                      20.0,
                      da_width as f32,
                      da_height as f32,
                      pen);
            draw_demo2_key_release(x1, y1, da_width, da_height, da, cs);
        }
    }
}

pub fn cal_demo3(x_loc1: f32,
                 y_loc1: f32,
                 x_loc2: f32,
                 y_loc2: f32,
                 x_loc3: f32,
                 y_loc3: f32,
                 side_margin: f32,
                 mid_margin: f32,
                 top_margin: f32,
                 da_width: f32,
                 da_height: f32,
                 pen: &mut PenStream) {
    let exp1 = Expression::new(vec![new_var("P", 1.0)],
                               Relationship::EQ,
                               vec![new_var("width1", 1.0),
                                    new_var("height1", 1.0),
                                    new_var("width2", 1.0),
                                    new_var("height2", 1.0),
                                    new_var("width3", 1.0),
                                    new_var("height3", 1.0)]);
    let exp2 = Expression::new(vec![new_var("width2", 1.0), new_var("width3", 1.0)],
                               Relationship::LEQ,
                               vec![new_const("con1",
                                              da_width - (2.0 * side_margin) - (2.0 * mid_margin) -
                                              100.0)]);
    let exp3 = Expression::new(vec![new_var("height2", 1.0)],
                               Relationship::LEQ,
                               vec![new_const("con2", da_height - (2.0 * top_margin))]);
    let exp4 = Expression::new(vec![new_var("height3", 1.0)],
                               Relationship::LEQ,
                               vec![new_const("con3", da_height - (2.0 * top_margin))]);
    let exp5 = Expression::new(vec![new_var("height1", 1.0)],
                               Relationship::EQ,
                               vec![new_const("con4", 100.0)]);
    let exp6 = Expression::new(vec![new_var("width2", 1.0)],
                               Relationship::LEQ,
                               vec![new_const("con5", x_loc3 - x_loc2 - mid_margin)]);
    let exp7 = Expression::new(vec![new_var("width3", 1.0)],
                               Relationship::LEQ,
                               vec![new_const("con6", da_width - (x_loc3 + side_margin))]);
    let exp8 = Expression::new(vec![new_var("width1", 1.0)],
                               Relationship::EQ,
                               vec![new_const("con7", 100.0)]);
    let mut objective_func = Function::new(exp1, ProblemType::MAX);
    let c1 = new_reg_con(exp2);
    let c2 = new_reg_con(exp3);
    let c3 = new_reg_con(exp4);
    let c4 = new_reg_con(exp5);
    let c5 = new_reg_con(exp6);
    let c6 = new_reg_con(exp7);
    let c7 = new_reg_con(exp8);
    let subject_to = SystemOfConstraints::new(vec![c1, c2, c3, c4, c5, c6, c7]);
    let solution = cassowary::optimise(&mut objective_func, &subject_to);
    let width1 = solution.iter()
        .find(|&entry| entry.0 == "width1")
        .unwrap()
        .1;
    let height1 = solution.iter()
        .find(|&entry| entry.0 == "height1")
        .unwrap()
        .1;
    let width2 = solution.iter()
        .find(|&entry| entry.0 == "width2")
        .unwrap()
        .1;
    let height2 = solution.iter()
        .find(|&entry| entry.0 == "height2")
        .unwrap()
        .1;
    let width3 = solution.iter()
        .find(|&entry| entry.0 == "width3")
        .unwrap()
        .1;
    let height3 = solution.iter()
        .find(|&entry| entry.0 == "height3")
        .unwrap()
        .1;
    pen.add_rec_to_draw(x_loc1 as f64, y_loc1 as f64, width1 as f64, height1 as f64);
    pen.add_rec_to_draw(x_loc2 as f64, y_loc2 as f64, width2 as f64, height2 as f64);
    pen.add_rec_to_draw(x_loc3 as f64, y_loc3 as f64, width3 as f64, height3 as f64);
}

pub fn demo3_size_change(pen: &mut PenStream, da: &DrawingArea, cs: &mut Vec<DrawCommand>) {
    pen.clear_all_recs();
    let (da_width, da_height) = drawing_area_height_width(da);
    cal_demo3(30.0,
              20.0,
              150.0,
              20.0,
              300.0,
              20.0,
              30.0,
              20.0,
              20.0,
              da_width as f32,
              da_height as f32,
              pen);
    cs.push(DrawCommand::DrawAll);
    da.queue_draw_area(149, 19, da_width - 148, da_height - 38);
}

fn draw_demo2_key_release(x: i32,
                          y: i32,
                          da_w: i32,
                          da_h: i32,
                          da: &DrawingArea,
                          cs: &mut Vec<DrawCommand>) {
    cs.push(DrawCommand::DrawAll);
    da.queue_draw_area(x - 1, y - 1, da_w - 58, da_h - 38);
}

fn drawing_area_height_width(da: &DrawingArea) -> (i32, i32) {
    (da.get_allocated_width(), da.get_allocated_height())
}
<commit_msg>Remove constraints not needed from demo2. Remove println! statements.<commit_after>use gtk::{WidgetExt, DrawingArea};
use cassowary;
use cassowary::math::variables::{new_var, new_const};
use cassowary::math::relationships::Relationship;
use cassowary::math::expressions::Expression;
use cassowary::objective::functions::Function;
use cassowary::objective::problems::ProblemType;
use cassowary::objective::constraints::{new_reg_con, SystemOfConstraints};
use pen::PenStream;
use visualiser::DrawCommand;

pub fn cal_demo1(da_width: f32, da_height: f32, pen: &mut PenStream) {
    let x_loc = 30.0;
    let y_loc = 20.0;
    let side_margin = 30.0;
    let top_margin = 20.0;
    let exp1 = Expression::new(vec![new_var("P", 1.0)],
                               Relationship::EQ,
                               vec![new_var("width", 1.0), new_var("height", 1.0)]);
    let exp2 = Expression::new(vec![new_var("width", 1.0)],
                               Relationship::LEQ,
                               vec![new_const("con1", da_width - 2.0 * side_margin)]);
    let exp3 = Expression::new(vec![new_var("height", 1.0)],
                               Relationship::LEQ,
                               vec![new_const("con2", da_height - 2.0 * top_margin)]);
    let mut objective_func = Function::new(exp1, ProblemType::MAX);
    let c1 = new_reg_con(exp2);
    let c2 = new_reg_con(exp3);
    let subject_to = SystemOfConstraints::new(vec![c1, c2]);
    let solution = cassowary::optimise(&mut objective_func, &subject_to);
    let width = solution.iter()
        .find(|&entry| entry.0 == "width")
        .unwrap()
        .1;
    let height = solution.iter()
        .find(|&entry| entry.0 == "height")
        .unwrap()
        .1;
    pen.add_rec_to_draw(x_loc as f64, y_loc as f64, width as f64, height as f64);
}

pub fn cal_demo2(x_loc1: f32,
                 y_loc1: f32,
                 x_loc2: f32,
                 y_loc2: f32,
                 side_margin: f32,
                 mid_margin: f32,
                 top_margin: f32,
                 da_width: f32,
                 da_height: f32,
                 pen: &mut PenStream) {
    let exp1 = Expression::new(vec![new_var("P", 1.0)],
                               Relationship::EQ,
                               vec![new_var("width1", 0.5),
                                    new_var("height1", 1.0),
                                    new_var("width2", 1.0),
                                    new_var("height2", 1.0)]);
    let exp2 = Expression::new(vec![new_var("width1", 1.0), new_var("width2", 1.0)],
                               Relationship::LEQ,
                               vec![new_const("con1",
                                              da_width - (2.0 * side_margin) - mid_margin)]);
    let exp3 = Expression::new(vec![new_var("height1", 1.0)],
                               Relationship::LEQ,
                               vec![new_const("con2", da_height - 2.0 * top_margin)]);
    let exp4 = Expression::new(vec![new_var("height2", 1.0)],
                               Relationship::LEQ,
                               vec![new_const("con3", da_height - 2.0 * top_margin)]);
    let exp5 = Expression::new(vec![new_var("width1", 1.0)],
                               Relationship::LEQ,
                               vec![new_const("con4", x_loc2 - x_loc1 - mid_margin)]);
    let exp6 = Expression::new(vec![new_var("width2", 1.0)],
                               Relationship::LEQ,
                               vec![new_const("con4", da_width - (x_loc2 + side_margin))]);
    let mut objective_func = Function::new(exp1, ProblemType::MAX);
    let c1 = new_reg_con(exp2);
    let c2 = new_reg_con(exp3);
    let c3 = new_reg_con(exp4);
    let c4 = new_reg_con(exp5);
    let c5 = new_reg_con(exp6);
    let subject_to = SystemOfConstraints::new(vec![c1, c2, c3, c4, c5]);
    let solution = cassowary::optimise(&mut objective_func, &subject_to);
    let width1 = solution.iter()
        .find(|&entry| entry.0 == "width1")
        .unwrap()
        .1;
    let height1 = solution.iter()
        .find(|&entry| entry.0 == "height1")
        .unwrap()
        .1;
    let width2 = solution.iter()
        .find(|&entry| entry.0 == "width2")
        .unwrap()
        .1;
    let height2 = solution.iter()
        .find(|&entry| entry.0 == "height2")
        .unwrap()
        .1;
    pen.add_rec_to_draw(x_loc1 as f64, y_loc1 as f64, width1 as f64, height1 as f64);
    pen.add_rec_to_draw(x_loc2 as f64, y_loc2 as f64, width2 as f64, height2 as f64);
}

pub fn demo2_key_release(left: bool,
                         ri: usize,
                         pen: &mut PenStream,
                         da: &DrawingArea,
                         cs: &mut Vec<DrawCommand>) {
    let (x, y) = match left {
        true => {
            let rec_info = pen.rectangle_info(ri);
            (rec_info.0 - 5, rec_info.1)
        }
        false => {
            let rec_info = pen.rectangle_info(ri);
            (rec_info.0 + 5, rec_info.1)
        }
    };

    match ri {
        0 => {
            let (da_width, da_height) = drawing_area_height_width(da);
            let (x2, y2, _, _) = pen.rectangle_info(1);
            pen.clear_all_recs();
            cal_demo2(x as f32,
                      y as f32,
                      x2 as f32,
                      y2 as f32,
                      30.0,
                      20.0,
                      20.0,
                      da_width as f32,
                      da_height as f32,
                      pen);
            draw_demo2_key_release(x - 5, y, da_width, da_height, da, cs);
        }
        _ => {
            let (da_width, da_height) = drawing_area_height_width(da);
            let (x1, y1, _, _) = pen.rectangle_info(0);
            pen.clear_all_recs();
            cal_demo2(x1 as f32,
                      y1 as f32,
                      x as f32,
                      y as f32,
                      30.0,
                      20.0,
                      20.0,
                      da_width as f32,
                      da_height as f32,
                      pen);
            draw_demo2_key_release(x1, y1, da_width, da_height, da, cs);
        }
    }
}

pub fn cal_demo3(x_loc1: f32,
                 y_loc1: f32,
                 x_loc2: f32,
                 y_loc2: f32,
                 x_loc3: f32,
                 y_loc3: f32,
                 side_margin: f32,
                 mid_margin: f32,
                 top_margin: f32,
                 da_width: f32,
                 da_height: f32,
                 pen: &mut PenStream) {
    let exp1 = Expression::new(vec![new_var("P", 1.0)],
                               Relationship::EQ,
                               vec![new_var("width1", 1.0),
                                    new_var("height1", 1.0),
                                    new_var("width2", 1.0),
                                    new_var("height2", 1.0),
                                    new_var("width3", 1.0),
                                    new_var("height3", 1.0)]);
    let exp2 = Expression::new(vec![new_var("width2", 1.0), new_var("width3", 1.0)],
                               Relationship::LEQ,
                               vec![new_const("con1",
                                              da_width - (2.0 * side_margin) - (2.0 * mid_margin) -
                                              100.0)]);
    let exp3 = Expression::new(vec![new_var("height2", 1.0)],
                               Relationship::LEQ,
                               vec![new_const("con2", da_height - (2.0 * top_margin))]);
    let exp4 = Expression::new(vec![new_var("height3", 1.0)],
                               Relationship::LEQ,
                               vec![new_const("con3", da_height - (2.0 * top_margin))]);
    let exp5 = Expression::new(vec![new_var("height1", 1.0)],
                               Relationship::EQ,
                               vec![new_const("con4", 100.0)]);
    let exp6 = Expression::new(vec![new_var("width2", 1.0)],
                               Relationship::LEQ,
                               vec![new_const("con5", x_loc3 - x_loc2 - mid_margin)]);
    let exp7 = Expression::new(vec![new_var("width3", 1.0)],
                               Relationship::LEQ,
                               vec![new_const("con6", da_width - (x_loc3 + side_margin))]);
    let exp8 = Expression::new(vec![new_var("width1", 1.0)],
                               Relationship::EQ,
                               vec![new_const("con7", 100.0)]);
    let mut objective_func = Function::new(exp1, ProblemType::MAX);
    let c1 = new_reg_con(exp2);
    let c2 = new_reg_con(exp3);
    let c3 = new_reg_con(exp4);
    let c4 = new_reg_con(exp5);
    let c5 = new_reg_con(exp6);
    let c6 = new_reg_con(exp7);
    let c7 = new_reg_con(exp8);
    let subject_to = SystemOfConstraints::new(vec![c1, c2, c3, c4, c5, c6, c7]);
    let solution = cassowary::optimise(&mut objective_func, &subject_to);
    let width1 = solution.iter()
        .find(|&entry| entry.0 == "width1")
        .unwrap()
        .1;
    let height1 = solution.iter()
        .find(|&entry| entry.0 == "height1")
        .unwrap()
        .1;
    let width2 = solution.iter()
        .find(|&entry| entry.0 == "width2")
        .unwrap()
        .1;
    let height2 = solution.iter()
        .find(|&entry| entry.0 == "height2")
        .unwrap()
        .1;
    let width3 = solution.iter()
        .find(|&entry| entry.0 == "width3")
        .unwrap()
        .1;
    let height3 = solution.iter()
        .find(|&entry| entry.0 == "height3")
        .unwrap()
        .1;
    pen.add_rec_to_draw(x_loc1 as f64, y_loc1 as f64, width1 as f64, height1 as f64);
    pen.add_rec_to_draw(x_loc2 as f64, y_loc2 as f64, width2 as f64, height2 as f64);
    pen.add_rec_to_draw(x_loc3 as f64, y_loc3 as f64, width3 as f64, height3 as f64);
}

pub fn demo3_size_change(pen: &mut PenStream, da: &DrawingArea, cs: &mut Vec<DrawCommand>) {
    pen.clear_all_recs();
    let (da_width, da_height) = drawing_area_height_width(da);
    cal_demo3(30.0,
              20.0,
              150.0,
              20.0,
              300.0,
              20.0,
              30.0,
              20.0,
              20.0,
              da_width as f32,
              da_height as f32,
              pen);
    cs.push(DrawCommand::DrawAll);
    da.queue_draw_area(149, 19, da_width - 148, da_height - 38);
}

fn draw_demo2_key_release(x: i32,
                          y: i32,
                          da_w: i32,
                          da_h: i32,
                          da: &DrawingArea,
                          cs: &mut Vec<DrawCommand>) {
    cs.push(DrawCommand::DrawAll);
    da.queue_draw_area(x - 1, y - 1, da_w - 58, da_h - 38);
}

fn drawing_area_height_width(da: &DrawingArea) -> (i32, i32) {
    (da.get_allocated_width(), da.get_allocated_height())
}
<|endoftext|>


<commit_before>use super::collect_versions;
use crate::{config::Config, env::Env, file_saver::save_to_file, nameutil, version::Version};
use log::info;
use std::{collections::HashMap, fs::File, io::prelude::*};
use toml::{self, value::Table, Value};

pub fn generate(env: &Env) -> String {
    info!("Generating sys Cargo.toml for {}", env.config.library_name);

    let path = env.config.target_path.join("Cargo.toml");

    let mut toml_str = String::new();
    if let Ok(mut file) = File::open(&path) {
        file.read_to_string(&mut toml_str).unwrap();
    }
    let empty = toml_str.trim().is_empty();
    let mut root_table = toml::from_str(&toml_str).unwrap_or_else(|_| Table::new());
    let crate_name = get_crate_name(&env.config, &root_table);

    if empty {
        fill_empty(&mut root_table, env, &crate_name);
    }
    fill_in(&mut root_table, env);

    save_to_file(&path, env.config.make_backup, |w| {
        w.write_all(toml::to_string(&root_table).unwrap().as_bytes())
    });

    crate_name
}

fn fill_empty(root: &mut Table, env: &Env, crate_name: &str) {
    let package_name = nameutil::exported_crate_name(crate_name);

    {
        let package = upsert_table(root, "package");
        set_string(package, "name", package_name);
        set_string(package, "version", "0.0.1");
        set_string(
            package,
            "links",
            nameutil::crate_name(&env.config.library_name),
        );
    }

    {
        let lib = upsert_table(root, "lib");
        set_string(lib, "name", crate_name);
    }

    let deps = upsert_table(root, "dependencies");
    for ext_lib in &env.config.external_libraries {
        let ext_package = if ext_lib.crate_name == "cairo" {
            format!("{}-sys-rs", ext_lib.crate_name)
        } else if ext_lib.crate_name == "gdk_pixbuf" {
            "gdk-pixbuf-sys".into()
        } else {
            format!("{}-sys", ext_lib.crate_name)
        };
        let dep = upsert_table(deps, &*ext_package);
        if ext_lib.crate_name == "cairo" {
            set_string(dep, "git", "https://github.com/gtk-rs/cairo");
        } else if ext_package.starts_with("sourceview") {
            set_string(dep, "git", "https://github.com/gtk-rs/sourceview");
        } else {
            set_string(dep, "git", "https://github.com/gtk-rs/sys");
        }
    }
}

fn fill_in(root: &mut Table, env: &Env) {
    {
        let package = upsert_table(root, "package");
        set_string(package, "build", "build.rs");
        //set_string(package, "version", "0.2.0");
    }

    {
        let deps = upsert_table(root, "dependencies");
        set_string(deps, "libc", "0.2");
    }

    {
        let build_deps = upsert_table(root, "build-dependencies");
        set_string(build_deps, "system-deps", "1.3");
        unset(build_deps, "pkg-config");
    }

    {
        let dev_deps = upsert_table(root, "dev-dependencies");
        set_string(dev_deps, "shell-words", "0.1.0");
        set_string(dev_deps, "tempfile", "3");
        unset(dev_deps, "tempdir");
    }

    {
        let features = upsert_table(root, "features");
        let versions = collect_versions(env);
        versions.keys().fold(None::<Version>, |prev, &version| {
            let prev_array: Vec<Value> =
                get_feature_dependencies(version, prev, &env.config.feature_dependencies)
                    .iter()
                    .map(|s| Value::String(s.clone()))
                    .collect();
            features.insert(version.to_feature(), Value::Array(prev_array));
            Some(version)
        });
        features.insert("dox".to_string(), Value::Array(Vec::new()));
    }

    {
        let meta = upsert_table(root, "package");
        let meta = upsert_table(meta, "metadata");
        let meta = upsert_table(meta, "system-deps");

        let ns = env.namespaces.main();
        let lib_name = ns.package_name.as_ref().unwrap();

        let meta = upsert_table(meta, nameutil::lib_name_to_toml(lib_name));
        set_string(meta, "name", lib_name);
        set_string(meta, "version", env.config.min_cfg_version.to_string());

        let versions = upsert_table(meta, "feature-versions");
        collect_versions(env)
            .keys()
            .filter(|&&v| v > env.config.min_cfg_version)
            .for_each(|v| {
                set_string(versions, &v.to_feature(), v.to_string());
            });
    }

    {
        // Small trick to prevent having double quotes around it since toml doesn't like having '.'
        let docs_rs_metadata = upsert_table(root, "package");
        let docs_rs_metadata = upsert_table(docs_rs_metadata, "metadata");
        let docs_rs_metadata = upsert_table(docs_rs_metadata, "docs");
        let docs_rs_metadata = upsert_table(docs_rs_metadata, "rs");
        let mut docs_rs_features = env.config.docs_rs_features.clone();
        docs_rs_features.push("dox".to_owned());
        docs_rs_metadata.insert(
            "features".to_string(),
            Value::Array(
                docs_rs_features
                    .into_iter()
                    .map(Value::String)
                    .collect::<Vec<_>>(),
            ),
        );
    }
}

fn get_feature_dependencies(
    version: Version,
    prev_version: Option<Version>,
    feature_dependencies: &HashMap<Version, Vec<String>>,
) -> Vec<String> {
    let mut vec = Vec::with_capacity(10);
    if let Some(v) = prev_version {
        vec.push(v.to_feature());
    }
    if let Some(dependencies) = feature_dependencies.get(&version) {
        vec.extend_from_slice(dependencies);
    }
    vec
}

/// Returns the name of crate being currently generated.
fn get_crate_name(config: &Config, root: &Table) -> String {
    if let Some(&Value::Table(ref lib)) = root.get("lib") {
        if let Some(&Value::String(ref lib_name)) = lib.get("name") {
            //Converting don't needed as library target names cannot contain hyphens
            return lib_name.to_owned();
        }
    }
    if let Some(&Value::Table(ref package)) = root.get("package") {
        if let Some(&Value::String(ref package_name)) = package.get("name") {
            return nameutil::crate_name(package_name);
        }
    }
    return format!("{}_sys", nameutil::crate_name(&config.library_name));
}

fn set_string<S: Into<String>>(table: &mut Table, name: &str, new_value: S) {
    if let Some(v) = table.get_mut(name) {
        *v = Value::String(new_value.into());
        return;
    }
    table.insert(name.into(), Value::String(new_value.into()));
}

fn unset(table: &mut Table, name: &str) {
    table.remove(name);
}

fn upsert_table<S: Into<String>>(parent: &mut Table, name: S) -> &mut Table {
    if let Value::Table(ref mut table) = *parent
        .entry(name.into())
        .or_insert_with(|| Value::Table(toml::map::Map::new()))
    {
        table
    } else {
        unreachable!()
    }
}
<commit_msg>cargo_toml: use lib_version_overrides when defining feature versions<commit_after>use super::collect_versions;
use crate::{config::Config, env::Env, file_saver::save_to_file, nameutil, version::Version};
use log::info;
use std::{collections::HashMap, fs::File, io::prelude::*};
use toml::{self, value::Table, Value};

pub fn generate(env: &Env) -> String {
    info!("Generating sys Cargo.toml for {}", env.config.library_name);

    let path = env.config.target_path.join("Cargo.toml");

    let mut toml_str = String::new();
    if let Ok(mut file) = File::open(&path) {
        file.read_to_string(&mut toml_str).unwrap();
    }
    let empty = toml_str.trim().is_empty();
    let mut root_table = toml::from_str(&toml_str).unwrap_or_else(|_| Table::new());
    let crate_name = get_crate_name(&env.config, &root_table);

    if empty {
        fill_empty(&mut root_table, env, &crate_name);
    }
    fill_in(&mut root_table, env);

    save_to_file(&path, env.config.make_backup, |w| {
        w.write_all(toml::to_string(&root_table).unwrap().as_bytes())
    });

    crate_name
}

fn fill_empty(root: &mut Table, env: &Env, crate_name: &str) {
    let package_name = nameutil::exported_crate_name(crate_name);

    {
        let package = upsert_table(root, "package");
        set_string(package, "name", package_name);
        set_string(package, "version", "0.0.1");
        set_string(
            package,
            "links",
            nameutil::crate_name(&env.config.library_name),
        );
    }

    {
        let lib = upsert_table(root, "lib");
        set_string(lib, "name", crate_name);
    }

    let deps = upsert_table(root, "dependencies");
    for ext_lib in &env.config.external_libraries {
        let ext_package = if ext_lib.crate_name == "cairo" {
            format!("{}-sys-rs", ext_lib.crate_name)
        } else if ext_lib.crate_name == "gdk_pixbuf" {
            "gdk-pixbuf-sys".into()
        } else {
            format!("{}-sys", ext_lib.crate_name)
        };
        let dep = upsert_table(deps, &*ext_package);
        if ext_lib.crate_name == "cairo" {
            set_string(dep, "git", "https://github.com/gtk-rs/cairo");
        } else if ext_package.starts_with("sourceview") {
            set_string(dep, "git", "https://github.com/gtk-rs/sourceview");
        } else {
            set_string(dep, "git", "https://github.com/gtk-rs/sys");
        }
    }
}

fn fill_in(root: &mut Table, env: &Env) {
    {
        let package = upsert_table(root, "package");
        set_string(package, "build", "build.rs");
        //set_string(package, "version", "0.2.0");
    }

    {
        let deps = upsert_table(root, "dependencies");
        set_string(deps, "libc", "0.2");
    }

    {
        let build_deps = upsert_table(root, "build-dependencies");
        set_string(build_deps, "system-deps", "1.3");
        unset(build_deps, "pkg-config");
    }

    {
        let dev_deps = upsert_table(root, "dev-dependencies");
        set_string(dev_deps, "shell-words", "0.1.0");
        set_string(dev_deps, "tempfile", "3");
        unset(dev_deps, "tempdir");
    }

    {
        let features = upsert_table(root, "features");
        let versions = collect_versions(env);
        versions.keys().fold(None::<Version>, |prev, &version| {
            let prev_array: Vec<Value> =
                get_feature_dependencies(version, prev, &env.config.feature_dependencies)
                    .iter()
                    .map(|s| Value::String(s.clone()))
                    .collect();
            features.insert(version.to_feature(), Value::Array(prev_array));
            Some(version)
        });
        features.insert("dox".to_string(), Value::Array(Vec::new()));
    }

    {
        let meta = upsert_table(root, "package");
        let meta = upsert_table(meta, "metadata");
        let meta = upsert_table(meta, "system-deps");

        let ns = env.namespaces.main();
        let lib_name = ns.package_name.as_ref().unwrap();

        let meta = upsert_table(meta, nameutil::lib_name_to_toml(lib_name));
        set_string(meta, "name", lib_name);
        set_string(meta, "version", env.config.min_cfg_version.to_string());

        let versions = upsert_table(meta, "feature-versions");
        collect_versions(env)
            .iter()
            .filter(|(&v, _)| v > env.config.min_cfg_version)
            .for_each(|(v, lib_version)| {
                set_string(versions, &v.to_feature(), lib_version.to_string());
            });
    }

    {
        // Small trick to prevent having double quotes around it since toml doesn't like having '.'
        let docs_rs_metadata = upsert_table(root, "package");
        let docs_rs_metadata = upsert_table(docs_rs_metadata, "metadata");
        let docs_rs_metadata = upsert_table(docs_rs_metadata, "docs");
        let docs_rs_metadata = upsert_table(docs_rs_metadata, "rs");
        let mut docs_rs_features = env.config.docs_rs_features.clone();
        docs_rs_features.push("dox".to_owned());
        docs_rs_metadata.insert(
            "features".to_string(),
            Value::Array(
                docs_rs_features
                    .into_iter()
                    .map(Value::String)
                    .collect::<Vec<_>>(),
            ),
        );
    }
}

fn get_feature_dependencies(
    version: Version,
    prev_version: Option<Version>,
    feature_dependencies: &HashMap<Version, Vec<String>>,
) -> Vec<String> {
    let mut vec = Vec::with_capacity(10);
    if let Some(v) = prev_version {
        vec.push(v.to_feature());
    }
    if let Some(dependencies) = feature_dependencies.get(&version) {
        vec.extend_from_slice(dependencies);
    }
    vec
}

/// Returns the name of crate being currently generated.
fn get_crate_name(config: &Config, root: &Table) -> String {
    if let Some(&Value::Table(ref lib)) = root.get("lib") {
        if let Some(&Value::String(ref lib_name)) = lib.get("name") {
            //Converting don't needed as library target names cannot contain hyphens
            return lib_name.to_owned();
        }
    }
    if let Some(&Value::Table(ref package)) = root.get("package") {
        if let Some(&Value::String(ref package_name)) = package.get("name") {
            return nameutil::crate_name(package_name);
        }
    }
    return format!("{}_sys", nameutil::crate_name(&config.library_name));
}

fn set_string<S: Into<String>>(table: &mut Table, name: &str, new_value: S) {
    if let Some(v) = table.get_mut(name) {
        *v = Value::String(new_value.into());
        return;
    }
    table.insert(name.into(), Value::String(new_value.into()));
}

fn unset(table: &mut Table, name: &str) {
    table.remove(name);
}

fn upsert_table<S: Into<String>>(parent: &mut Table, name: S) -> &mut Table {
    if let Value::Table(ref mut table) = *parent
        .entry(name.into())
        .or_insert_with(|| Value::Table(toml::map::Map::new()))
    {
        table
    } else {
        unreachable!()
    }
}
<|endoftext|>