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

#![forbid(unsafe_code)]

use serde::{Deserialize, Serialize};
use serde_json::json;
use std::env;
use thiserror::Error;

/// Request timeout for github operations
const ACCEPT_HEADER: &str = "Accept";
const ACCEPT_VALUE: &str = "Accept: application/vnd.github.v3+json";
const TIMEOUT: u64 = 10_000;
const URL: &str = "https://api.github.com";

#[derive(Debug, Error, PartialEq)]
pub enum Error {
    #[error("Http error: {1}")]
    HttpError(u16, String),
    #[error("Internal error: {0}")]
    InternalError(String),
    #[error("Missing field {0}")]
    MissingField(String),
    #[error("404: Not Found: {0}")]
    NotFound(String),
    #[error("Serialization error: {0}")]
    SerializationError(String),
}

impl From<std::io::Error> for Error {
    fn from(error: std::io::Error) -> Self {
        Self::SerializationError(format!("{}", error))
    }
}

impl From<ureq::Response> for Error {
    fn from(resp: ureq::Response) -> Self {
        Error::HttpError(resp.status(), resp.status_line().into())
    }
}

impl From<serde_json::Error> for Error {
    fn from(error: serde_json::Error) -> Self {
        Self::SerializationError(format!("{}", error))
    }
}

/// Client provides a client around the restful interface to GitHub API version 3. Learn more
/// here: https://developer.github.com/v3
///
/// This is not intended for securely storing private data, though perhaps it could with a private
/// repository. The tooling is intended to be used to exchange data in an authenticated fashion
/// across multiple peers.
pub struct Client {
    repository: String,
    owner: String,
    token: String,
}

impl Client {
    pub fn new(owner: String, repository: String, token: String) -> Self {
        Self {
            repository,
            owner,
            token,
        }
    }

    /// Delete a file from a GitHub repository
    pub fn delete_file(&self, path: &str) -> Result<(), Error> {
        // Occasionally GitHub sends us back delayed results and the file is already deleted.
        let hash = match self.get_sha(path) {
            Ok(hash) => hash,
            Err(Error::NotFound(_)) => return Ok(()),
            Err(e) => return Err(e),
        };

        let resp = self
            .upgrade_request(ureq::delete(&self.url(path)))
            .send_json(json!({ "message": "libra-secure", "sha": hash }));

        match resp.status() {
            200 => Ok(()),
            _ => Err(resp.into()),
        }
    }

    /// Recursively delete all files, which as a by product will delete all folders
    pub fn delete_directory(&self, path: &str) -> Result<(), Error> {
        let files = self.get_directory(path)?;
        for file in files {
            if file.ends_with('/') {
                self.delete_directory(&file[..file.len() - 1])?;
                continue;
            }
            self.delete_file(&file)?;
        }
        Ok(())
    }

    /// Retrieve a list of branches, this is effectively a status check on the repository
    pub fn get_branches(&self) -> Result<Vec<String>, Error> {
        let url = format!("{}/repos/{}/{}/branches", URL, self.owner, self.repository);
        let resp = self.upgrade_request(ureq::get(&url)).call();

        match resp.status() {
            200 => {
                let resp = resp.into_string()?;
                let branches: Vec<Branch> = serde_json::from_str(&resp)?;
                Ok(branches.into_iter().map(|b| b.name).collect())
            }
            _ => Err(resp.into()),
        }
    }

    /// Retrieve the name of contents within a given directory, note, there are no such thing as
    /// empty directories in git.
    pub fn get_directory(&self, path: &str) -> Result<Vec<String>, Error> {
        Ok(self
            .get_internal(path)?
            .into_iter()
            .map(|entry| {
                if entry.content_type == "dir" {
                    entry.path + "/"
                } else {
                    entry.path
                }
            })
            .collect())
    }

    /// Retrieve the contents of a file.
    pub fn get_file(&self, path: &str) -> Result<String, Error> {
        let value = self.get_internal(path)?;
        if value.len() == 1 && value[0].path == path {
            let content = value[0]
                .content
                .as_ref()
                .ok_or_else(|| Error::InternalError("No content found".into()))?;
            // Apparently GitHub introduces newlines every 60 characters and at the end of content,
            // this strips those characters out.
            Ok(content.lines().collect::<Vec<_>>().join(""))
        } else {
            Err(Error::InternalError(format!(
                "get mismatch, found {} entries",
                value.len()
            )))
        }
    }

    /// Create or update a file.
    pub fn put(&self, path: &str, content: &str) -> Result<(), Error> {
        let json = match self.get_sha(path) {
            Ok(hash) => json!({ "content": content, "message": "libra-secure", "sha": hash }),
            Err(Error::NotFound(_)) => json!({ "content": content, "message": "libra-secure" }),
            Err(e) => return Err(e),
        };

        let resp = self
            .upgrade_request(ureq::put(&self.url(path)))
            .send_json(json);

        match resp.status() {
            200 => Ok(()),
            201 => Ok(()),
            _ => Err(resp.into()),
        }
    }

    /// Simple wrapper around requests to add default parameters to the request
    fn upgrade_request(&self, mut request: ureq::Request) -> ureq::Request {
        request
            .set("Authorization", &format!("token {}", self.token))
            .set(ACCEPT_HEADER, ACCEPT_VALUE)
            .timeout_connect(TIMEOUT);
        match env::var("https_proxy") {
            Ok(proxy) => request
                .set_proxy(
                    ureq::Proxy::new(proxy)
                        .expect("Unable to parse https_proxy environment variable"),
                )
                .build(),
            Err(_e) => request,
        }
    }

    /// Get can read files or directories, this makes it easier to use
    fn get_internal(&self, path: &str) -> Result<Vec<GetResponse>, Error> {
        let resp = self.upgrade_request(ureq::get(&self.url(path))).call();
        match resp.status() {
            200 => {
                let resp = resp.into_string()?;
                let get_resp: Result<GetResponse, Error> =
                    serde_json::from_str(&resp).map_err(|e| e.into());

                if let Ok(get_resp) = get_resp {
                    return Ok(vec![get_resp]);
                }

                let get_resp: Result<Vec<GetResponse>, Error> =
                    serde_json::from_str(&resp).map_err(|e| e.into());
                if let Ok(get_resp) = get_resp {
                    return Ok(get_resp);
                }

                Err(Error::SerializationError(resp))
            }
            404 => Err(Error::NotFound(path.into())),
            _ => Err(resp.into()),
        }
    }

    /// Returns the sha hash of a file according to GitHub
    fn get_sha(&self, path: &str) -> Result<String, Error> {
        let value = self.get_internal(path)?;
        if let Some(value) = value.iter().find(|entry| entry.path == path) {
            Ok(value.sha.clone())
        } else {
            // This is a directory
            Err(Error::InternalError(format!(
                "get mismatch, found {} entries",
                value.len()
            )))
        }
    }

    fn url(&self, path: &str) -> String {
        format!(
            "{}/repos/{}/{}/contents/{}",
            URL, self.owner, self.repository, path
        )
    }
}

#[derive(Debug, Deserialize, PartialEq, Serialize)]
struct Branch {
    name: String,
}

#[derive(Debug, Deserialize, PartialEq, Serialize)]
struct GetResponse {
    #[serde(rename = "type")]
    content_type: String,
    path: String,
    sha: String,
    content: Option<String>,
}

// This test suite depends on the developer providing an active repository, the account where that
// repository is hosted, and a token that can modify that repository. Due to the nature of these
// tests, they cannot be provided in this framework and the tests are labeled as ignored
//
// GitHub updates can misbehave due to changes in file structure thus dependent operations can
// fail, for example, an update of a file reads its hash and then updates. If another operation
// happens after the hash retrieval but before the update, the hash will change resulting in a
// failed update. Therefore these tests must be run in series via:
// `cargo xtest -- --ignored --test-threads=1`
#[cfg(test)]
mod tests {
    use super::*;

    const OWNER: &str = "OWNER";
    const REPOSITORY: &str = "REPOSITORY";
    const TOKEN: &str = "TOKEN";

    #[ignore]
    #[test]
    fn test_files() {
        let path = "data.txt";
        let value1 = "hello";
        let value1_encoded = base64::encode(&value1);
        let value2 = "world";
        let value2_encoded = base64::encode(&value2);

        let github = Client::new(OWNER.into(), REPOSITORY.into(), TOKEN.into());

        // Try a create
        github.get_file(path).unwrap_err();
        github.put(path, &value1_encoded).unwrap();
        let b64_value = base64::decode(github.get_file(path).unwrap()).unwrap();
        let value = std::str::from_utf8(&b64_value).unwrap();
        assert_eq!(value, value1);

        // Try an update
        github.put(path, &value2_encoded).unwrap();
        let b64_value = base64::decode(github.get_file(path).unwrap()).unwrap();
        let value = std::str::from_utf8(&b64_value).unwrap();
        assert_eq!(value, value2);

        // Verify delete_file
        github.delete_file(path).unwrap();
        github.get_file(path).unwrap_err();
    }

    #[ignore]
    #[test]
    fn test_directories() {
        let path1_root = "dir";
        let path1 = "dir/data1.txt";
        let value1 = "hello";
        let value1_encoded = base64::encode(&value1);

        let path2_root = "dir1";
        let path2 = "dir1/data1.txt";
        let value2 = "world";
        let value2_encoded = base64::encode(&value2);

        let github = Client::new(OWNER.into(), REPOSITORY.into(), TOKEN.into());

        // Initialize two directories with a file each
        github.put(path1, &value1_encoded).unwrap();
        github.put(path2, &value2_encoded).unwrap();

        // Verify the contents
        let b64_value = base64::decode(github.get_file(path1).unwrap()).unwrap();
        let value = std::str::from_utf8(&b64_value).unwrap();
        assert_eq!(value, value1);

        let b64_value = base64::decode(github.get_file(path2).unwrap()).unwrap();
        let value = std::str::from_utf8(&b64_value).unwrap();
        assert_eq!(value, value2);

        // Delete one of the directories
        github.delete_file(path1).unwrap();

        // Verify one is good and the other is gone
        github.get_directory(path1_root).unwrap_err();

        let b64_value = base64::decode(github.get_file(path2).unwrap()).unwrap();
        let value = std::str::from_utf8(&b64_value).unwrap();
        assert_eq!(value, value2);

        // Cleanup the rest
        github.delete_file(path2).unwrap();
        github.get_directory(path2_root).unwrap_err();
    }

    #[ignore]
    #[test]
    fn test_recursive_delete_file() {
        let root = "root_dir";
        let file0 = "root_dir/another_dir/another_dir/ok.txt";
        let file1 = "root_dir/another_dir/ok.txt";
        let value = "hello";
        let value_encoded = base64::encode(&value);

        let github = Client::new(OWNER.into(), REPOSITORY.into(), TOKEN.into());

        github.put(file0, &value_encoded).unwrap();
        github.put(file1, &value_encoded).unwrap();

        let b64_value = base64::decode(github.get_file(file0).unwrap()).unwrap();
        let rv = std::str::from_utf8(&b64_value).unwrap();
        assert_eq!(value, rv);

        let b64_value = base64::decode(github.get_file(file1).unwrap()).unwrap();
        let rv = std::str::from_utf8(&b64_value).unwrap();
        assert_eq!(value, rv);

        github.delete_directory(root).unwrap();
        github.get_file(file0).unwrap_err();
        github.get_file(file1).unwrap_err();
    }

    #[ignore]
    #[test]
    fn test_branches() {
        let github = Client::new(OWNER.into(), REPOSITORY.into(), TOKEN.into());
        let branches = github.get_branches().unwrap();
        assert!(!branches.is_empty());
        assert!(branches.iter().any(|b| b == "master"));
    }
}
<commit_msg>[secure-storage] More useful Github commit message<commit_after>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

#![forbid(unsafe_code)]

use serde::{Deserialize, Serialize};
use serde_json::json;
use std::env;
use thiserror::Error;

/// Request timeout for github operations
const ACCEPT_HEADER: &str = "Accept";
const ACCEPT_VALUE: &str = "Accept: application/vnd.github.v3+json";
const TIMEOUT: u64 = 10_000;
const URL: &str = "https://api.github.com";

#[derive(Debug, Error, PartialEq)]
pub enum Error {
    #[error("Http error: {1}")]
    HttpError(u16, String),
    #[error("Internal error: {0}")]
    InternalError(String),
    #[error("Missing field {0}")]
    MissingField(String),
    #[error("404: Not Found: {0}")]
    NotFound(String),
    #[error("Serialization error: {0}")]
    SerializationError(String),
}

impl From<std::io::Error> for Error {
    fn from(error: std::io::Error) -> Self {
        Self::SerializationError(format!("{}", error))
    }
}

impl From<ureq::Response> for Error {
    fn from(resp: ureq::Response) -> Self {
        Error::HttpError(resp.status(), resp.status_line().into())
    }
}

impl From<serde_json::Error> for Error {
    fn from(error: serde_json::Error) -> Self {
        Self::SerializationError(format!("{}", error))
    }
}

/// Client provides a client around the restful interface to GitHub API version 3. Learn more
/// here: https://developer.github.com/v3
///
/// This is not intended for securely storing private data, though perhaps it could with a private
/// repository. The tooling is intended to be used to exchange data in an authenticated fashion
/// across multiple peers.
pub struct Client {
    repository: String,
    owner: String,
    token: String,
}

impl Client {
    pub fn new(owner: String, repository: String, token: String) -> Self {
        Self {
            repository,
            owner,
            token,
        }
    }

    /// Delete a file from a GitHub repository
    pub fn delete_file(&self, path: &str) -> Result<(), Error> {
        // Occasionally GitHub sends us back delayed results and the file is already deleted.
        let hash = match self.get_sha(path) {
            Ok(hash) => hash,
            Err(Error::NotFound(_)) => return Ok(()),
            Err(e) => return Err(e),
        };

        let resp = self
            .upgrade_request(ureq::delete(&self.url(path)))
            .send_json(json!({ "message": "libra-secure", "sha": hash }));

        match resp.status() {
            200 => Ok(()),
            _ => Err(resp.into()),
        }
    }

    /// Recursively delete all files, which as a by product will delete all folders
    pub fn delete_directory(&self, path: &str) -> Result<(), Error> {
        let files = self.get_directory(path)?;
        for file in files {
            if file.ends_with('/') {
                self.delete_directory(&file[..file.len() - 1])?;
                continue;
            }
            self.delete_file(&file)?;
        }
        Ok(())
    }

    /// Retrieve a list of branches, this is effectively a status check on the repository
    pub fn get_branches(&self) -> Result<Vec<String>, Error> {
        let url = format!("{}/repos/{}/{}/branches", URL, self.owner, self.repository);
        let resp = self.upgrade_request(ureq::get(&url)).call();

        match resp.status() {
            200 => {
                let resp = resp.into_string()?;
                let branches: Vec<Branch> = serde_json::from_str(&resp)?;
                Ok(branches.into_iter().map(|b| b.name).collect())
            }
            _ => Err(resp.into()),
        }
    }

    /// Retrieve the name of contents within a given directory, note, there are no such thing as
    /// empty directories in git.
    pub fn get_directory(&self, path: &str) -> Result<Vec<String>, Error> {
        Ok(self
            .get_internal(path)?
            .into_iter()
            .map(|entry| {
                if entry.content_type == "dir" {
                    entry.path + "/"
                } else {
                    entry.path
                }
            })
            .collect())
    }

    /// Retrieve the contents of a file.
    pub fn get_file(&self, path: &str) -> Result<String, Error> {
        let value = self.get_internal(path)?;
        if value.len() == 1 && value[0].path == path {
            let content = value[0]
                .content
                .as_ref()
                .ok_or_else(|| Error::InternalError("No content found".into()))?;
            // Apparently GitHub introduces newlines every 60 characters and at the end of content,
            // this strips those characters out.
            Ok(content.lines().collect::<Vec<_>>().join(""))
        } else {
            Err(Error::InternalError(format!(
                "get mismatch, found {} entries",
                value.len()
            )))
        }
    }

    /// Create or update a file.
    pub fn put(&self, path: &str, content: &str) -> Result<(), Error> {
        let json = match self.get_sha(path) {
            Ok(hash) => {
                json!({ "content": content, "message": format!("[libra-management] {}", path), "sha": hash })
            }
            Err(Error::NotFound(_)) => {
                json!({ "content": content, "message": format!("[libra-management] {}", path) })
            }
            Err(e) => return Err(e),
        };

        let resp = self
            .upgrade_request(ureq::put(&self.url(path)))
            .send_json(json);

        match resp.status() {
            200 => Ok(()),
            201 => Ok(()),
            _ => Err(resp.into()),
        }
    }

    /// Simple wrapper around requests to add default parameters to the request
    fn upgrade_request(&self, mut request: ureq::Request) -> ureq::Request {
        request
            .set("Authorization", &format!("token {}", self.token))
            .set(ACCEPT_HEADER, ACCEPT_VALUE)
            .timeout_connect(TIMEOUT);
        match env::var("https_proxy") {
            Ok(proxy) => request
                .set_proxy(
                    ureq::Proxy::new(proxy)
                        .expect("Unable to parse https_proxy environment variable"),
                )
                .build(),
            Err(_e) => request,
        }
    }

    /// Get can read files or directories, this makes it easier to use
    fn get_internal(&self, path: &str) -> Result<Vec<GetResponse>, Error> {
        let resp = self.upgrade_request(ureq::get(&self.url(path))).call();
        match resp.status() {
            200 => {
                let resp = resp.into_string()?;
                let get_resp: Result<GetResponse, Error> =
                    serde_json::from_str(&resp).map_err(|e| e.into());

                if let Ok(get_resp) = get_resp {
                    return Ok(vec![get_resp]);
                }

                let get_resp: Result<Vec<GetResponse>, Error> =
                    serde_json::from_str(&resp).map_err(|e| e.into());
                if let Ok(get_resp) = get_resp {
                    return Ok(get_resp);
                }

                Err(Error::SerializationError(resp))
            }
            404 => Err(Error::NotFound(path.into())),
            _ => Err(resp.into()),
        }
    }

    /// Returns the sha hash of a file according to GitHub
    fn get_sha(&self, path: &str) -> Result<String, Error> {
        let value = self.get_internal(path)?;
        if let Some(value) = value.iter().find(|entry| entry.path == path) {
            Ok(value.sha.clone())
        } else {
            // This is a directory
            Err(Error::InternalError(format!(
                "get mismatch, found {} entries",
                value.len()
            )))
        }
    }

    fn url(&self, path: &str) -> String {
        format!(
            "{}/repos/{}/{}/contents/{}",
            URL, self.owner, self.repository, path
        )
    }
}

#[derive(Debug, Deserialize, PartialEq, Serialize)]
struct Branch {
    name: String,
}

#[derive(Debug, Deserialize, PartialEq, Serialize)]
struct GetResponse {
    #[serde(rename = "type")]
    content_type: String,
    path: String,
    sha: String,
    content: Option<String>,
}

// This test suite depends on the developer providing an active repository, the account where that
// repository is hosted, and a token that can modify that repository. Due to the nature of these
// tests, they cannot be provided in this framework and the tests are labeled as ignored
//
// GitHub updates can misbehave due to changes in file structure thus dependent operations can
// fail, for example, an update of a file reads its hash and then updates. If another operation
// happens after the hash retrieval but before the update, the hash will change resulting in a
// failed update. Therefore these tests must be run in series via:
// `cargo xtest -- --ignored --test-threads=1`
#[cfg(test)]
mod tests {
    use super::*;

    const OWNER: &str = "OWNER";
    const REPOSITORY: &str = "REPOSITORY";
    const TOKEN: &str = "TOKEN";

    #[ignore]
    #[test]
    fn test_files() {
        let path = "data.txt";
        let value1 = "hello";
        let value1_encoded = base64::encode(&value1);
        let value2 = "world";
        let value2_encoded = base64::encode(&value2);

        let github = Client::new(OWNER.into(), REPOSITORY.into(), TOKEN.into());

        // Try a create
        github.get_file(path).unwrap_err();
        github.put(path, &value1_encoded).unwrap();
        let b64_value = base64::decode(github.get_file(path).unwrap()).unwrap();
        let value = std::str::from_utf8(&b64_value).unwrap();
        assert_eq!(value, value1);

        // Try an update
        github.put(path, &value2_encoded).unwrap();
        let b64_value = base64::decode(github.get_file(path).unwrap()).unwrap();
        let value = std::str::from_utf8(&b64_value).unwrap();
        assert_eq!(value, value2);

        // Verify delete_file
        github.delete_file(path).unwrap();
        github.get_file(path).unwrap_err();
    }

    #[ignore]
    #[test]
    fn test_directories() {
        let path1_root = "dir";
        let path1 = "dir/data1.txt";
        let value1 = "hello";
        let value1_encoded = base64::encode(&value1);

        let path2_root = "dir1";
        let path2 = "dir1/data1.txt";
        let value2 = "world";
        let value2_encoded = base64::encode(&value2);

        let github = Client::new(OWNER.into(), REPOSITORY.into(), TOKEN.into());

        // Initialize two directories with a file each
        github.put(path1, &value1_encoded).unwrap();
        github.put(path2, &value2_encoded).unwrap();

        // Verify the contents
        let b64_value = base64::decode(github.get_file(path1).unwrap()).unwrap();
        let value = std::str::from_utf8(&b64_value).unwrap();
        assert_eq!(value, value1);

        let b64_value = base64::decode(github.get_file(path2).unwrap()).unwrap();
        let value = std::str::from_utf8(&b64_value).unwrap();
        assert_eq!(value, value2);

        // Delete one of the directories
        github.delete_file(path1).unwrap();

        // Verify one is good and the other is gone
        github.get_directory(path1_root).unwrap_err();

        let b64_value = base64::decode(github.get_file(path2).unwrap()).unwrap();
        let value = std::str::from_utf8(&b64_value).unwrap();
        assert_eq!(value, value2);

        // Cleanup the rest
        github.delete_file(path2).unwrap();
        github.get_directory(path2_root).unwrap_err();
    }

    #[ignore]
    #[test]
    fn test_recursive_delete_file() {
        let root = "root_dir";
        let file0 = "root_dir/another_dir/another_dir/ok.txt";
        let file1 = "root_dir/another_dir/ok.txt";
        let value = "hello";
        let value_encoded = base64::encode(&value);

        let github = Client::new(OWNER.into(), REPOSITORY.into(), TOKEN.into());

        github.put(file0, &value_encoded).unwrap();
        github.put(file1, &value_encoded).unwrap();

        let b64_value = base64::decode(github.get_file(file0).unwrap()).unwrap();
        let rv = std::str::from_utf8(&b64_value).unwrap();
        assert_eq!(value, rv);

        let b64_value = base64::decode(github.get_file(file1).unwrap()).unwrap();
        let rv = std::str::from_utf8(&b64_value).unwrap();
        assert_eq!(value, rv);

        github.delete_directory(root).unwrap();
        github.get_file(file0).unwrap_err();
        github.get_file(file1).unwrap_err();
    }

    #[ignore]
    #[test]
    fn test_branches() {
        let github = Client::new(OWNER.into(), REPOSITORY.into(), TOKEN.into());
        let branches = github.get_branches().unwrap();
        assert!(!branches.is_empty());
        assert!(branches.iter().any(|b| b == "master"));
    }
}
<|endoftext|>


<commit_before>use diesel::prelude::*;
use diesel::sqlite::SqliteConnection;

use dotenv;

use clap::{clap_app, crate_version, AppSettings};

use prettytable as pt;
use pt::{cell, row};

use ratfist_server::db::models::Node;

fn get_node_list(db_conn: &SqliteConnection) -> Vec<Node> {
    use ratfist_server::db::schema::nodes::dsl::*;

    nodes.load::<Node>(db_conn).unwrap()
}

fn list_all_nodes(db_conn: &SqliteConnection) {
    let nodes = get_node_list(db_conn);

    let mut table = pt::Table::new();

    table.set_titles(row![
        Fbb =>
        "Public ID",
        "Name",
        "Route Type",
        "Route Type Parameters"
    ]);

    if nodes.is_empty() {
        table.add_row(row![
            H4c =>
            "(none)"
        ]);
    }

    for node in nodes {
        table.add_row(row![
            node.public_id,
            node.name,
            node.route_type,
            node.route_param.unwrap_or("".to_string())
        ]);
    }

    table.printstd();
}

fn main() {
    let matches = clap_app!(meteo_cli =>
        (version: crate_version!())
        (@subcommand list =>
        )
    )
    .setting(AppSettings::SubcommandRequiredElseHelp)
    .get_matches();

    let db_url = dotenv::var("DATABASE_URL").expect("missing DATABASE_URL env variable");

    let db_conn = SqliteConnection::establish(&db_url)
        .expect(&format!("failed to connect to DB: {}", db_url));

    match matches.subcommand() {
        ("list", _) => list_all_nodes(&db_conn),
        _ => panic!(),
    }
}
<commit_msg>Added clap subcommand structure for listing and adding nodes and sensors<commit_after>use diesel::prelude::*;
use diesel::sqlite::SqliteConnection;

use dotenv;

use clap::{arg_enum, crate_version, App, AppSettings, Arg};

use prettytable as pt;
use pt::{cell, row};

use ratfist_server::db::models::Node;

fn print_table(mut title_row: pt::Row, table_rows: Vec<pt::Row>) {
    let was_empty = table_rows.is_empty();
    let table_width = title_row.len();

    let mut table = pt::Table::init(table_rows);

    for title_cell in title_row.iter_mut() {
        title_cell.style(pt::Attr::ForegroundColor(pt::color::BLUE));
    }

    table.set_titles(title_row);

    if was_empty {
        let row = pt::Row::new(vec![pt::Cell::new_align(
            "(none)",
            pt::format::Alignment::CENTER,
        )
        .with_hspan(table_width)]);
        table.add_row(row);
    }

    table.printstd();
}

fn get_node_list(db_conn: &SqliteConnection) -> Vec<Node> {
    use ratfist_server::db::schema::nodes::dsl::*;

    nodes.load::<Node>(db_conn).unwrap()
}

fn list_all_nodes(db_conn: &SqliteConnection) {
    let nodes = get_node_list(db_conn);

    print_table(
        row!["Public ID", "Name", "Route Type", "Route Type Parameters"],
        nodes
            .into_iter()
            .map(|node| {
                row![
                    node.public_id,
                    node.name,
                    node.route_type,
                    node.route_param.unwrap_or("".to_string())
                ]
            })
            .collect(),
    );
}

arg_enum! {
    #[derive(Debug)]
    enum RouteTypes {
        Serial
    }
}

arg_enum! {
    #[derive(Debug)]
    enum SensorTypes {
        Pressure,
        Temperature,
        Humidity,
        LightLevel
    }
}

fn is_positive_integer_i32(arg: String) -> Result<(), String> {
    let err_string = format!("must be a positive integer in [0, {}]", std::i32::MAX);

    let val = arg.parse::<i32>().map_err(|_| err_string.clone())?;

    if val < 0 {
        Err(err_string)
    } else {
        Ok(())
    }
}

fn main() {
    let matches = App::new("meteo_cli")
        .version(crate_version!())
        .subcommands(vec![
            App::new("list")
                .subcommands(vec![
                    App::new("nodes"),
                    App::new("sensors").arg(
                        Arg::with_name("node_public_id")
                            .required(true)
                            .validator(is_positive_integer_i32),
                    ),
                ])
                .setting(AppSettings::SubcommandRequiredElseHelp),
            App::new("add")
                .subcommands(vec![
                    App::new("node").args(&vec![
                        Arg::with_name("public_id")
                            .required(true)
                            .validator(is_positive_integer_i32),
                        Arg::with_name("name").required(true),
                        Arg::with_name("route_type")
                            .required(true)
                            .possible_values(&RouteTypes::variants()),
                    ]),
                    App::new("sensor").args(&vec![
                        Arg::with_name("node_public_id")
                            .required(true)
                            .validator(is_positive_integer_i32),
                        Arg::with_name("sensor_public_id")
                            .required(true)
                            .validator(is_positive_integer_i32),
                        Arg::with_name("sensor_type")
                            .required(true)
                            .possible_values(&SensorTypes::variants()),
                        Arg::with_name("name").required(true),
                    ]),
                ])
                .setting(AppSettings::SubcommandRequiredElseHelp),
        ])
        .setting(AppSettings::SubcommandRequiredElseHelp)
        .get_matches();

    let db_url = dotenv::var("DATABASE_URL").expect("missing DATABASE_URL env variable");

    let db_conn = SqliteConnection::establish(&db_url)
        .expect(&format!("failed to connect to DB: {}", db_url));

    match matches.subcommand() {
        ("list", Some(list_matches)) => match list_matches.subcommand() {
            ("nodes", _) => list_all_nodes(&db_conn),
            ("sensors", Some(_sensors_matches)) => {
                unimplemented!("Listing sensors not implemented yet.")
            }
            _ => unreachable!(),
        },
        ("add", Some(add_matches)) => match add_matches.subcommand() {
            ("node", Some(_node_matches)) => {
                unimplemented!("Adding new nodes not implemented yet.")
            }
            ("sensor", Some(_sensor_matches)) => {
                unimplemented!("Adding new sensors not implemented yet.")
            }
            _ => unreachable!(),
        },
        _ => unreachable!(),
    }
}
<|endoftext|>


<commit_before>extern crate inkwell;

use self::inkwell::context::Context;
use self::inkwell::values::InstructionOpcode;

#[test]
fn test_basic_block_ordering() {
    let context = Context::create();
    let module = context.create_module("test");
    let builder = context.create_builder();

    assert!(builder.get_insert_block().is_none());

    let i128_type = context.i128_type();
    let fn_type = i128_type.fn_type(&[], false);

    let function = module.add_function("testing", fn_type, None);

    assert!(function.get_first_basic_block().is_none());

    let basic_block = context.append_basic_block(function, "entry");
    let basic_block4 = context.insert_basic_block_after(basic_block, "block4");
    let basic_block2 = context.insert_basic_block_after(basic_block, "block2");
    let basic_block3 = context.prepend_basic_block(basic_block4, "block3");

    let basic_blocks = function.get_basic_blocks();

    assert_eq!(basic_blocks.len(), 4);
    assert_eq!(basic_blocks[0], basic_block);
    assert_eq!(basic_blocks[1], basic_block2);
    assert_eq!(basic_blocks[2], basic_block3);
    assert_eq!(basic_blocks[3], basic_block4);

    assert!(basic_block3.move_before(basic_block2).is_ok());
    assert!(basic_block.move_after(basic_block4).is_ok());

    let basic_block5 = context.prepend_basic_block(basic_block, "block5");
    let basic_blocks = function.get_basic_blocks();

    assert_eq!(basic_blocks.len(), 5);
    assert_eq!(basic_blocks[0], basic_block3);
    assert_eq!(basic_blocks[1], basic_block2);
    assert_eq!(basic_blocks[2], basic_block4);
    assert_eq!(basic_blocks[3], basic_block5);
    assert_eq!(basic_blocks[4], basic_block);

    assert_ne!(basic_blocks[0], basic_block);
    assert_ne!(basic_blocks[1], basic_block3);
    assert_ne!(basic_blocks[2], basic_block2);
    assert_ne!(basic_blocks[3], basic_block4);
    assert_ne!(basic_blocks[4], basic_block5);

    context.append_basic_block(function, "block6");

    let bb1 = function.get_first_basic_block().unwrap();
    let bb4 = basic_block5.get_previous_basic_block().unwrap();

    assert_eq!(bb1, basic_block3);
    assert_eq!(bb4, basic_block4);
    assert!(basic_block3.get_previous_basic_block().is_none());

    unsafe {
        assert!(bb4.delete().is_ok());
    }

    let bb2 = basic_block5.get_previous_basic_block().unwrap();

    assert_eq!(bb2, basic_block2);

    let bb3 = function.get_first_basic_block().unwrap();

    assert_eq!(bb3, basic_block3);

    #[cfg(not(any(feature = "llvm3-6", feature = "llvm3-7", feature = "llvm3-8")))]
    {
        assert_eq!(basic_block.get_name().to_str(), Ok("entry"));
        assert_eq!(basic_block2.get_name().to_str(), Ok("block2"));
        assert_eq!(basic_block3.get_name().to_str(), Ok("block3"));
        assert_eq!(basic_block5.get_name().to_str(), Ok("block5"));
    }
}

#[test]
fn test_get_basic_blocks() {
    let context = Context::create();
    let module = context.create_module("test");

    let bool_type = context.bool_type();
    let fn_type = bool_type.fn_type(&[], false);

    let function = module.add_function("testing", fn_type, None);

    assert_eq!(function.get_name().to_str(), Ok("testing"));
    assert_eq!(fn_type.get_return_type().unwrap().into_int_type().get_bit_width(), 1);

    assert!(function.get_last_basic_block().is_none());
    assert_eq!(function.get_basic_blocks().len(), 0);

    let basic_block = context.append_basic_block(function, "entry");

    let last_basic_block = function.get_last_basic_block()
                                   .expect("Did not find expected basic block");

    assert_eq!(last_basic_block, basic_block);

    let basic_blocks = function.get_basic_blocks();

    assert_eq!(basic_blocks.len(), 1);
    assert_eq!(basic_blocks[0], basic_block);
}

#[test]
fn test_get_terminator() {
    let context = Context::create();
    let module = context.create_module("test");
    let builder = context.create_builder();

    let void_type = context.void_type();
    let fn_type = void_type.fn_type(&[], false);

    let function = module.add_function("testing", fn_type, None);
    let basic_block = context.append_basic_block(function, "entry");

    builder.position_at_end(basic_block);

    // REVIEW: What's the difference between a terminator and last instruction?
    assert!(basic_block.get_terminator().is_none());
    assert!(basic_block.get_first_instruction().is_none());
    assert!(basic_block.get_last_instruction().is_none());

    builder.build_return(None);

    assert_eq!(basic_block.get_terminator().unwrap().get_opcode(), InstructionOpcode::Return);
    assert_eq!(basic_block.get_first_instruction().unwrap().get_opcode(), InstructionOpcode::Return);
    assert_eq!(basic_block.get_last_instruction().unwrap().get_opcode(), InstructionOpcode::Return);
    assert_eq!(basic_block.get_last_instruction(), basic_block.get_terminator());
}

#[test]
fn test_no_parent() {
    let context = Context::create();
    let module = context.create_module("test");

    let void_type = context.void_type();
    let fn_type = void_type.fn_type(&[], false);

    let function = module.add_function("testing", fn_type, None);
    let basic_block = context.append_basic_block(function, "entry");
    let basic_block2 = context.append_basic_block(function, "next");

    assert_eq!(basic_block.get_parent().unwrap(), function);
    assert_eq!(basic_block.get_next_basic_block().unwrap(), basic_block2);

    assert!(basic_block.remove_from_function().is_ok());

    assert!(basic_block.get_next_basic_block().is_none());
    assert!(basic_block2.get_previous_basic_block().is_none());

    assert!(basic_block.remove_from_function().is_err());
    assert!(basic_block.remove_from_function().is_err());

    assert!(basic_block.get_parent().is_none());
}

#[test]
fn test_rauw() {
    let context = Context::create();
    let builder = context.create_builder();
    let module = context.create_module("my_mod");
    let void_type = context.void_type();
    let fn_type = void_type.fn_type(&[], false);
    let fn_val = module.add_function("my_fn", fn_type, None);
    let entry = context.append_basic_block(fn_val, "entry");
    let bb1 = context.append_basic_block(fn_val, "bb1");
    let bb2 = context.append_basic_block(fn_val, "bb2");
    builder.position_at_end(entry);
    let branch_inst = builder.build_unconditional_branch(bb1);

    bb1.replace_all_uses_with(&bb1);  // no-op
    bb1.replace_all_uses_with(&bb2);

    assert_eq!(branch_inst.get_operand(0).unwrap().right().unwrap(), bb2);
}

#[test]
fn test_get_first_use() {
    let context = Context::create();
    let module = context.create_module("ivs");
    let builder = context.create_builder();
    let void_type = context.void_type();
    let fn_type = void_type.fn_type(&[], false);
    let fn_val = module.add_function("my_fn", fn_type, None);
    let entry = context.append_basic_block(fn_val, "entry");
    let bb1 = context.append_basic_block(fn_val, "bb1");
    let bb2 = context.append_basic_block(fn_val, "bb2");
    builder.position_at_end(entry);
    let branch_inst = builder.build_unconditional_branch(bb1);

    assert!(bb2.get_first_use().is_none());
    assert!(bb1.get_first_use().is_some());
    assert_eq!(bb1.get_first_use().unwrap().get_user(), branch_inst);
    assert!(bb1.get_first_use().unwrap().get_next_use().is_none());
}
<commit_msg>Add test for BasicBlock::get_address<commit_after>extern crate inkwell;

use self::inkwell::context::Context;
use self::inkwell::values::InstructionOpcode;

#[test]
fn test_basic_block_ordering() {
    let context = Context::create();
    let module = context.create_module("test");
    let builder = context.create_builder();

    assert!(builder.get_insert_block().is_none());

    let i128_type = context.i128_type();
    let fn_type = i128_type.fn_type(&[], false);

    let function = module.add_function("testing", fn_type, None);

    assert!(function.get_first_basic_block().is_none());

    let basic_block = context.append_basic_block(function, "entry");
    let basic_block4 = context.insert_basic_block_after(basic_block, "block4");
    let basic_block2 = context.insert_basic_block_after(basic_block, "block2");
    let basic_block3 = context.prepend_basic_block(basic_block4, "block3");

    let basic_blocks = function.get_basic_blocks();

    assert_eq!(basic_blocks.len(), 4);
    assert_eq!(basic_blocks[0], basic_block);
    assert_eq!(basic_blocks[1], basic_block2);
    assert_eq!(basic_blocks[2], basic_block3);
    assert_eq!(basic_blocks[3], basic_block4);

    assert!(basic_block3.move_before(basic_block2).is_ok());
    assert!(basic_block.move_after(basic_block4).is_ok());

    let basic_block5 = context.prepend_basic_block(basic_block, "block5");
    let basic_blocks = function.get_basic_blocks();

    assert_eq!(basic_blocks.len(), 5);
    assert_eq!(basic_blocks[0], basic_block3);
    assert_eq!(basic_blocks[1], basic_block2);
    assert_eq!(basic_blocks[2], basic_block4);
    assert_eq!(basic_blocks[3], basic_block5);
    assert_eq!(basic_blocks[4], basic_block);

    assert_ne!(basic_blocks[0], basic_block);
    assert_ne!(basic_blocks[1], basic_block3);
    assert_ne!(basic_blocks[2], basic_block2);
    assert_ne!(basic_blocks[3], basic_block4);
    assert_ne!(basic_blocks[4], basic_block5);

    context.append_basic_block(function, "block6");

    let bb1 = function.get_first_basic_block().unwrap();
    let bb4 = basic_block5.get_previous_basic_block().unwrap();

    assert_eq!(bb1, basic_block3);
    assert_eq!(bb4, basic_block4);
    assert!(basic_block3.get_previous_basic_block().is_none());

    unsafe {
        assert!(bb4.delete().is_ok());
    }

    let bb2 = basic_block5.get_previous_basic_block().unwrap();

    assert_eq!(bb2, basic_block2);

    let bb3 = function.get_first_basic_block().unwrap();

    assert_eq!(bb3, basic_block3);

    #[cfg(not(any(feature = "llvm3-6", feature = "llvm3-7", feature = "llvm3-8")))]
    {
        assert_eq!(basic_block.get_name().to_str(), Ok("entry"));
        assert_eq!(basic_block2.get_name().to_str(), Ok("block2"));
        assert_eq!(basic_block3.get_name().to_str(), Ok("block3"));
        assert_eq!(basic_block5.get_name().to_str(), Ok("block5"));
    }
}

#[test]
fn test_get_basic_blocks() {
    let context = Context::create();
    let module = context.create_module("test");

    let bool_type = context.bool_type();
    let fn_type = bool_type.fn_type(&[], false);

    let function = module.add_function("testing", fn_type, None);

    assert_eq!(function.get_name().to_str(), Ok("testing"));
    assert_eq!(fn_type.get_return_type().unwrap().into_int_type().get_bit_width(), 1);

    assert!(function.get_last_basic_block().is_none());
    assert_eq!(function.get_basic_blocks().len(), 0);

    let basic_block = context.append_basic_block(function, "entry");

    let last_basic_block = function.get_last_basic_block()
                                   .expect("Did not find expected basic block");

    assert_eq!(last_basic_block, basic_block);

    let basic_blocks = function.get_basic_blocks();

    assert_eq!(basic_blocks.len(), 1);
    assert_eq!(basic_blocks[0], basic_block);
}

#[test]
fn test_get_terminator() {
    let context = Context::create();
    let module = context.create_module("test");
    let builder = context.create_builder();

    let void_type = context.void_type();
    let fn_type = void_type.fn_type(&[], false);

    let function = module.add_function("testing", fn_type, None);
    let basic_block = context.append_basic_block(function, "entry");

    builder.position_at_end(basic_block);

    // REVIEW: What's the difference between a terminator and last instruction?
    assert!(basic_block.get_terminator().is_none());
    assert!(basic_block.get_first_instruction().is_none());
    assert!(basic_block.get_last_instruction().is_none());

    builder.build_return(None);

    assert_eq!(basic_block.get_terminator().unwrap().get_opcode(), InstructionOpcode::Return);
    assert_eq!(basic_block.get_first_instruction().unwrap().get_opcode(), InstructionOpcode::Return);
    assert_eq!(basic_block.get_last_instruction().unwrap().get_opcode(), InstructionOpcode::Return);
    assert_eq!(basic_block.get_last_instruction(), basic_block.get_terminator());
}

#[test]
fn test_no_parent() {
    let context = Context::create();
    let module = context.create_module("test");

    let void_type = context.void_type();
    let fn_type = void_type.fn_type(&[], false);

    let function = module.add_function("testing", fn_type, None);
    let basic_block = context.append_basic_block(function, "entry");
    let basic_block2 = context.append_basic_block(function, "next");

    assert_eq!(basic_block.get_parent().unwrap(), function);
    assert_eq!(basic_block.get_next_basic_block().unwrap(), basic_block2);

    assert!(basic_block.remove_from_function().is_ok());

    assert!(basic_block.get_next_basic_block().is_none());
    assert!(basic_block2.get_previous_basic_block().is_none());

    assert!(basic_block.remove_from_function().is_err());
    assert!(basic_block.remove_from_function().is_err());

    assert!(basic_block.get_parent().is_none());
}

#[test]
fn test_rauw() {
    let context = Context::create();
    let builder = context.create_builder();
    let module = context.create_module("my_mod");
    let void_type = context.void_type();
    let fn_type = void_type.fn_type(&[], false);
    let fn_val = module.add_function("my_fn", fn_type, None);
    let entry = context.append_basic_block(fn_val, "entry");
    let bb1 = context.append_basic_block(fn_val, "bb1");
    let bb2 = context.append_basic_block(fn_val, "bb2");
    builder.position_at_end(entry);
    let branch_inst = builder.build_unconditional_branch(bb1);

    bb1.replace_all_uses_with(&bb1);  // no-op
    bb1.replace_all_uses_with(&bb2);

    assert_eq!(branch_inst.get_operand(0).unwrap().right().unwrap(), bb2);
}

#[test]
fn test_get_first_use() {
    let context = Context::create();
    let module = context.create_module("ivs");
    let builder = context.create_builder();
    let void_type = context.void_type();
    let fn_type = void_type.fn_type(&[], false);
    let fn_val = module.add_function("my_fn", fn_type, None);
    let entry = context.append_basic_block(fn_val, "entry");
    let bb1 = context.append_basic_block(fn_val, "bb1");
    let bb2 = context.append_basic_block(fn_val, "bb2");
    builder.position_at_end(entry);
    let branch_inst = builder.build_unconditional_branch(bb1);

    assert!(bb2.get_first_use().is_none());
    assert!(bb1.get_first_use().is_some());
    assert_eq!(bb1.get_first_use().unwrap().get_user(), branch_inst);
    assert!(bb1.get_first_use().unwrap().get_next_use().is_none());
}

#[test]
fn test_get_address() {
    let context = Context::create();
    let module = context.create_module("my_mod");
    let void_type = context.void_type();
    let fn_type = void_type.fn_type(&[], false);
    let fn_val = module.add_function("my_fn", fn_type, None);
    let bb = context.append_basic_block(fn_val, "entry");

    assert!(bb.get_address().is_some());
}
<|endoftext|>


<commit_before>#![feature(globs)]

extern crate conduit;

use std::fmt::Show;

use conduit::{Request, Response, Handler};

pub trait Middleware {
    #[allow(unused_variable)]
    fn before<'a>(&self, req: &'a mut Request) -> &'a mut Request {
        req
    }
    #[allow(unused_variable)]
    fn after<'a>(&self, req: &mut Request, res: &'a mut Response) -> &'a mut Response {
        res
    }
}

pub trait AroundMiddleware : Handler {
    fn with_handler(&mut self, handler: Box<Handler + 'static>);
}

pub struct MiddlewareBuilder {
    middlewares: Vec<Box<Middleware>>,
    handler: Option<Box<Handler + 'static>>
}

impl MiddlewareBuilder {
    pub fn new(handler: Box<Handler + 'static>) -> MiddlewareBuilder {
        MiddlewareBuilder {
            middlewares: vec!(),
            handler: Some(handler)
        }
    }

    pub fn add<M: Middleware + 'static>(&mut self, middleware: M) {
        self.middlewares.push(box middleware as Box<Middleware>);
    }

    pub fn around<M: AroundMiddleware + 'static>(&mut self, mut middleware: M) {
        let handler = self.handler.take_unwrap();
        middleware.with_handler(handler);
        self.handler = Some(box middleware as Box<Handler + 'static>);
    }
}

impl Handler for MiddlewareBuilder {
    fn call(&self, req: &mut Request) -> Result<Response, Box<Show>> {
        for middleware in self.middlewares.iter() {
            middleware.before(req);
        }

        match (self.handler.get_ref()).call(req) {
            Err(err) => return Err(err),
            Ok(mut res) => {
                for middleware in self.middlewares.iter() {
                   middleware.after(req, &mut res);
                }

                Ok(res)
            }
        }
    }
}

#[cfg(test)]
mod tests {
    extern crate semver;

    use super::*;

    use std::any::{Any, AnyRefExt};
    use std::io::net::ip::IpAddr;
    use std::io::MemReader;
    use std::fmt::Show;
    use std::collections::HashMap;

    use conduit;
    use conduit::{Request, Response, Host, Headers, Method, Scheme, Extensions, Handler};

    struct RequestSentinel {
        path: String,
        extensions: HashMap<&'static str, Box<Any>>,
        method: Method
    }

    impl RequestSentinel {
        fn new(method: Method, path: &'static str) -> RequestSentinel {
            RequestSentinel {
                path: path.to_str(),
                extensions: HashMap::new(),
                method: method
            }
        }
    }

    impl conduit::Request for RequestSentinel {
        fn http_version(&self) -> semver::Version { unimplemented!() }
        fn conduit_version(&self) -> semver::Version { unimplemented!() }
        fn method(&self) -> Method { self.method }
        fn scheme(&self) -> Scheme { unimplemented!() }
        fn host<'a>(&'a self) -> Host<'a> { unimplemented!() }
        fn virtual_root<'a>(&'a self) -> Option<&'a str> { unimplemented!() }
        fn path<'a>(&'a self) -> &'a str {
            self.path.as_slice()
        }
        fn query_string<'a>(&'a self) -> Option<&'a str> { unimplemented!() }
        fn remote_ip(&self) -> IpAddr { unimplemented!() }
        fn content_length(&self) -> Option<uint> { unimplemented!() }
        fn headers<'a>(&'a self) -> &'a Headers { unimplemented!() }
        fn body<'a>(&'a mut self) -> &'a mut Reader { unimplemented!() }
        fn extensions<'a>(&'a self) -> &'a Extensions {
            &self.extensions
        }
        fn mut_extensions<'a>(&'a mut self) -> &'a mut Extensions {
            &mut self.extensions
        }
    }

    struct MyMiddleware;

    impl Middleware for MyMiddleware {
        fn before<'a>(&self, req: &'a mut Request) -> &'a mut Request {
            req.mut_extensions().insert("test.middleware", box "hello".to_str() as Box<Any>);
            req
        }
    }

    struct MyAroundMiddleware {
        handler: Option<Box<Handler + 'static>>
    }

    impl MyAroundMiddleware {
        fn new() -> MyAroundMiddleware {
            MyAroundMiddleware { handler: None }
        }
    }

    impl Middleware for MyAroundMiddleware {}

    impl AroundMiddleware for MyAroundMiddleware {
        fn with_handler(&mut self, handler: Box<Handler + 'static>) {
            self.handler = Some(handler)
        }
    }

    impl Handler for MyAroundMiddleware {
        fn call(&self, req: &mut Request) -> Result<Response, Box<Show>> {
            req.mut_extensions().insert("test.round-and-round", box "hello".to_str() as Box<Any>);
            self.handler.get_ref().call(req)
        }
    }

    fn get_extension<'a, T: 'static>(req: &'a Request, key: &'static str) -> &'a T {
        req.extensions().find(&key).and_then(|s| s.as_ref::<T>())
            .expect(format!("No {} key found in extensions", key).as_slice())
    }

    fn response(string: String) -> Response {
        Response {
            status: (200, "OK"),
            headers: HashMap::new(),
            body: box MemReader::new(string.into_bytes())
        }
    }

    fn handler(req: &mut Request) -> Result<Response, ()> {
        let hello = get_extension::<String>(req, "test.middleware");
        Ok(response(hello.clone()))
    }

    fn middle_handler(req: &mut Request) -> Result<Response, ()> {
        let hello = get_extension::<String>(req, "test.middleware");
        let middle = get_extension::<String>(req, "test.round-and-round");

        Ok(response(format!("{} {}", hello, middle)))
    }

    #[test]
    fn test_simple_middleware() {

        let mut builder = MiddlewareBuilder::new(box handler);
        builder.add(MyMiddleware);

        let mut req = RequestSentinel::new(conduit::Get, "/");
        let mut res = builder.call(&mut req).ok().expect("No response");

        assert_eq!(res.body.read_to_str().ok().expect("No body"), "hello".to_str());
    }

    #[test]
    fn test_around_middleware() {
        let mut builder = MiddlewareBuilder::new(box middle_handler);
        builder.add(MyMiddleware);
        builder.around(MyAroundMiddleware::new());

        let mut req = RequestSentinel::new(conduit::Get, "/");
        let mut res = builder.call(&mut req).ok().expect("No response");

        assert_eq!(res.body.read_to_str().ok().expect("No body"), "hello hello".to_str());
    }
}
<commit_msg>Allow middleware to fail<commit_after>#![feature(globs)]

extern crate conduit;

use std::fmt::Show;

use conduit::{Request, Response, Handler};

pub trait Middleware {
    #[allow(unused_variable)]
    fn before<'a>(&self, req: &'a mut Request) -> Result<&'a mut Request, Box<Show>> {
        Ok(req)
    }
    #[allow(unused_variable)]
    fn after<'a>(&self, req: &mut Request, res: &'a mut Response) -> Result<&'a mut Response, Box<Show>> {
        Ok(res)
    }
}

pub trait AroundMiddleware : Handler {
    fn with_handler(&mut self, handler: Box<Handler + 'static>);
}

pub struct MiddlewareBuilder {
    middlewares: Vec<Box<Middleware>>,
    handler: Option<Box<Handler + 'static>>
}

impl MiddlewareBuilder {
    pub fn new(handler: Box<Handler + 'static>) -> MiddlewareBuilder {
        MiddlewareBuilder {
            middlewares: vec!(),
            handler: Some(handler)
        }
    }

    pub fn add<M: Middleware + 'static>(&mut self, middleware: M) {
        self.middlewares.push(box middleware as Box<Middleware>);
    }

    pub fn around<M: AroundMiddleware + 'static>(&mut self, mut middleware: M) {
        let handler = self.handler.take_unwrap();
        middleware.with_handler(handler);
        self.handler = Some(box middleware as Box<Handler + 'static>);
    }
}

impl Handler for MiddlewareBuilder {
    fn call(&self, req: &mut Request) -> Result<Response, Box<Show>> {
        for middleware in self.middlewares.iter() {
            try!(middleware.before(req));
        }

        match (self.handler.get_ref()).call(req) {
            Err(err) => return Err(err),
            Ok(mut res) => {
                for middleware in self.middlewares.iter() {
                   try!(middleware.after(req, &mut res));
                }

                Ok(res)
            }
        }
    }
}

#[cfg(test)]
mod tests {
    extern crate semver;

    use super::*;

    use std::any::{Any, AnyRefExt};
    use std::io::net::ip::IpAddr;
    use std::io::MemReader;
    use std::fmt::Show;
    use std::collections::HashMap;

    use conduit;
    use conduit::{Request, Response, Host, Headers, Method, Scheme, Extensions, Handler};

    struct RequestSentinel {
        path: String,
        extensions: HashMap<&'static str, Box<Any>>,
        method: Method
    }

    impl RequestSentinel {
        fn new(method: Method, path: &'static str) -> RequestSentinel {
            RequestSentinel {
                path: path.to_str(),
                extensions: HashMap::new(),
                method: method
            }
        }
    }

    impl conduit::Request for RequestSentinel {
        fn http_version(&self) -> semver::Version { unimplemented!() }
        fn conduit_version(&self) -> semver::Version { unimplemented!() }
        fn method(&self) -> Method { self.method }
        fn scheme(&self) -> Scheme { unimplemented!() }
        fn host<'a>(&'a self) -> Host<'a> { unimplemented!() }
        fn virtual_root<'a>(&'a self) -> Option<&'a str> { unimplemented!() }
        fn path<'a>(&'a self) -> &'a str {
            self.path.as_slice()
        }
        fn query_string<'a>(&'a self) -> Option<&'a str> { unimplemented!() }
        fn remote_ip(&self) -> IpAddr { unimplemented!() }
        fn content_length(&self) -> Option<uint> { unimplemented!() }
        fn headers<'a>(&'a self) -> &'a Headers { unimplemented!() }
        fn body<'a>(&'a mut self) -> &'a mut Reader { unimplemented!() }
        fn extensions<'a>(&'a self) -> &'a Extensions {
            &self.extensions
        }
        fn mut_extensions<'a>(&'a mut self) -> &'a mut Extensions {
            &mut self.extensions
        }
    }

    struct MyMiddleware;

    impl Middleware for MyMiddleware {
        fn before<'a>(&self, req: &'a mut Request) -> Result<&'a mut Request, Box<Show>> {
            req.mut_extensions().insert("test.middleware", box "hello".to_str() as Box<Any>);
            Ok(req)
        }
    }

    struct MyAroundMiddleware {
        handler: Option<Box<Handler + 'static>>
    }

    impl MyAroundMiddleware {
        fn new() -> MyAroundMiddleware {
            MyAroundMiddleware { handler: None }
        }
    }

    impl Middleware for MyAroundMiddleware {}

    impl AroundMiddleware for MyAroundMiddleware {
        fn with_handler(&mut self, handler: Box<Handler + 'static>) {
            self.handler = Some(handler)
        }
    }

    impl Handler for MyAroundMiddleware {
        fn call(&self, req: &mut Request) -> Result<Response, Box<Show>> {
            req.mut_extensions().insert("test.round-and-round", box "hello".to_str() as Box<Any>);
            self.handler.get_ref().call(req)
        }
    }

    fn get_extension<'a, T: 'static>(req: &'a Request, key: &'static str) -> &'a T {
        req.extensions().find(&key).and_then(|s| s.as_ref::<T>())
            .expect(format!("No {} key found in extensions", key).as_slice())
    }

    fn response(string: String) -> Response {
        Response {
            status: (200, "OK"),
            headers: HashMap::new(),
            body: box MemReader::new(string.into_bytes())
        }
    }

    fn handler(req: &mut Request) -> Result<Response, ()> {
        let hello = get_extension::<String>(req, "test.middleware");
        Ok(response(hello.clone()))
    }

    fn middle_handler(req: &mut Request) -> Result<Response, ()> {
        let hello = get_extension::<String>(req, "test.middleware");
        let middle = get_extension::<String>(req, "test.round-and-round");

        Ok(response(format!("{} {}", hello, middle)))
    }

    #[test]
    fn test_simple_middleware() {

        let mut builder = MiddlewareBuilder::new(box handler);
        builder.add(MyMiddleware);

        let mut req = RequestSentinel::new(conduit::Get, "/");
        let mut res = builder.call(&mut req).ok().expect("No response");

        assert_eq!(res.body.read_to_str().ok().expect("No body"), "hello".to_str());
    }

    #[test]
    fn test_around_middleware() {
        let mut builder = MiddlewareBuilder::new(box middle_handler);
        builder.add(MyMiddleware);
        builder.around(MyAroundMiddleware::new());

        let mut req = RequestSentinel::new(conduit::Get, "/");
        let mut res = builder.call(&mut req).ok().expect("No response");

        assert_eq!(res.body.read_to_str().ok().expect("No body"), "hello hello".to_str());
    }
}
<|endoftext|>


<commit_before>extern crate symbolic_expressions;

use secsp::ast::*;
use self::symbolic_expressions::Sexp;

macro_rules! cil {
    ($x:expr) => {
        $x.into_sexp()
    }
}

macro_rules! cil_list {
    [ $( $x:expr ),* $(,)* ] => {
        {
            let mut temp_vec : Vec<Sexp> = Vec::new();
            $(
                temp_vec.push($x.into());
            )*

            let sexp : Sexp = temp_vec.into();
            sexp
        }
    };
}

pub trait ToCil: Sized + Clone {
    fn into_sexp(&self) -> Sexp;
}

pub trait CilType: TypeSpecifier {
    fn requires_initializer(&self) -> bool;
}

impl CilType for SymbolType {
    fn requires_initializer(&self) -> bool {
        match *self {
            SymbolType::Context => true,
            _ => false,
        }
    }
}

fn compile_if(condition: &Expr, block: &Vec<Statement>) -> Sexp {
    let mut statement_sexpr: Sexp = cil_list!["booleanif", condition.into_sexp()];
    let mut true_branch: Sexp = cil_list!["true"];

    for stmt in block {
        true_branch.push(stmt.into_sexp());
    }

    statement_sexpr.push(true_branch);
    statement_sexpr
}

fn compile_if_else_if(
    condition: &Expr,
    block: &Vec<Statement>,
    else_ifs: &Vec<(Expr, Vec<Statement>)>,
    else_block: Option<&Vec<Statement>>,
) -> Sexp {
    let mut if_stmt = compile_if(condition, block);
    let mut false_branch = cil_list!["false"];

    let mut else_if_iter = else_ifs.into_iter().rev();
    let mut last = match else_if_iter.next() {
        Some(else_if) => {
            let (ref expr, ref statements) = *else_if;
            compile_if_else(expr, statements, else_block)
        }
        _ => panic!("compile_if_else_if called with no else_ifs"),
    };

    for else_if in else_if_iter {
        let (ref condition, ref statements) = *else_if;

        let mut stmt = compile_if(condition, statements);
        let else_branch = cil_list!["false", last];
        stmt.push(else_branch);

        last = stmt;
    }

    false_branch.push(last);
    if_stmt.push(false_branch);

    if_stmt
}

fn compile_if_else(
    condition: &Expr,
    block: &Vec<Statement>,
    else_block: Option<&Vec<Statement>>,
) -> Sexp {
    let mut if_stmt = compile_if(condition, block);

    if let Some(statements) = else_block {
        let mut false_branch = cil_list!["false"];

        for stmt in statements {
            false_branch.push(stmt.into_sexp());
        }

        if_stmt.push(false_branch);
    }

    if_stmt
}

impl ToCil for Statement {
    fn into_sexp(&self) -> Sexp {
        match *self {
            Statement::Declaration(ref decl) => decl.into_sexp(),
            Statement::MacroCall(ref id, ref params) => {
                let params_sexpr: Vec<Sexp> =
                    params.iter().map(|ref p: &Expr| p.into_sexp()).collect();

                cil_list!["call", id, params_sexpr]
            }
            Statement::IfElse {
                ref condition,
                ref then_block,
                ref else_ifs,
                ref else_block,
            } => {
                if else_ifs.is_empty() {
                    compile_if_else(condition, then_block, else_block.as_ref())
                } else {
                    compile_if_else_if(condition, then_block, else_ifs, else_block.as_ref())
                }
            }
            Statement::AccessVectorRule {
                ref rule_type,
                ref source,
                ref target,
                ref access_vector,
            } => {
                let mut rule_sexp =
                    cil_list![rule_type.to_cil(), source.into_sexp(), target.into_sexp()];

                match *access_vector {
                    AccessVector::Permission(ref expr) => {
                        rule_sexp.push(expr.into_sexp());
                    }
                    AccessVector::ClassAndPermissions(ref class, ref expr) => {
                        rule_sexp.push(cil_list![class.into_sexp(), expr.into_sexp()]);
                    }
                }

                rule_sexp
            }
            _ => Sexp::Empty,
        }
    }
}

impl ToCil for BlockType {
    fn into_sexp(&self) -> Sexp {
        self.to_cil().into()
    }
}

impl ToCil for SymbolType {
    fn into_sexp(&self) -> Sexp {
        self.to_cil().into()
    }
}

impl ToCil for MacroParameter {
    fn into_sexp(&self) -> Sexp {
        cil_list![self.qualifier.into_sexp(), self.name.clone()]
    }
}

impl ToCil for Declaration {
    fn into_sexp(&self) -> Sexp {
        match *self {
            Declaration::Block {
                ref is_abstract,
                ref qualifier,
                ref name,
                ref statements,
                ref extends,
            } => {
                let mut statement: Sexp = cil_list![qualifier.into_sexp(), name];
                if *is_abstract {
                    let blockabstract_sexpr: Sexp = cil_list!["blockabstract", name];
                    statement.push(blockabstract_sexpr);
                }

                if let Some(ref extends_list) = *extends {
                    for id in extends_list {
                        statement.push(cil_list!["blockinherit", id]);
                    }
                }

                for stmt in statements {
                    statement.push(stmt.into_sexp());
                }

                statement
            }
            Declaration::Symbol {
                ref qualifier,
                ref name,
                ref initializer,
            } => {
                let mut declaration: Sexp = cil_list![qualifier.into_sexp(), name];

                if qualifier.requires_initializer() {
                    if let Some(ref expr) = *initializer {
                        declaration.push(expr.into_sexp());
                    } else {
                        //@todo - raise error
                    }
                }

                declaration
            }
            Declaration::Macro {
                ref name,
                ref parameters,
                ref statements,
            } => {
                let params: Vec<Sexp> = parameters.iter().map(|it| it.into_sexp()).collect();
                let mut decl: Sexp = cil_list!["macro", name, params];

                for stmt in statements {
                    decl.push(stmt.into_sexp());
                }

                decl
            }

            _ => Sexp::Empty,
        }
    }
}

impl ToCil for Expr {
    fn into_sexp(&self) -> Sexp {
        match *self {
            Expr::Binary(ref lhs, ref op, ref rhs) => {
                cil_list![cil!(op), cil!(lhs.as_ref()), cil!(rhs.as_ref())]
            }
            Expr::Unary(ref op, ref expr) => cil_list![cil!(op), cil!(expr.as_ref())],
            Expr::Variable(ref id) => id.into(),
            Expr::VariableList(ref list) => {
                let mut list_sexpr = cil_list![];

                for identifier in list {
                    list_sexpr.push(identifier);
                }

                list_sexpr
            }
            Expr::LevelRange(ref low, ref high) => cil_list![cil!(low), cil!(high)],
            Expr::Level {
                ref sensitivity,
                ref categories,
            } => {
                let categories_sexpr: Sexp = cil_list![categories.into_sexp()];

                cil_list![sensitivity, categories_sexpr]
            }
            Expr::Context {
                ref user_id,
                ref role_id,
                ref type_id,
                ref level_range,
            } => {
                let mut context_sexp: Sexp = cil_list![user_id, role_id, type_id];

                if let Some(ref expr) = *level_range {
                    context_sexp.push(expr.as_ref().into_sexp());
                }

                context_sexp
            }
            _ => Sexp::Empty, //@TODO
        }
    }
}

impl ToCil for BinaryOp {
    fn into_sexp(&self) -> Sexp {
        let result = match *self {
            BinaryOp::BitwiseAnd | BinaryOp::ConditionalAnd => "and",
            BinaryOp::BitwiseOr | BinaryOp::ConditionalOr => "or",
            BinaryOp::BitwiseXor | BinaryOp::ConditionalXor => "xor",
        };

        result.into()
    }
}

impl ToCil for UnaryOp {
    fn into_sexp(&self) -> Sexp {
        let result = match *self {
            UnaryOp::ConditionalNot | UnaryOp::BitwiseNot => "not",
        };

        result.into()
    }
}

#[cfg(test)]
mod testing {

    use super::*;
    use secsp;

    pub fn parse_and_compile_stmt(input: &str) -> Sexp {
        let stmt = match secsp::parse_from_slice(input.as_bytes()) {
            secsp::ParseResult::Ok(stmts) => stmts[0].clone(),
            _ => panic!("Unable to parse"),
        };

        stmt.into_sexp()
    }

    #[test]
    pub fn compile_block_decl() {
        let expected: Sexp = cil_list!["block", "my_block"];
        let actual = parse_and_compile_stmt("block my_block {}");

        assert_eq!(expected, actual);
    }

    #[test]
    pub fn compile_abstract_block_decl() {
        let expected: Sexp = cil_list!["block", "my_block", cil_list!["blockabstract", "my_block"]];
        let actual = parse_and_compile_stmt("abstract block my_block {}");

        assert_eq!(expected, actual);
    }

    #[test]
    pub fn compile_block_decl_with_extends() {
        let expected =
            cil_list![
            "block",
            "my_block",
            cil_list!["blockinherit", "my_other_block"],
            cil_list!["blockinherit", "my_other_other_block"],
        ];
        let actual = parse_and_compile_stmt(
            "block my_block extends my_other_block, my_other_other_block {}",
        );

        assert_eq!(expected, actual);
    }

    #[test]
    pub fn compile_if_else() {
        let expected =
            cil_list![
            "booleanif",
            "my_bool",
            cil_list!["true"],
            cil_list!["false"],
        ];

        let actual = parse_and_compile_stmt(
            "
           if (my_bool) {

           } else {

           }",
        );

        assert_eq!(expected, actual);
    }

    #[test]
    pub fn compile_macro_call() {
        let expected = cil_list!["call", "my_macro", cil_list!["a"]];
        let actual = parse_and_compile_stmt("my_macro(a);");

        assert_eq!(expected, actual);
    }

    #[test]
    pub fn compile_context_decl() {
        let expected = cil_list!["context", "my_context", cil_list!["user", "role", "type"]];
        let actual = parse_and_compile_stmt("context my_context = user:role:type;");

        assert_eq!(expected, actual);
    }

    #[test]
    pub fn compile_access_vector_rule() {
        let expected = cil_list!["allow", "src", "target", "permission_set"];
        let actual = parse_and_compile_stmt("allow src target : permission_set;");

        assert_eq!(expected, actual);
    }

    #[test]
    pub fn compile_access_vector_rule_anonymous_perms() {
        let expected =
            cil_list![
            "dontaudit",
            "src",
            "target",
            cil_list!["security_class", cil_list!["or", cil_list!["perm1", "perm2"], cil_list!["perm3", "perm4"]]],
        ];

        let actual = parse_and_compile_stmt(
            "dontaudit src target : security_class ((perm1 perm2) | (perm3 perm4));",
        );

        assert_eq!(expected, actual);
    }
}<commit_msg>Add compiler tests covering macro decls and if-else-if<commit_after>extern crate symbolic_expressions;

use secsp::ast::*;
use self::symbolic_expressions::Sexp;

macro_rules! cil {
    ($x:expr) => {
        $x.into_sexp()
    }
}

macro_rules! cil_list {
    [ $( $x:expr ),* $(,)* ] => {
        {
            let mut temp_vec : Vec<Sexp> = Vec::new();
            $(
                temp_vec.push($x.into());
            )*

            let sexp : Sexp = temp_vec.into();
            sexp
        }
    };
}

pub trait ToCil: Sized + Clone {
    fn into_sexp(&self) -> Sexp;
}

pub trait CilType: TypeSpecifier {
    fn requires_initializer(&self) -> bool;
}

impl CilType for SymbolType {
    fn requires_initializer(&self) -> bool {
        match *self {
            SymbolType::Context => true,
            _ => false,
        }
    }
}

fn compile_if(condition: &Expr, block: &Vec<Statement>) -> Sexp {
    let mut statement_sexpr: Sexp = cil_list!["booleanif", condition.into_sexp()];
    let mut true_branch: Sexp = cil_list!["true"];

    for stmt in block {
        true_branch.push(stmt.into_sexp());
    }

    statement_sexpr.push(true_branch);
    statement_sexpr
}

fn compile_if_else_if(
    condition: &Expr,
    block: &Vec<Statement>,
    else_ifs: &Vec<(Expr, Vec<Statement>)>,
    else_block: Option<&Vec<Statement>>,
) -> Sexp {
    let mut if_stmt = compile_if(condition, block);
    let mut false_branch = cil_list!["false"];

    let mut else_if_iter = else_ifs.into_iter().rev();
    let mut last = match else_if_iter.next() {
        Some(else_if) => {
            let (ref expr, ref statements) = *else_if;
            compile_if_else(expr, statements, else_block)
        }
        _ => panic!("compile_if_else_if called with no else_ifs"),
    };

    for else_if in else_if_iter {
        let (ref condition, ref statements) = *else_if;

        let mut stmt = compile_if(condition, statements);
        let else_branch = cil_list!["false", last];
        stmt.push(else_branch);

        last = stmt;
    }

    false_branch.push(last);
    if_stmt.push(false_branch);

    if_stmt
}

fn compile_if_else(
    condition: &Expr,
    block: &Vec<Statement>,
    else_block: Option<&Vec<Statement>>,
) -> Sexp {
    let mut if_stmt = compile_if(condition, block);

    if let Some(statements) = else_block {
        let mut false_branch = cil_list!["false"];

        for stmt in statements {
            false_branch.push(stmt.into_sexp());
        }

        if_stmt.push(false_branch);
    }

    if_stmt
}

impl ToCil for Statement {
    fn into_sexp(&self) -> Sexp {
        match *self {
            Statement::Declaration(ref decl) => decl.into_sexp(),
            Statement::MacroCall(ref id, ref params) => {
                let params_sexpr: Vec<Sexp> =
                    params.iter().map(|ref p: &Expr| p.into_sexp()).collect();

                cil_list!["call", id, params_sexpr]
            }
            Statement::IfElse {
                ref condition,
                ref then_block,
                ref else_ifs,
                ref else_block,
            } => {
                if else_ifs.is_empty() {
                    compile_if_else(condition, then_block, else_block.as_ref())
                } else {
                    compile_if_else_if(condition, then_block, else_ifs, else_block.as_ref())
                }
            }
            Statement::AccessVectorRule {
                ref rule_type,
                ref source,
                ref target,
                ref access_vector,
            } => {
                let mut rule_sexp =
                    cil_list![rule_type.to_cil(), source.into_sexp(), target.into_sexp()];

                match *access_vector {
                    AccessVector::Permission(ref expr) => {
                        rule_sexp.push(expr.into_sexp());
                    }
                    AccessVector::ClassAndPermissions(ref class, ref expr) => {
                        rule_sexp.push(cil_list![class.into_sexp(), expr.into_sexp()]);
                    }
                }

                rule_sexp
            }
            _ => Sexp::Empty,
        }
    }
}

impl ToCil for BlockType {
    fn into_sexp(&self) -> Sexp {
        self.to_cil().into()
    }
}

impl ToCil for SymbolType {
    fn into_sexp(&self) -> Sexp {
        self.to_cil().into()
    }
}

impl ToCil for MacroParameter {
    fn into_sexp(&self) -> Sexp {
        cil_list![self.qualifier.into_sexp(), self.name.clone()]
    }
}

impl ToCil for Declaration {
    fn into_sexp(&self) -> Sexp {
        match *self {
            Declaration::Block {
                ref is_abstract,
                ref qualifier,
                ref name,
                ref statements,
                ref extends,
            } => {
                let mut statement: Sexp = cil_list![qualifier.into_sexp(), name];
                if *is_abstract {
                    let blockabstract_sexpr: Sexp = cil_list!["blockabstract", name];
                    statement.push(blockabstract_sexpr);
                }

                if let Some(ref extends_list) = *extends {
                    for id in extends_list {
                        statement.push(cil_list!["blockinherit", id]);
                    }
                }

                for stmt in statements {
                    statement.push(stmt.into_sexp());
                }

                statement
            }
            Declaration::Symbol {
                ref qualifier,
                ref name,
                ref initializer,
            } => {
                let mut declaration: Sexp = cil_list![qualifier.into_sexp(), name];

                if qualifier.requires_initializer() {
                    if let Some(ref expr) = *initializer {
                        declaration.push(expr.into_sexp());
                    } else {
                        //@todo - raise error
                    }
                }

                declaration
            }
            Declaration::Macro {
                ref name,
                ref parameters,
                ref statements,
            } => {
                let params: Vec<Sexp> = parameters.iter().map(|it| it.into_sexp()).collect();
                let mut decl: Sexp = cil_list!["macro", name, params];

                for stmt in statements {
                    decl.push(stmt.into_sexp());
                }

                decl
            }

            _ => Sexp::Empty,
        }
    }
}

impl ToCil for Expr {
    fn into_sexp(&self) -> Sexp {
        match *self {
            Expr::Binary(ref lhs, ref op, ref rhs) => {
                cil_list![cil!(op), cil!(lhs.as_ref()), cil!(rhs.as_ref())]
            }
            Expr::Unary(ref op, ref expr) => cil_list![cil!(op), cil!(expr.as_ref())],
            Expr::Variable(ref id) => id.into(),
            Expr::VariableList(ref list) => {
                let mut list_sexpr = cil_list![];

                for identifier in list {
                    list_sexpr.push(identifier);
                }

                list_sexpr
            }
            Expr::LevelRange(ref low, ref high) => cil_list![cil!(low), cil!(high)],
            Expr::Level {
                ref sensitivity,
                ref categories,
            } => {
                let categories_sexpr: Sexp = cil_list![categories.into_sexp()];

                cil_list![sensitivity, categories_sexpr]
            }
            Expr::Context {
                ref user_id,
                ref role_id,
                ref type_id,
                ref level_range,
            } => {
                let mut context_sexp: Sexp = cil_list![user_id, role_id, type_id];

                if let Some(ref expr) = *level_range {
                    context_sexp.push(expr.as_ref().into_sexp());
                }

                context_sexp
            }
            _ => Sexp::Empty, //@TODO
        }
    }
}

impl ToCil for BinaryOp {
    fn into_sexp(&self) -> Sexp {
        let result = match *self {
            BinaryOp::BitwiseAnd | BinaryOp::ConditionalAnd => "and",
            BinaryOp::BitwiseOr | BinaryOp::ConditionalOr => "or",
            BinaryOp::BitwiseXor | BinaryOp::ConditionalXor => "xor",
        };

        result.into()
    }
}

impl ToCil for UnaryOp {
    fn into_sexp(&self) -> Sexp {
        let result = match *self {
            UnaryOp::ConditionalNot | UnaryOp::BitwiseNot => "not",
        };

        result.into()
    }
}

#[cfg(test)]
mod testing {

    use super::*;
    use secsp;

    pub fn parse_and_compile_stmt(input: &str) -> Sexp {
        let stmt = match secsp::parse_from_slice(input.as_bytes()) {
            secsp::ParseResult::Ok(stmts) => stmts[0].clone(),
            _ => panic!("Unable to parse"),
        };

        stmt.into_sexp()
    }

    #[test]
    pub fn compile_block_decl() {
        let expected: Sexp = cil_list!["block", "my_block"];
        let actual = parse_and_compile_stmt("block my_block {}");

        assert_eq!(expected, actual);
    }

    #[test]
    pub fn compile_abstract_block_decl() {
        let expected: Sexp = cil_list!["block", "my_block", cil_list!["blockabstract", "my_block"]];
        let actual = parse_and_compile_stmt("abstract block my_block {}");

        assert_eq!(expected, actual);
    }

    #[test]
    pub fn compile_block_decl_with_extends() {
        let expected =
            cil_list![
            "block",
            "my_block",
            cil_list!["blockinherit", "my_other_block"],
            cil_list!["blockinherit", "my_other_other_block"],
        ];
        let actual = parse_and_compile_stmt(
            "block my_block extends my_other_block, my_other_other_block {}",
        );

        assert_eq!(expected, actual);
    }

    #[test]
    pub fn compile_if_else() {
        let expected =
            cil_list![
            "booleanif",
            "my_bool",
            cil_list!["true"],
            cil_list!["false"],
        ];

        let actual = parse_and_compile_stmt(
            "
           if (my_bool) {

           } else {

           }",
        );

        assert_eq!(expected, actual);
    }

    #[test]
    pub fn compile_if_else_if() {
        let expected =
            cil_list![
                "booleanif",
                cil_list!["and", "a", "b"],
                cil_list!["true"],
                cil_list![
                    "false",
                    cil_list!["booleanif", "c", cil_list!["true"], cil_list!["false"]],
                ],
            ];

        let actual = parse_and_compile_stmt(
            "
            if a && b {

            } elseif c {

            } else {

            }
        ",
        );

        assert_eq!(expected, actual);
    }

    #[test]
    pub fn compile_macro_call() {
        let expected = cil_list!["call", "my_macro", cil_list!["a"]];
        let actual = parse_and_compile_stmt("my_macro(a);");

        assert_eq!(expected, actual);
    }

    #[test]
    pub fn compile_macro_decl() {
        let expected =
            cil_list![
            "macro",
            "my_macro",
            cil_list![cil_list!["type", "my_type"]],
            cil_list!["type", "my_other_type"],
        ];

        let actual = parse_and_compile_stmt(
            "
            macro my_macro (type my_type) {
                type my_other_type;
            }
        ",
        );

        assert_eq!(expected, actual);
    }

    #[test]
    pub fn compile_context_decl() {
        let expected = cil_list!["context", "my_context", cil_list!["user", "role", "type"]];
        let actual = parse_and_compile_stmt("context my_context = user:role:type;");

        assert_eq!(expected, actual);
    }

    #[test]
    pub fn compile_access_vector_rule() {
        let expected = cil_list!["allow", "src", "target", "permission_set"];
        let actual = parse_and_compile_stmt("allow src target : permission_set;");

        assert_eq!(expected, actual);
    }

    #[test]
    pub fn compile_access_vector_rule_anonymous_perms() {
        let expected =
            cil_list![
            "dontaudit",
            "src",
            "target",
            cil_list!["security_class", cil_list!["or", cil_list!["perm1", "perm2"], cil_list!["perm3", "perm4"]]],
        ];

        let actual = parse_and_compile_stmt(
            "dontaudit src target : security_class ((perm1 perm2) | (perm3 perm4));",
        );

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


<commit_before>use std::num::Zero;
use std::ptr;
use glcore::*;
// use glcore::types::GL_VERSION_1_0::*;
use nalgebra::traits::norm::Norm;
use nalgebra::traits::cross::Cross;
use nalgebra::traits::dot::Dot;
use nalgebra::vec::{Vec2, Vec3};
use nalgebra::mat::Mat4;

pub enum CameraMode
{
  ArcBall(Vec3<GLfloat>, Vec3<GLfloat>, float), // FIXME: add parameters for sencitivity
  FPS                                    // FIXME: add parameters for sencitivity
}

pub struct Camera
{
  priv changed:       bool,
  priv mode:          CameraMode,
  priv distance:      float,
  priv pitch:         float,
  priv yaw:           float,
  priv mouse_pressed: bool,
  priv mouse_start:   Vec2<float>
}

impl Camera
{
  pub fn new(mode: CameraMode) -> Camera
  {
    Camera {
      changed:       true,
      mode:          mode,
      distance:      3.0,
      pitch:         Real::pi::<float>() / 2.0,
      yaw:           0.0,
      mouse_pressed: false,
      mouse_start: Zero::zero()
    }
  }

  pub fn position(&mut self) -> Vec3<GLfloat>
  {
    match self.mode
    {
      ArcBall(ref pos, _, _) => *pos,
      FPS                    => fail!("FPS camera not yet implemented.")
    }
  }

  pub fn set_mode(&mut self, mode: CameraMode)
  {
    self.mode    = mode;
    self.changed = true;
    // FIXME: update internal datas
  }

  pub fn handle_cursor_pos(&mut self, xpos: float, ypos: float)
  {
    let yaw_step   = 0.005; // FIXME: should be a parameter of the camera
    let pitch_step = 0.005; // FIXME: should be a parameter of the camera

    if self.mouse_pressed
    {
      let dx = xpos - self.mouse_start.at[0];
      let dy = ypos - self.mouse_start.at[1];

      self.yaw   = self.yaw   - dx * yaw_step;
      self.pitch = self.pitch - dy * pitch_step;

      self.changed = true
    }

    self.mouse_start.at[0] = xpos;
    self.mouse_start.at[1] = ypos;
  }

  pub fn handle_mouse_button(&mut self, _: int, action: int, _: int)
  {
    if action == 1
    { self.mouse_pressed = true }
    else
    { self.mouse_pressed = false }
  }

  pub fn handle_scroll(&mut self, _: float, yoff: float)
  {
    match self.mode
    {
      ArcBall(_, _, zoom_factor) => self.distance += zoom_factor * yoff / 120.0,
      FPS                        => fail!("FPS mode not yet implemented.")
    }

    self.changed = true;
  }

  pub fn handle_keyboard(&mut self,
                         _: int,
                         _: int)
  {
    // FIXME: useful for FPS mode
  }

  fn update(&mut self)
  {
    let curr_mode = self.mode;
    match curr_mode
    {
      ArcBall(ref _0, ref at, ref _1) => {
        if (self.distance < 0.00001)
        { self.distance = 0.00001 }

        if (self.pitch <= 0.0001)
        { self.pitch = 0.0001 }

        if (self.pitch > Real::pi::<float>() - 0.0001)
        { self.pitch = Real::pi::<float>() - 0.0001 }

        let px = at.at[0] as float + self.distance * self.yaw.cos() * self.pitch.sin();
        let py = at.at[1] as float + self.distance * self.pitch.cos();
        let pz = at.at[2] as float + self.distance * self.yaw.sin() * self.pitch.sin();

        self.mode = ArcBall(Vec3::new([px as GLfloat, py as GLfloat, pz as GLfloat]), *at, *_1);
      }
      FPS => { }
    }
  }

  pub fn upload(&mut self, view_location: i32)
  {
    if self.changed // do not reupload if nothing changed
    {
      self.update();

      let (eye, at) = match self.mode
      {
        ArcBall(ref e, ref a, _) => (e, a),
        FPS                      => fail!("FPS camera not yet implemented.")
      };

      let zaxis = (eye - *at).normalized();
      let xaxis = Vec3::new([0.0, 1.0, 0.0]).cross(&zaxis).normalized();
      let yaxis = zaxis.cross(&xaxis);

      let look_at= Mat4::new::<GLfloat>(
        [
          xaxis.at[0], yaxis.at[0], zaxis.at[0], 0.0,
          xaxis.at[1], yaxis.at[1], zaxis.at[1], 0.0,
          xaxis.at[2], yaxis.at[2], zaxis.at[2], 0.0,
          -xaxis.dot(eye), -yaxis.dot(eye), -zaxis.dot(eye), 1.0
        ]
      );

      unsafe {
        glUniformMatrix4fv(view_location,
                           1,
                           GL_FALSE,
                           ptr::to_unsafe_ptr(&look_at.mij[0]));
      }

      self.changed = false;
    }
  }
}
<commit_msg>Fix mouse x axis inversion.<commit_after>use std::num::Zero;
use std::ptr;
use glcore::*;
// use glcore::types::GL_VERSION_1_0::*;
use nalgebra::traits::norm::Norm;
use nalgebra::traits::cross::Cross;
use nalgebra::traits::dot::Dot;
use nalgebra::vec::{Vec2, Vec3};
use nalgebra::mat::Mat4;

pub enum CameraMode
{
  ArcBall(Vec3<GLfloat>, Vec3<GLfloat>, float), // FIXME: add parameters for sencitivity
  FPS                                           // FIXME: add parameters for sencitivity
}

pub struct Camera
{
  priv changed:       bool,
  priv mode:          CameraMode,
  priv distance:      float,
  priv pitch:         float,
  priv yaw:           float,
  priv mouse_pressed: bool,
  priv mouse_start:   Vec2<float>
}

impl Camera
{
  pub fn new(mode: CameraMode) -> Camera
  {
    Camera {
      changed:       true,
      mode:          mode,
      distance:      3.0,
      pitch:         Real::pi::<float>() / 2.0,
      yaw:           0.0,
      mouse_pressed: false,
      mouse_start: Zero::zero()
    }
  }

  pub fn position(&mut self) -> Vec3<GLfloat>
  {
    match self.mode
    {
      ArcBall(ref pos, _, _) => *pos,
      FPS                    => fail!("FPS camera not yet implemented.")
    }
  }

  pub fn set_mode(&mut self, mode: CameraMode)
  {
    self.mode    = mode;
    self.changed = true;
    // FIXME: update internal datas
  }

  pub fn handle_cursor_pos(&mut self, xpos: float, ypos: float)
  {
    let yaw_step   = 0.005; // FIXME: should be a parameter of the camera
    let pitch_step = 0.005; // FIXME: should be a parameter of the camera

    if self.mouse_pressed
    {
      let dx = -xpos + self.mouse_start.at[0];
      let dy = ypos - self.mouse_start.at[1];

      self.yaw   = self.yaw   - dx * yaw_step;
      self.pitch = self.pitch - dy * pitch_step;

      self.changed = true
    }

    self.mouse_start.at[0] = xpos;
    self.mouse_start.at[1] = ypos;
  }

  pub fn handle_mouse_button(&mut self, _: int, action: int, _: int)
  {
    if action == 1
    { self.mouse_pressed = true }
    else
    { self.mouse_pressed = false }
  }

  pub fn handle_scroll(&mut self, _: float, yoff: float)
  {
    match self.mode
    {
      ArcBall(_, _, zoom_factor) => self.distance += zoom_factor * yoff / 120.0,
      FPS                        => fail!("FPS mode not yet implemented.")
    }

    self.changed = true;
  }

  pub fn handle_keyboard(&mut self,
                         _: int,
                         _: int)
  {
    // FIXME: useful for FPS mode
  }

  fn update(&mut self)
  {
    let curr_mode = self.mode;
    match curr_mode
    {
      ArcBall(ref _0, ref at, ref _1) => {
        if (self.distance < 0.00001)
        { self.distance = 0.00001 }

        if (self.pitch <= 0.0001)
        { self.pitch = 0.0001 }

        if (self.pitch > Real::pi::<float>() - 0.0001)
        { self.pitch = Real::pi::<float>() - 0.0001 }

        let px = at.at[0] as float + self.distance * self.yaw.cos() * self.pitch.sin();
        let py = at.at[1] as float + self.distance * self.pitch.cos();
        let pz = at.at[2] as float + self.distance * self.yaw.sin() * self.pitch.sin();

        self.mode = ArcBall(Vec3::new([px as GLfloat, py as GLfloat, pz as GLfloat]), *at, *_1);
      }
      FPS => { }
    }
  }

  pub fn upload(&mut self, view_location: i32)
  {
    if self.changed // do not reupload if nothing changed
    {
      self.update();

      let (eye, at) = match self.mode
      {
        ArcBall(ref e, ref a, _) => (e, a),
        FPS                      => fail!("FPS camera not yet implemented.")
      };

      let zaxis = (eye - *at).normalized();
      let xaxis = Vec3::new([0.0, 1.0, 0.0]).cross(&zaxis).normalized();
      let yaxis = zaxis.cross(&xaxis);

      let look_at= Mat4::new::<GLfloat>(
        [
          xaxis.at[0], yaxis.at[0], zaxis.at[0], 0.0,
          xaxis.at[1], yaxis.at[1], zaxis.at[1], 0.0,
          xaxis.at[2], yaxis.at[2], zaxis.at[2], 0.0,
          -xaxis.dot(eye), -yaxis.dot(eye), -zaxis.dot(eye), 1.0
        ]
      );

      unsafe {
        glUniformMatrix4fv(view_location,
                           1,
                           GL_FALSE,
                           ptr::to_unsafe_ptr(&look_at.mij[0]));
      }

      self.changed = false;
    }
  }
}
<|endoftext|>