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

use crate::error::Error;
use diem_crypto::{
    ed25519::{Ed25519PrivateKey, Ed25519PublicKey},
    x25519,
};
use diem_network_address_encryption::Encryptor;
use diem_secure_storage::{CryptoStorage, KVStorage, Storage};
use diem_types::{
    account_address::AccountAddress,
    transaction::{RawTransaction, SignedTransaction, Transaction},
    waypoint::Waypoint,
};
use serde::{de::DeserializeOwned, Serialize};

/// A helper to handle common error handling and functionality for tooling
pub struct StorageWrapper {
    pub(crate) storage_name: &'static str,
    pub(crate) storage: Storage,
}

impl StorageWrapper {
    pub fn encryptor(self) -> Encryptor<Storage> {
        Encryptor::new(self.storage)
    }

    pub fn value<T: DeserializeOwned>(&self, name: &'static str) -> Result<T, Error> {
        self.storage
            .get(name)
            .map(|v| v.value)
            .map_err(|e| Error::StorageReadError(self.storage_name, name, e.to_string()))
    }

    pub fn account_address(&self, name: &'static str) -> Result<AccountAddress, Error> {
        self.value(name)
    }

    pub fn string(&self, name: &'static str) -> Result<String, Error> {
        self.value(name)
    }

    pub fn transaction(&self, name: &'static str) -> Result<Transaction, Error> {
        self.value(name)
    }

    pub fn u64(&self, name: &'static str) -> Result<u64, Error> {
        self.value(name)
    }

    pub fn waypoint(&self, name: &'static str) -> Result<Waypoint, Error> {
        self.value(name)
    }

    /// Retrieves the Public key, that is stored as a public key
    pub fn ed25519_key(&self, name: &'static str) -> Result<Ed25519PublicKey, Error> {
        self.value(name)
    }

    /// Retrieves the Public key that is stored as a public key
    pub fn x25519_key(&self, name: &'static str) -> Result<x25519::PublicKey, Error> {
        self.ed25519_key(name).and_then(to_x25519)
    }

    pub fn rotate_key(&mut self, name: &'static str) -> Result<Ed25519PublicKey, Error> {
        self.storage
            .rotate_key(name)
            .map_err(|e| Error::StorageWriteError(self.storage_name, name, e.to_string()))
    }

    /// Retrieves public key from the stored private key
    pub fn ed25519_public_from_private(
        &self,
        key_name: &'static str,
    ) -> Result<Ed25519PublicKey, Error> {
        Ok(self
            .storage
            .get_public_key(key_name)
            .map_err(|e| Error::StorageReadError(self.storage_name, key_name, e.to_string()))?
            .public_key)
    }

    /// Retrieves the previous public key from the stored private key
    pub fn ed25519_public_from_private_previous_version(
        &self,
        key_name: &'static str,
    ) -> Result<Ed25519PublicKey, Error> {
        self.storage
            .get_public_key_previous_version(key_name)
            .map_err(|e| Error::StorageReadError(self.storage_name, key_name, e.to_string()))
    }

    /// Retrieves public key from the stored private key
    pub fn ed25519_private(&self, key_name: &'static str) -> Result<Ed25519PrivateKey, Error> {
        self.storage
            .export_private_key(key_name)
            .map_err(|e| Error::StorageReadError(self.storage_name, key_name, e.to_string()))
    }

    pub fn x25519_private(&self, key_name: &'static str) -> Result<x25519::PrivateKey, Error> {
        let key = self
            .storage
            .export_private_key(key_name)
            .map_err(|e| Error::StorageReadError(self.storage_name, key_name, e.to_string()))?;
        x25519::PrivateKey::from_ed25519_private_bytes(&key.to_bytes())
            .map_err(|e| Error::StorageReadError(self.storage_name, key_name, e.to_string()))
    }

    /// Retrieves public key from the stored private key
    pub fn x25519_public_from_private(
        &self,
        key_name: &'static str,
    ) -> Result<x25519::PublicKey, Error> {
        let key = self.ed25519_public_from_private(key_name)?;
        to_x25519(key)
    }

    pub fn set<T: Serialize>(&mut self, name: &'static str, value: T) -> Result<(), Error> {
        self.storage
            .set(name, value)
            .map_err(|e| Error::StorageWriteError(self.storage_name, name, e.to_string()))
    }

    /// Sign a transaction
    pub fn sign(
        &mut self,
        key_name: &'static str,
        script_name: &'static str,
        raw_transaction: RawTransaction,
    ) -> Result<SignedTransaction, Error> {
        let public_key = self.ed25519_public_from_private(key_name)?;
        let signature = self.storage.sign(key_name, &raw_transaction).map_err(|e| {
            Error::StorageSigningError(self.storage_name, script_name, key_name, e.to_string())
        })?;

        Ok(SignedTransaction::new(
            raw_transaction,
            public_key,
            signature,
        ))
    }

    /// Sign a transaction with the given version
    pub fn sign_using_version(
        &mut self,
        key_name: &'static str,
        key_version: Ed25519PublicKey,
        script_name: &'static str,
        raw_transaction: RawTransaction,
    ) -> Result<SignedTransaction, Error> {
        let signature = self
            .storage
            .sign_using_version(key_name, key_version.clone(), &raw_transaction)
            .map_err(|e| {
                Error::StorageSigningError(self.storage_name, script_name, key_name, e.to_string())
            })?;

        Ok(SignedTransaction::new(
            raw_transaction,
            key_version,
            signature,
        ))
    }
}

pub fn to_x25519(edkey: Ed25519PublicKey) -> Result<x25519::PublicKey, Error> {
    x25519::PublicKey::from_ed25519_public_bytes(&edkey.to_bytes())
        .map_err(|e| Error::UnexpectedError(e.to_string()))
}
<commit_msg>management: add constructor for StorageWrapper<commit_after>// Copyright (c) The Diem Core Contributors
// SPDX-License-Identifier: Apache-2.0

use crate::error::Error;
use diem_crypto::{
    ed25519::{Ed25519PrivateKey, Ed25519PublicKey},
    x25519,
};
use diem_network_address_encryption::Encryptor;
use diem_secure_storage::{CryptoStorage, KVStorage, Storage};
use diem_types::{
    account_address::AccountAddress,
    transaction::{RawTransaction, SignedTransaction, Transaction},
    waypoint::Waypoint,
};
use serde::{de::DeserializeOwned, Serialize};

/// A helper to handle common error handling and functionality for tooling
pub struct StorageWrapper {
    pub(crate) storage_name: &'static str,
    pub(crate) storage: Storage,
}

impl StorageWrapper {
    pub fn new(storage_name: &'static str, storage: Storage) -> Self {
        Self {
            storage_name,
            storage,
        }
    }

    pub fn encryptor(self) -> Encryptor<Storage> {
        Encryptor::new(self.storage)
    }

    pub fn value<T: DeserializeOwned>(&self, name: &'static str) -> Result<T, Error> {
        self.storage
            .get(name)
            .map(|v| v.value)
            .map_err(|e| Error::StorageReadError(self.storage_name, name, e.to_string()))
    }

    pub fn account_address(&self, name: &'static str) -> Result<AccountAddress, Error> {
        self.value(name)
    }

    pub fn string(&self, name: &'static str) -> Result<String, Error> {
        self.value(name)
    }

    pub fn transaction(&self, name: &'static str) -> Result<Transaction, Error> {
        self.value(name)
    }

    pub fn u64(&self, name: &'static str) -> Result<u64, Error> {
        self.value(name)
    }

    pub fn waypoint(&self, name: &'static str) -> Result<Waypoint, Error> {
        self.value(name)
    }

    /// Retrieves the Public key, that is stored as a public key
    pub fn ed25519_key(&self, name: &'static str) -> Result<Ed25519PublicKey, Error> {
        self.value(name)
    }

    /// Retrieves the Public key that is stored as a public key
    pub fn x25519_key(&self, name: &'static str) -> Result<x25519::PublicKey, Error> {
        self.ed25519_key(name).and_then(to_x25519)
    }

    pub fn rotate_key(&mut self, name: &'static str) -> Result<Ed25519PublicKey, Error> {
        self.storage
            .rotate_key(name)
            .map_err(|e| Error::StorageWriteError(self.storage_name, name, e.to_string()))
    }

    /// Retrieves public key from the stored private key
    pub fn ed25519_public_from_private(
        &self,
        key_name: &'static str,
    ) -> Result<Ed25519PublicKey, Error> {
        Ok(self
            .storage
            .get_public_key(key_name)
            .map_err(|e| Error::StorageReadError(self.storage_name, key_name, e.to_string()))?
            .public_key)
    }

    /// Retrieves the previous public key from the stored private key
    pub fn ed25519_public_from_private_previous_version(
        &self,
        key_name: &'static str,
    ) -> Result<Ed25519PublicKey, Error> {
        self.storage
            .get_public_key_previous_version(key_name)
            .map_err(|e| Error::StorageReadError(self.storage_name, key_name, e.to_string()))
    }

    /// Retrieves public key from the stored private key
    pub fn ed25519_private(&self, key_name: &'static str) -> Result<Ed25519PrivateKey, Error> {
        self.storage
            .export_private_key(key_name)
            .map_err(|e| Error::StorageReadError(self.storage_name, key_name, e.to_string()))
    }

    pub fn x25519_private(&self, key_name: &'static str) -> Result<x25519::PrivateKey, Error> {
        let key = self
            .storage
            .export_private_key(key_name)
            .map_err(|e| Error::StorageReadError(self.storage_name, key_name, e.to_string()))?;
        x25519::PrivateKey::from_ed25519_private_bytes(&key.to_bytes())
            .map_err(|e| Error::StorageReadError(self.storage_name, key_name, e.to_string()))
    }

    /// Retrieves public key from the stored private key
    pub fn x25519_public_from_private(
        &self,
        key_name: &'static str,
    ) -> Result<x25519::PublicKey, Error> {
        let key = self.ed25519_public_from_private(key_name)?;
        to_x25519(key)
    }

    pub fn set<T: Serialize>(&mut self, name: &'static str, value: T) -> Result<(), Error> {
        self.storage
            .set(name, value)
            .map_err(|e| Error::StorageWriteError(self.storage_name, name, e.to_string()))
    }

    /// Sign a transaction
    pub fn sign(
        &mut self,
        key_name: &'static str,
        script_name: &'static str,
        raw_transaction: RawTransaction,
    ) -> Result<SignedTransaction, Error> {
        let public_key = self.ed25519_public_from_private(key_name)?;
        let signature = self.storage.sign(key_name, &raw_transaction).map_err(|e| {
            Error::StorageSigningError(self.storage_name, script_name, key_name, e.to_string())
        })?;

        Ok(SignedTransaction::new(
            raw_transaction,
            public_key,
            signature,
        ))
    }

    /// Sign a transaction with the given version
    pub fn sign_using_version(
        &mut self,
        key_name: &'static str,
        key_version: Ed25519PublicKey,
        script_name: &'static str,
        raw_transaction: RawTransaction,
    ) -> Result<SignedTransaction, Error> {
        let signature = self
            .storage
            .sign_using_version(key_name, key_version.clone(), &raw_transaction)
            .map_err(|e| {
                Error::StorageSigningError(self.storage_name, script_name, key_name, e.to_string())
            })?;

        Ok(SignedTransaction::new(
            raw_transaction,
            key_version,
            signature,
        ))
    }
}

pub fn to_x25519(edkey: Ed25519PublicKey) -> Result<x25519::PublicKey, Error> {
    x25519::PublicKey::from_ed25519_public_bytes(&edkey.to_bytes())
        .map_err(|e| Error::UnexpectedError(e.to_string()))
}
<|endoftext|>


<commit_before>extern crate clap;
#[macro_use] extern crate log;
extern crate semver;
#[macro_use] extern crate version;

extern crate libimagnotes;
extern crate libimagrt;
extern crate libimagtag;
extern crate libimagutil;

use std::process::exit;

use libimagrt::runtime::Runtime;
use libimagnotes::note::Note;
use libimagutil::trace::trace_error;

mod ui;
use ui::build_ui;

fn main() {
    let name = "imag-notes";
    let version = &version!()[..];
    let about = "Note taking helper";
    let ui = build_ui(Runtime::get_default_cli_builder(name, version, about));
    let rt = {
        let rt = Runtime::new(ui);
        if rt.is_ok() {
            rt.unwrap()
        } else {
            println!("Could not set up Runtime");
            println!("{:?}", rt.err().unwrap());
            exit(1);
        }
    };

    debug!("Hello. Logging was just enabled");
    debug!("I already set up the Runtime object and build the commandline interface parser.");
    debug!("Lets get rollin' ...");

    rt.cli()
        .subcommand_name()
        .map(|name| {
            debug!("Call: {}", name);
            match name {
                "create" => create(&rt),
                "delete" => delete(&rt),
                "edit"   => edit(&rt),
                "list"   => list(&rt),
                _        => {
                    debug!("Unknown command"); // More error handling
                },
            };
        });
}

fn name_from_cli(rt: &Runtime, subcmd: &str) -> String {
    rt.cli().subcommand_matches(subcmd).unwrap().value_of("name").map(String::from).unwrap()
}

fn create(rt: &Runtime) {
    Note::new(rt.store(), String::from(name_from_cli(rt, "create")), String::new())
        .map_err(|e| trace_error(&e))
        .map(|note| {
            // call editor now...
        });
}

fn delete(rt: &Runtime) {
    Note::delete(rt.store(), String::from(name_from_cli(rt, "delete")))
        .map_err(|e| trace_error(&e))
        .map(|_| println!("Ok"));
}

fn edit(rt: &Runtime) {
    unimplemented!()
}

fn list(rt: &Runtime) {
    use std::cmp::Ordering;

    let iter = Note::all_notes(rt.store());
    if iter.is_err() {
        trace_error(&iter.err().unwrap());
        exit(1);
    }

    let mut iter = iter.unwrap()
        .filter_map(|note| {
            match note {
                Err(e) => {
                    trace_error(&e);
                    None
                },
                Ok(e) => Some(e)
            }
        })
        .collect::<Vec<Note>>();

    iter.sort_by(|note_a, note_b| {
        if let (Ok(a), Ok(b)) = (note_a.get_name(), note_b.get_name()) {
            return a.cmp(&b)
        } else {
            return Ordering::Greater;
        }
    });

    for note in iter {
        note.get_name().map(|name| println!("{}", name));
    }
}

<commit_msg>Remove warnings<commit_after>extern crate clap;
#[macro_use] extern crate log;
extern crate semver;
#[macro_use] extern crate version;

extern crate libimagnotes;
extern crate libimagrt;
extern crate libimagtag;
extern crate libimagutil;

use std::process::exit;

use libimagrt::runtime::Runtime;
use libimagnotes::note::Note;
use libimagutil::trace::trace_error;

mod ui;
use ui::build_ui;

fn main() {
    let name = "imag-notes";
    let version = &version!()[..];
    let about = "Note taking helper";
    let ui = build_ui(Runtime::get_default_cli_builder(name, version, about));
    let rt = {
        let rt = Runtime::new(ui);
        if rt.is_ok() {
            rt.unwrap()
        } else {
            println!("Could not set up Runtime");
            println!("{:?}", rt.err().unwrap());
            exit(1);
        }
    };

    debug!("Hello. Logging was just enabled");
    debug!("I already set up the Runtime object and build the commandline interface parser.");
    debug!("Lets get rollin' ...");

    rt.cli()
        .subcommand_name()
        .map(|name| {
            debug!("Call: {}", name);
            match name {
                "create" => create(&rt),
                "delete" => delete(&rt),
                "edit"   => edit(&rt),
                "list"   => list(&rt),
                _        => {
                    debug!("Unknown command"); // More error handling
                },
            };
        });
}

fn name_from_cli(rt: &Runtime, subcmd: &str) -> String {
    rt.cli().subcommand_matches(subcmd).unwrap().value_of("name").map(String::from).unwrap()
}

fn create(rt: &Runtime) {
    Note::new(rt.store(), String::from(name_from_cli(rt, "create")), String::new())
        .map_err(|e| trace_error(&e))
        .map(|_| {
            // call editor now...
        })
        .ok();
}

fn delete(rt: &Runtime) {
    Note::delete(rt.store(), String::from(name_from_cli(rt, "delete")))
        .map_err(|e| trace_error(&e))
        .map(|_| println!("Ok"))
        .ok();
}

fn edit(_: &Runtime) {
    unimplemented!()
}

fn list(rt: &Runtime) {
    use std::cmp::Ordering;

    let iter = Note::all_notes(rt.store());
    if iter.is_err() {
        trace_error(&iter.err().unwrap());
        exit(1);
    }

    let mut iter = iter.unwrap()
        .filter_map(|note| {
            match note {
                Err(e) => {
                    trace_error(&e);
                    None
                },
                Ok(e) => Some(e)
            }
        })
        .collect::<Vec<Note>>();

    iter.sort_by(|note_a, note_b| {
        if let (Ok(a), Ok(b)) = (note_a.get_name(), note_b.get_name()) {
            return a.cmp(&b)
        } else {
            return Ordering::Greater;
        }
    });

    for note in iter {
        note.get_name()
            .map(|name| println!("{}", name))
            .map_err(|e| trace_error(&e))
            .ok();
    }
}

<|endoftext|>


<commit_before>use std::result::Result;
use std::ascii::AsciiExt;

use geo::Point;

use codearea::CodeArea;

use consts::{
    SEPARATOR, SEPARATOR_POSITION, PADDING_CHAR, PADDING_CHAR_STR, CODE_ALPHABET, ENCODING_BASE,
    LATITUDE_MAX, LONGITUDE_MAX, PAIR_CODE_LENGTH, PAIR_RESOLUTIONS, GRID_COLUMNS, GRID_ROWS,
    MIN_TRIMMABLE_CODE_LEN,
};

use private::{
    code_value, normalize_longitude, clip_latitude, compute_latitude_precision, prefix_by_reference,
    narrow_region,
};

/// Determines if a code is a valid Open Location Code.
pub fn is_valid(_code: &str) -> bool {
    let mut code: String = _code.to_string();
    if code.len() < 3 {
        // A code must have at-least a separater character + 1 lat/lng pair
        return false;
    }

    // Validate separator character
    if code.find(SEPARATOR).is_none() {
        // The code MUST contain a separator character
        return false;
    }
    if code.find(SEPARATOR) != code.rfind(SEPARATOR) {
        // .. And only one separator character
        return false;
    }
    let spos = code.find(SEPARATOR).unwrap();
    if spos % 2 == 1 || spos > SEPARATOR_POSITION {
        // The separator must be in a valid location
        return false;
    }
    if code.len() - spos - 1 == 1 {
        // There must be > 1 character after the separator
        return false;
    }

    // Validate padding
    let padstart = code.find(PADDING_CHAR);
    if padstart.is_some() {
        let ppos = padstart.unwrap();
        if ppos == 0 || ppos % 2 == 1 {
            // Padding must be "within" the string, starting at an even position
            return false;
        }
        if code.len() > spos + 1 {
            // If there is padding, the code must end with the separator char
            return false;
        }
        let eppos = code.rfind(PADDING_CHAR).unwrap();
        if eppos - ppos % 2 == 1 {
            // Must have even number of padding chars
            return false;
        }
        // Extract the padding from the code (mutates code)
        let padding: String = code.drain(ppos..eppos+1).collect();
        if padding.chars().any(|c| c != PADDING_CHAR) {
            // Padding must be one, contiguous block of padding chars
            return false;
        }
    }

    // Validate all characters are permissible
    code.chars()
        .map(|c| c.to_ascii_uppercase())
        .all(|c| c == SEPARATOR || CODE_ALPHABET.contains(&c))
}

/// Determines if a code is a valid short code.
///
/// A short Open Location Code is a sequence created by removing four or more
/// digits from an Open Location Code. It must include a separator character.
pub fn is_short(_code: &str) -> bool {
    is_valid(_code) &&
        _code.find(SEPARATOR).unwrap() < SEPARATOR_POSITION
}

/// Determines if a code is a valid full Open Location Code.
///
/// Not all possible combinations of Open Location Code characters decode to
/// valid latitude and longitude values. This checks that a code is valid
/// and also that the latitude and longitude values are legal. If the prefix
/// character is present, it must be the first character. If the separator
/// character is present, it must be after four characters.
pub fn is_full(_code: &str) -> bool {
    is_valid(_code) && !is_short(_code)
}

/// Encode a location into an Open Location Code.
///
/// Produces a code of the specified length, or the default length if no
/// length is provided.
/// The length determines the accuracy of the code. The default length is
/// 10 characters, returning a code of approximately 13.5x13.5 meters. Longer
/// codes represent smaller areas, but lengths > 14 are sub-centimetre and so
/// 11 or 12 are probably the limit of useful codes.
pub fn encode(pt: Point<f64>, code_length: usize) -> String {
    let mut lat = clip_latitude(pt.lat());
    let mut lng = normalize_longitude(pt.lng());

    // Latitude 90 needs to be adjusted to be just less, so the returned code
    // can also be decoded.
    if lat > LATITUDE_MAX || (LATITUDE_MAX - lat) < 1e-10f64 {
        lat -= compute_latitude_precision(code_length);
    }

    lat += LATITUDE_MAX;
    lng += LONGITUDE_MAX;

    let mut code = String::with_capacity(code_length + 1);
    let mut digit = 0;
    while digit < code_length {
        narrow_region(digit, &mut lat, &mut lng);

        let lat_digit = lat as usize;
        let lng_digit = lng as usize;
        if digit < PAIR_CODE_LENGTH {
            code.push(CODE_ALPHABET[lat_digit]);
            code.push(CODE_ALPHABET[lng_digit]);
            digit += 2;
        } else {
            code.push(CODE_ALPHABET[4 * lat_digit + lng_digit]);
            digit += 1;
        }
        lat -= lat_digit as f64;
        lng -= lng_digit as f64;
        if digit == SEPARATOR_POSITION {
            code.push(SEPARATOR);
        }
    }
    if digit < SEPARATOR_POSITION {
        code.push_str(
            PADDING_CHAR_STR.repeat(SEPARATOR_POSITION - digit).as_str()
        );
        code.push(SEPARATOR);
    }
    code
}

/// Decodes an Open Location Code into the location coordinates.
///
/// Returns a CodeArea object that includes the coordinates of the bounding
/// box - the lower left, center and upper right.
pub fn decode(_code: &str) -> Result<CodeArea, String> {
    if !is_full(_code) {
        return Err(format!("Code must be a valid full code: {}", _code));
    }
    let code = _code.to_string()
        .replace(SEPARATOR, "")
        .replace(PADDING_CHAR_STR, "")
        .to_uppercase();

    let mut lat = -LATITUDE_MAX;
    let mut lng = -LONGITUDE_MAX;
    let mut lat_res = ENCODING_BASE * ENCODING_BASE;
    let mut lng_res = ENCODING_BASE * ENCODING_BASE;

    for (idx, chr) in code.chars().enumerate() {
        if idx < PAIR_CODE_LENGTH {
            if idx % 2 == 0 {
                lat_res /= ENCODING_BASE;
                lat += lat_res * code_value(chr) as f64;
            } else {
                lng_res /= ENCODING_BASE;
                lng += lng_res * code_value(chr) as f64;
            }
        } else {
            lat_res /= GRID_ROWS;
            lng_res /= GRID_COLUMNS;
            lat += lat_res * (code_value(chr) as f64 / GRID_COLUMNS).trunc();

            lng += lng_res * (code_value(chr) as f64 % GRID_COLUMNS);
        }
    }
    Ok(CodeArea::new(lat, lng, lat + lat_res, lng + lng_res, code.len()))
}

/// Remove characters from the start of an OLC code.
///
/// This uses a reference location to determine how many initial characters
/// can be removed from the OLC code. The number of characters that can be
/// removed depends on the distance between the code center and the reference
/// location.
/// The minimum number of characters that will be removed is four. If more
/// than four characters can be removed, the additional characters will be
/// replaced with the padding character. At most eight characters will be
/// removed.
/// The reference location must be within 50% of the maximum range. This
/// ensures that the shortened code will be able to be recovered using
/// slightly different locations.
///
/// It returns either the original code, if the reference location was not
/// close enough, or the .
pub fn shorten(_code: &str, ref_pt: Point<f64>) -> Result<String, String> {
    if !is_full(_code) {
        return Ok(_code.to_string());
    }
    if _code.find(PADDING_CHAR).is_some() {
        return Err("Cannot shorten padded codes".to_owned());
    }

    let codearea: CodeArea = decode(_code).unwrap();
    if codearea.code_length < MIN_TRIMMABLE_CODE_LEN {
        return Err(format!("Code length must be at least {}", MIN_TRIMMABLE_CODE_LEN));
    }

    // How close are the latitude and longitude to the code center.
    let range = (codearea.center.lat() - clip_latitude(ref_pt.lat())).abs().max(
        (codearea.center.lng() - normalize_longitude(ref_pt.lng())).abs()
    );

    for i in 0..PAIR_RESOLUTIONS.len() - 2 {
        // Check if we're close enough to shorten. The range must be less than 1/2
        // the resolution to shorten at all, and we want to allow some safety, so
        // use 0.3 instead of 0.5 as a multiplier.
        let idx = PAIR_RESOLUTIONS.len() - 2 - i;
        if range < (PAIR_RESOLUTIONS[idx] * 0.3f64) {
            let mut code = _code.to_string();
            code.drain(..((idx + 1) * 2));
            return Ok(code);
        }
    }
    Ok(_code.to_string())
}

/// Recover the nearest matching code to a specified location.
///
/// Given a short Open Location Code of between four and seven characters,
/// this recovers the nearest matching full code to the specified location.
/// The number of characters that will be prepended to the short code, depends
/// on the length of the short code and whether it starts with the separator.
/// If it starts with the separator, four characters will be prepended. If it
/// does not, the characters that will be prepended to the short code, where S
/// is the supplied short code and R are the computed characters, are as
/// follows:
///
/// * SSSS    -> RRRR.RRSSSS
/// * SSSSS   -> RRRR.RRSSSSS
/// * SSSSSS  -> RRRR.SSSSSS
/// * SSSSSSS -> RRRR.SSSSSSS
///
/// Note that short codes with an odd number of characters will have their
/// last character decoded using the grid refinement algorithm.
///
/// It returns the nearest full Open Location Code to the reference location
/// that matches the [shortCode]. Note that the returned code may not have the
/// same computed characters as the reference location (provided by
/// [referenceLatitude] and [referenceLongitude]). This is because it returns
/// the nearest match, not necessarily the match within the same cell. If the
/// passed code was not a valid short code, but was a valid full code, it is
/// returned unchanged.
pub fn recover_nearest(_code: &str, ref_pt: Point<f64>) -> Result<String, String> {
    if !is_short(_code) {
        if is_full(_code) {
            return Ok(_code.to_string());
        } else {
            return Err(format!("Passed short code is not valid: {}", _code));
        }
    }

    let prefix_len = SEPARATOR_POSITION - _code.find(SEPARATOR).unwrap();
    let mut code = prefix_by_reference(ref_pt, prefix_len);
    code.push_str(_code);

    let code_area = decode(code.as_str()).unwrap();

    let resolution = compute_latitude_precision(prefix_len);
    let half_res = area_range / 2f64;

    let mut latitude = code_area.center.lat();
    let mut longitude = code_area.center.lng();

    let ref_lat = clip_latitude(ref_pt.lat());
    let ref_lng = normalize_longitude(ref_pt.lng());
    if ref_lat + half_res < latitude && latitude - resolution >= -LATITUDE_MAX {
        latitude -= resolution;
    } else if ref_lat - half_res > latitude && latitude + resolution <= LATITUDE_MAX {
        latitude += resolution;
    }
    if ref_lng + half_res < longitude {
        longitude -= resolution;
    } else if ref_lng - half_res > longitude {
        longitude += resolution;
    }
    Ok(encode(Point::new(longitude, latitude), code_area.code_length))
}

<commit_msg>Update interface.rs<commit_after>use std::result::Result;
use std::ascii::AsciiExt;

use geo::Point;

use codearea::CodeArea;

use consts::{
    SEPARATOR, SEPARATOR_POSITION, PADDING_CHAR, PADDING_CHAR_STR, CODE_ALPHABET, ENCODING_BASE,
    LATITUDE_MAX, LONGITUDE_MAX, PAIR_CODE_LENGTH, PAIR_RESOLUTIONS, GRID_COLUMNS, GRID_ROWS,
    MIN_TRIMMABLE_CODE_LEN,
};

use private::{
    code_value, normalize_longitude, clip_latitude, compute_latitude_precision, prefix_by_reference,
    narrow_region,
};

/// Determines if a code is a valid Open Location Code.
pub fn is_valid(_code: &str) -> bool {
    let mut code: String = _code.to_string();
    if code.len() < 3 {
        // A code must have at-least a separater character + 1 lat/lng pair
        return false;
    }

    // Validate separator character
    if code.find(SEPARATOR).is_none() {
        // The code MUST contain a separator character
        return false;
    }
    if code.find(SEPARATOR) != code.rfind(SEPARATOR) {
        // .. And only one separator character
        return false;
    }
    let spos = code.find(SEPARATOR).unwrap();
    if spos % 2 == 1 || spos > SEPARATOR_POSITION {
        // The separator must be in a valid location
        return false;
    }
    if code.len() - spos - 1 == 1 {
        // There must be > 1 character after the separator
        return false;
    }

    // Validate padding
    let padstart = code.find(PADDING_CHAR);
    if padstart.is_some() {
        let ppos = padstart.unwrap();
        if ppos == 0 || ppos % 2 == 1 {
            // Padding must be "within" the string, starting at an even position
            return false;
        }
        if code.len() > spos + 1 {
            // If there is padding, the code must end with the separator char
            return false;
        }
        let eppos = code.rfind(PADDING_CHAR).unwrap();
        if eppos - ppos % 2 == 1 {
            // Must have even number of padding chars
            return false;
        }
        // Extract the padding from the code (mutates code)
        let padding: String = code.drain(ppos..eppos+1).collect();
        if padding.chars().any(|c| c != PADDING_CHAR) {
            // Padding must be one, contiguous block of padding chars
            return false;
        }
    }

    // Validate all characters are permissible
    code.chars()
        .map(|c| c.to_ascii_uppercase())
        .all(|c| c == SEPARATOR || CODE_ALPHABET.contains(&c))
}

/// Determines if a code is a valid short code.
///
/// A short Open Location Code is a sequence created by removing four or more
/// digits from an Open Location Code. It must include a separator character.
pub fn is_short(_code: &str) -> bool {
    is_valid(_code) &&
        _code.find(SEPARATOR).unwrap() < SEPARATOR_POSITION
}

/// Determines if a code is a valid full Open Location Code.
///
/// Not all possible combinations of Open Location Code characters decode to
/// valid latitude and longitude values. This checks that a code is valid
/// and also that the latitude and longitude values are legal. If the prefix
/// character is present, it must be the first character. If the separator
/// character is present, it must be after four characters.
pub fn is_full(_code: &str) -> bool {
    is_valid(_code) && !is_short(_code)
}

/// Encode a location into an Open Location Code.
///
/// Produces a code of the specified length, or the default length if no
/// length is provided.
/// The length determines the accuracy of the code. The default length is
/// 10 characters, returning a code of approximately 13.5x13.5 meters. Longer
/// codes represent smaller areas, but lengths > 14 are sub-centimetre and so
/// 11 or 12 are probably the limit of useful codes.
pub fn encode(pt: Point<f64>, code_length: usize) -> String {
    let mut lat = clip_latitude(pt.lat());
    let mut lng = normalize_longitude(pt.lng());

    // Latitude 90 needs to be adjusted to be just less, so the returned code
    // can also be decoded.
    if lat > LATITUDE_MAX || (LATITUDE_MAX - lat) < 1e-10f64 {
        lat -= compute_latitude_precision(code_length);
    }

    lat += LATITUDE_MAX;
    lng += LONGITUDE_MAX;

    let mut code = String::with_capacity(code_length + 1);
    let mut digit = 0;
    while digit < code_length {
        narrow_region(digit, &mut lat, &mut lng);

        let lat_digit = lat as usize;
        let lng_digit = lng as usize;
        if digit < PAIR_CODE_LENGTH {
            code.push(CODE_ALPHABET[lat_digit]);
            code.push(CODE_ALPHABET[lng_digit]);
            digit += 2;
        } else {
            code.push(CODE_ALPHABET[4 * lat_digit + lng_digit]);
            digit += 1;
        }
        lat -= lat_digit as f64;
        lng -= lng_digit as f64;
        if digit == SEPARATOR_POSITION {
            code.push(SEPARATOR);
        }
    }
    if digit < SEPARATOR_POSITION {
        code.push_str(
            PADDING_CHAR_STR.repeat(SEPARATOR_POSITION - digit).as_str()
        );
        code.push(SEPARATOR);
    }
    code
}

/// Decodes an Open Location Code into the location coordinates.
///
/// Returns a CodeArea object that includes the coordinates of the bounding
/// box - the lower left, center and upper right.
pub fn decode(_code: &str) -> Result<CodeArea, String> {
    if !is_full(_code) {
        return Err(format!("Code must be a valid full code: {}", _code));
    }
    let code = _code.to_string()
        .replace(SEPARATOR, "")
        .replace(PADDING_CHAR_STR, "")
        .to_uppercase();

    let mut lat = -LATITUDE_MAX;
    let mut lng = -LONGITUDE_MAX;
    let mut lat_res = ENCODING_BASE * ENCODING_BASE;
    let mut lng_res = ENCODING_BASE * ENCODING_BASE;

    for (idx, chr) in code.chars().enumerate() {
        if idx < PAIR_CODE_LENGTH {
            if idx % 2 == 0 {
                lat_res /= ENCODING_BASE;
                lat += lat_res * code_value(chr) as f64;
            } else {
                lng_res /= ENCODING_BASE;
                lng += lng_res * code_value(chr) as f64;
            }
        } else {
            lat_res /= GRID_ROWS;
            lng_res /= GRID_COLUMNS;
            lat += lat_res * (code_value(chr) as f64 / GRID_COLUMNS).trunc();

            lng += lng_res * (code_value(chr) as f64 % GRID_COLUMNS);
        }
    }
    Ok(CodeArea::new(lat, lng, lat + lat_res, lng + lng_res, code.len()))
}

/// Remove characters from the start of an OLC code.
///
/// This uses a reference location to determine how many initial characters
/// can be removed from the OLC code. The number of characters that can be
/// removed depends on the distance between the code center and the reference
/// location.
/// The minimum number of characters that will be removed is four. If more
/// than four characters can be removed, the additional characters will be
/// replaced with the padding character. At most eight characters will be
/// removed.
/// The reference location must be within 50% of the maximum range. This
/// ensures that the shortened code will be able to be recovered using
/// slightly different locations.
///
/// It returns either the original code, if the reference location was not
/// close enough, or the .
pub fn shorten(_code: &str, ref_pt: Point<f64>) -> Result<String, String> {
    if !is_full(_code) {
        return Ok(_code.to_string());
    }
    if _code.find(PADDING_CHAR).is_some() {
        return Err("Cannot shorten padded codes".to_owned());
    }

    let codearea: CodeArea = decode(_code).unwrap();
    if codearea.code_length < MIN_TRIMMABLE_CODE_LEN {
        return Err(format!("Code length must be at least {}", MIN_TRIMMABLE_CODE_LEN));
    }

    // How close are the latitude and longitude to the code center.
    let range = (codearea.center.lat() - clip_latitude(ref_pt.lat())).abs().max(
        (codearea.center.lng() - normalize_longitude(ref_pt.lng())).abs()
    );

    for i in 0..PAIR_RESOLUTIONS.len() - 2 {
        // Check if we're close enough to shorten. The range must be less than 1/2
        // the resolution to shorten at all, and we want to allow some safety, so
        // use 0.3 instead of 0.5 as a multiplier.
        let idx = PAIR_RESOLUTIONS.len() - 2 - i;
        if range < (PAIR_RESOLUTIONS[idx] * 0.3f64) {
            let mut code = _code.to_string();
            code.drain(..((idx + 1) * 2));
            return Ok(code);
        }
    }
    Ok(_code.to_string())
}

/// Recover the nearest matching code to a specified location.
///
/// Given a short Open Location Code of between four and seven characters,
/// this recovers the nearest matching full code to the specified location.
/// The number of characters that will be prepended to the short code, depends
/// on the length of the short code and whether it starts with the separator.
/// If it starts with the separator, four characters will be prepended. If it
/// does not, the characters that will be prepended to the short code, where S
/// is the supplied short code and R are the computed characters, are as
/// follows:
///
/// * SSSS    -> RRRR.RRSSSS
/// * SSSSS   -> RRRR.RRSSSSS
/// * SSSSSS  -> RRRR.SSSSSS
/// * SSSSSSS -> RRRR.SSSSSSS
///
/// Note that short codes with an odd number of characters will have their
/// last character decoded using the grid refinement algorithm.
///
/// It returns the nearest full Open Location Code to the reference location
/// that matches the [shortCode]. Note that the returned code may not have the
/// same computed characters as the reference location (provided by
/// [referenceLatitude] and [referenceLongitude]). This is because it returns
/// the nearest match, not necessarily the match within the same cell. If the
/// passed code was not a valid short code, but was a valid full code, it is
/// returned unchanged.
pub fn recover_nearest(_code: &str, ref_pt: Point<f64>) -> Result<String, String> {
    if !is_short(_code) {
        if is_full(_code) {
            return Ok(_code.to_string());
        } else {
            return Err(format!("Passed short code is not valid: {}", _code));
        }
    }

    let prefix_len = SEPARATOR_POSITION - _code.find(SEPARATOR).unwrap();
    let mut code = prefix_by_reference(ref_pt, prefix_len);
    code.push_str(_code);

    let code_area = decode(code.as_str()).unwrap();

    let resolution = compute_latitude_precision(prefix_len);
    let half_res = resolution / 2f64;

    let mut latitude = code_area.center.lat();
    let mut longitude = code_area.center.lng();

    let ref_lat = clip_latitude(ref_pt.lat());
    let ref_lng = normalize_longitude(ref_pt.lng());
    if ref_lat + half_res < latitude && latitude - resolution >= -LATITUDE_MAX {
        latitude -= resolution;
    } else if ref_lat - half_res > latitude && latitude + resolution <= LATITUDE_MAX {
        latitude += resolution;
    }
    if ref_lng + half_res < longitude {
        longitude -= resolution;
    } else if ref_lng - half_res > longitude {
        longitude += resolution;
    }
    Ok(encode(Point::new(longitude, latitude), code_area.code_length))
}

<|endoftext|>


<commit_before>extern crate libc;

extern {
    fn add(input1: libc::c_int, input2: libc::c_int) -> libc::c_int;
}

fn main() {
    let input1 = 4;
    let input2 = 3;
    let output = unsafe { add(input1, input2) };
    println!("{} + {} = {}", input1, input2, output);
}
<commit_msg>added test<commit_after>extern crate libc;

extern {
    fn add(input1: libc::c_int, input2: libc::c_int) -> libc::c_int;
}

#[test]
fn it_works() {
    assert_eq!(3, unsafe { add(1, 2) });
}

fn main() {
    let input1 = 4;
    let input2 = 3;
    let output = unsafe { add(input1, input2) };
    println!("{} + {} = {}", input1, input2, output);
}
<|endoftext|>


<commit_before>#[deriving(Show)]
enum ParserError {
    ExtraOperands,
    UnrecognizedInput
}

type ParserResult = Result<f64, ParserError>;

pub fn parse(input: &str) -> ParserResult {
    let mut split = input.split_str(" ");
    let mut vec = Vec::new();

    loop {
        let i = match split.next() {
            None => break,
            Some(i) => i
        };

        println!("{}", i);
        match i {
            "+" => {
                let b: f64 = vec.pop().unwrap();
                let a: f64 = vec.pop().unwrap();
                vec.push(a + b);
            },
            "-" => {
                let b: f64 = vec.pop().unwrap();
                let a: f64 = vec.pop().unwrap();
                vec.push(a - b);
            },
            "*" => {
                let b: f64 = vec.pop().unwrap();
                let a: f64 = vec.pop().unwrap();
                vec.push(a * b);
            },
            "/" => {
                let b: f64 = vec.pop().unwrap();
                let a: f64 = vec.pop().unwrap();
                vec.push(a / b);
            },
            "%" => {
                let b: f64 = vec.pop().unwrap();
                let a: f64 = vec.pop().unwrap();
                vec.push(a.rem(&b));
            },
            _ => {
                // Number
                let num: f64 = match from_str(i) {
                    None => return Err(ParserError::UnrecognizedInput),
                    Some(num) => num
                };
                vec.push(num);
            }
        };

        println!("Stack: {}", vec);
    }

    if vec.len() > 1 {
        Err(ParserError::ExtraOperands)
    } else {
        Ok(vec.pop().unwrap())
    }
}

#[cfg(test)]
mod test {
    use super::parse;

    #[test]
    fn number() {
        let result= parse("4");
        assert_eq!(4f64, result.unwrap());
    }

    #[test]
    fn add() {
        let result= parse("1 2 +");
        assert_eq!(3f64, result.unwrap());
    }

    #[test]
    fn subtract() {
        let result= parse("3 1 -");
        assert_eq!(2f64, result.unwrap());
    }

    #[test]
    fn multiply() {
        let result= parse("3 5 *");
        assert_eq!(15f64, result.unwrap());
    }

    #[test]
    fn divide() {
        let result= parse("5 2 /");
        assert_eq!(2.5f64, result.unwrap());
    }

    #[test]
    fn modulo() {
        let result= parse("20 8 %");
        assert_eq!(4f64, result.unwrap());
    }

    #[test]
    fn combo_a() {
        let result= parse("1.5 2 * 3 +");
        assert_eq!(6f64, result.unwrap());
    }

    #[test]
    fn combo_b() {
        let result= parse("4 2 1 2 + * -");
        assert_eq!(-2f64, result.unwrap());
    }
}
<commit_msg>Breaking change in Rust nightly<commit_after>#[deriving(Show)]
enum ParserError {
    ExtraOperands,
    UnrecognizedInput
}

type ParserResult = Result<f64, ParserError>;

pub fn parse(input: &str) -> ParserResult {
    let mut split = input.split_str(" ");
    let mut vec = Vec::new();

    loop {
        let i = match split.next() {
            None => break,
            Some(i) => i
        };

        println!("{}", i);
        match i {
            "+" => {
                let b: f64 = vec.pop().unwrap();
                let a: f64 = vec.pop().unwrap();
                vec.push(a + b);
            },
            "-" => {
                let b: f64 = vec.pop().unwrap();
                let a: f64 = vec.pop().unwrap();
                vec.push(a - b);
            },
            "*" => {
                let b: f64 = vec.pop().unwrap();
                let a: f64 = vec.pop().unwrap();
                vec.push(a * b);
            },
            "/" => {
                let b: f64 = vec.pop().unwrap();
                let a: f64 = vec.pop().unwrap();
                vec.push(a / b);
            },
            "%" => {
                let b: f64 = vec.pop().unwrap();
                let a: f64 = vec.pop().unwrap();
                vec.push(a.rem(b));
            },
            _ => {
                // Number
                let num: f64 = match from_str(i) {
                    None => return Err(ParserError::UnrecognizedInput),
                    Some(num) => num
                };
                vec.push(num);
            }
        };

        println!("Stack: {}", vec);
    }

    if vec.len() > 1 {
        Err(ParserError::ExtraOperands)
    } else {
        Ok(vec.pop().unwrap())
    }
}

#[cfg(test)]
mod test {
    use super::parse;

    #[test]
    fn number() {
        let result= parse("4");
        assert_eq!(4f64, result.unwrap());
    }

    #[test]
    fn add() {
        let result= parse("1 2 +");
        assert_eq!(3f64, result.unwrap());
    }

    #[test]
    fn subtract() {
        let result= parse("3 1 -");
        assert_eq!(2f64, result.unwrap());
    }

    #[test]
    fn multiply() {
        let result= parse("3 5 *");
        assert_eq!(15f64, result.unwrap());
    }

    #[test]
    fn divide() {
        let result= parse("5 2 /");
        assert_eq!(2.5f64, result.unwrap());
    }

    #[test]
    fn modulo() {
        let result= parse("20 8 %");
        assert_eq!(4f64, result.unwrap());
    }

    #[test]
    fn combo_a() {
        let result= parse("1.5 2 * 3 +");
        assert_eq!(6f64, result.unwrap());
    }

    #[test]
    fn combo_b() {
        let result= parse("4 2 1 2 + * -");
        assert_eq!(-2f64, result.unwrap());
    }
}
<|endoftext|>


<commit_before>//! Tests for profiles.

use std::env;

use cargo_test_support::project;

#[cargo_test]
fn profile_overrides() {
    let p = project()
        .file(
            "Cargo.toml",
            r#"
            [package]

            name = "test"
            version = "0.0.0"
            authors = []

            [profile.dev]
            opt-level = 1
            debug = false
            rpath = true
        "#,
        )
        .file("src/lib.rs", "")
        .build();
    p.cargo("build -v")
        .with_stderr(
            "\
[COMPILING] test v0.0.0 ([CWD])
[RUNNING] `rustc --crate-name test src/lib.rs [..]--crate-type lib \
        --emit=[..]link[..]\
        -C opt-level=1[..]\
        -C debug-assertions=on \
        -C metadata=[..] \
        -C rpath \
        --out-dir [..] \
        -L dependency=[CWD]/target/debug/deps`
[FINISHED] dev [optimized] target(s) in [..]
",
        )
        .run();
}

#[cargo_test]
fn opt_level_override_0() {
    let p = project()
        .file(
            "Cargo.toml",
            r#"
            [package]

            name = "test"
            version = "0.0.0"
            authors = []

            [profile.dev]
            opt-level = 0
        "#,
        )
        .file("src/lib.rs", "")
        .build();
    p.cargo("build -v")
        .with_stderr(
            "\
[COMPILING] test v0.0.0 ([CWD])
[RUNNING] `rustc --crate-name test src/lib.rs [..]--crate-type lib \
        --emit=[..]link[..]\
        -C debuginfo=2 \
        -C metadata=[..] \
        --out-dir [..] \
        -L dependency=[CWD]/target/debug/deps`
[FINISHED] [..] target(s) in [..]
",
        )
        .run();
}

#[cargo_test]
fn debug_override_1() {
    let p = project()
        .file(
            "Cargo.toml",
            r#"
            [package]
            name = "test"
            version = "0.0.0"
            authors = []

            [profile.dev]
            debug = 1
        "#,
        )
        .file("src/lib.rs", "")
        .build();
    p.cargo("build -v")
        .with_stderr(
            "\
[COMPILING] test v0.0.0 ([CWD])
[RUNNING] `rustc --crate-name test src/lib.rs [..]--crate-type lib \
        --emit=[..]link[..]\
        -C debuginfo=1 \
        -C metadata=[..] \
        --out-dir [..] \
        -L dependency=[CWD]/target/debug/deps`
[FINISHED] [..] target(s) in [..]
",
        )
        .run();
}

fn check_opt_level_override(profile_level: &str, rustc_level: &str) {
    let p = project()
        .file(
            "Cargo.toml",
            &format!(
                r#"
            [package]

            name = "test"
            version = "0.0.0"
            authors = []

            [profile.dev]
            opt-level = {level}
        "#,
                level = profile_level
            ),
        )
        .file("src/lib.rs", "")
        .build();
    p.cargo("build -v")
        .with_stderr(&format!(
            "\
[COMPILING] test v0.0.0 ([CWD])
[RUNNING] `rustc --crate-name test src/lib.rs [..]--crate-type lib \
        --emit=[..]link \
        -C opt-level={level}[..]\
        -C debuginfo=2 \
        -C debug-assertions=on \
        -C metadata=[..] \
        --out-dir [..] \
        -L dependency=[CWD]/target/debug/deps`
[FINISHED] [..] target(s) in [..]
",
            level = rustc_level
        ))
        .run();
}

#[cargo_test]
fn opt_level_overrides() {
    for &(profile_level, rustc_level) in &[
        ("1", "1"),
        ("2", "2"),
        ("3", "3"),
        ("\"s\"", "s"),
        ("\"z\"", "z"),
    ] {
        check_opt_level_override(profile_level, rustc_level)
    }
}

#[cargo_test]
fn top_level_overrides_deps() {
    let p = project()
        .file(
            "Cargo.toml",
            r#"
            [package]

            name = "test"
            version = "0.0.0"
            authors = []

            [profile.release]
            opt-level = 1
            debug = true

            [dependencies.foo]
            path = "foo"
        "#,
        )
        .file("src/lib.rs", "")
        .file(
            "foo/Cargo.toml",
            r#"
            [package]

            name = "foo"
            version = "0.0.0"
            authors = []

            [profile.release]
            opt-level = 0
            debug = false

            [lib]
            name = "foo"
            crate_type = ["dylib", "rlib"]
        "#,
        )
        .file("foo/src/lib.rs", "")
        .build();
    p.cargo("build -v --release")
        .with_stderr(&format!(
            "\
[COMPILING] foo v0.0.0 ([CWD]/foo)
[RUNNING] `rustc --crate-name foo foo/src/lib.rs [..]\
        --crate-type dylib --crate-type rlib \
        --emit=[..]link \
        -C prefer-dynamic \
        -C opt-level=1[..]\
        -C debuginfo=2 \
        -C metadata=[..] \
        --out-dir [CWD]/target/release/deps \
        -L dependency=[CWD]/target/release/deps`
[COMPILING] test v0.0.0 ([CWD])
[RUNNING] `rustc --crate-name test src/lib.rs [..]--crate-type lib \
        --emit=[..]link \
        -C opt-level=1[..]\
        -C debuginfo=2 \
        -C metadata=[..] \
        --out-dir [..] \
        -L dependency=[CWD]/target/release/deps \
        --extern foo=[CWD]/target/release/deps/\
                     {prefix}foo[..]{suffix} \
        --extern foo=[CWD]/target/release/deps/libfoo.rlib`
[FINISHED] release [optimized + debuginfo] target(s) in [..]
",
            prefix = env::consts::DLL_PREFIX,
            suffix = env::consts::DLL_SUFFIX
        ))
        .run();
}

#[cargo_test]
fn profile_in_non_root_manifest_triggers_a_warning() {
    let p = project()
        .file(
            "Cargo.toml",
            r#"
            [project]
            name = "foo"
            version = "0.1.0"
            authors = []

            [workspace]
            members = ["bar"]

            [profile.dev]
            debug = false
        "#,
        )
        .file("src/main.rs", "fn main() {}")
        .file(
            "bar/Cargo.toml",
            r#"
            [project]
            name = "bar"
            version = "0.1.0"
            authors = []
            workspace = ".."

            [profile.dev]
            opt-level = 1
        "#,
        )
        .file("bar/src/main.rs", "fn main() {}")
        .build();

    p.cargo("build -v")
        .cwd("bar")
        .with_stderr(
            "\
[WARNING] profiles for the non root package will be ignored, specify profiles at the workspace root:
package:   [..]
workspace: [..]
[COMPILING] bar v0.1.0 ([..])
[RUNNING] `rustc [..]`
[FINISHED] dev [unoptimized] target(s) in [..]",
        )
        .run();
}

#[cargo_test]
fn profile_in_virtual_manifest_works() {
    let p = project()
        .file(
            "Cargo.toml",
            r#"
            [workspace]
            members = ["bar"]

            [profile.dev]
            opt-level = 1
            debug = false
        "#,
        )
        .file("src/main.rs", "fn main() {}")
        .file(
            "bar/Cargo.toml",
            r#"
            [project]
            name = "bar"
            version = "0.1.0"
            authors = []
            workspace = ".."
        "#,
        )
        .file("bar/src/main.rs", "fn main() {}")
        .build();

    p.cargo("build -v")
        .cwd("bar")
        .with_stderr(
            "\
[COMPILING] bar v0.1.0 ([..])
[RUNNING] `rustc [..]`
[FINISHED] dev [optimized] target(s) in [..]",
        )
        .run();
}

#[cargo_test]
fn profile_panic_test_bench() {
    let p = project()
        .file(
            "Cargo.toml",
            r#"
            [package]
            name = "foo"
            version = "0.0.1"

            [profile.test]
            panic = "abort"

            [profile.bench]
            panic = "abort"
        "#,
        )
        .file("src/lib.rs", "")
        .build();

    p.cargo("build")
        .with_stderr_contains(
            "\
[WARNING] `panic` setting is ignored for `bench` profile
[WARNING] `panic` setting is ignored for `test` profile
",
        )
        .run();
}

#[cargo_test]
fn profile_doc_deprecated() {
    let p = project()
        .file(
            "Cargo.toml",
            r#"
            [package]
            name = "foo"
            version = "0.0.1"

            [profile.doc]
            opt-level = 0
        "#,
        )
        .file("src/lib.rs", "")
        .build();

    p.cargo("build")
        .with_stderr_contains("[WARNING] profile `doc` is deprecated and has no effect")
        .run();
}

#[cargo_test]
fn panic_unwind_does_not_build_twice() {
    // Check for a bug where `lib` was built twice, once with panic set and
    // once without. Since "unwind" is the default, they are the same and
    // should only be built once.
    let p = project()
        .file(
            "Cargo.toml",
            r#"
            [package]
            name = "foo"
            version = "0.1.0"

            [profile.dev]
            panic = "unwind"
            "#,
        )
        .file("src/lib.rs", "")
        .file("src/main.rs", "fn main() {}")
        .file("tests/t1.rs", "")
        .build();

    p.cargo("test -v --tests --no-run")
        .with_stderr_unordered(
            "\
[COMPILING] foo [..]
[RUNNING] `rustc --crate-name foo src/lib.rs [..]--crate-type lib [..]
[RUNNING] `rustc --crate-name foo src/lib.rs [..] --test [..]
[RUNNING] `rustc --crate-name foo src/main.rs [..]--crate-type bin [..]
[RUNNING] `rustc --crate-name foo src/main.rs [..] --test [..]
[RUNNING] `rustc --crate-name t1 tests/t1.rs [..]
[FINISHED] [..]
",
        )
        .run();
}

#[cargo_test]
fn debug_0_report() {
    // The finished line handles 0 correctly.
    let p = project()
        .file(
            "Cargo.toml",
            r#"
            [package]
            name = "foo"
            version = "0.1.0"

            [profile.dev]
            debug = 0
            "#,
        )
        .file("src/lib.rs", "")
        .build();

    p.cargo("build -v")
        .with_stderr(
            "\
[COMPILING] foo v0.1.0 [..]
[RUNNING] `rustc --crate-name foo src/lib.rs [..]-C debuginfo=0 [..]
[FINISHED] dev [unoptimized] target(s) in [..]
",
        )
        .run();
}

#[cargo_test]
fn thin_lto_works() {
    let p = project()
        .file(
            "Cargo.toml",
            r#"
            [project]
            name = "top"
            version = "0.5.0"
            authors = []

            [profile.release]
            lto = 'thin'
        "#,
        )
        .file("src/main.rs", "fn main() {}")
        .build();

    p.cargo("build --release -v")
        .with_stderr(
            "\
[COMPILING] top [..]
[RUNNING] `rustc [..] -C lto=thin [..]`
[FINISHED] [..]
",
        )
        .run();
}
<commit_msg>Add test<commit_after>//! Tests for profiles.

use std::env;

use cargo_test_support::project;

#[cargo_test]
fn profile_overrides() {
    let p = project()
        .file(
            "Cargo.toml",
            r#"
            [package]

            name = "test"
            version = "0.0.0"
            authors = []

            [profile.dev]
            opt-level = 1
            debug = false
            rpath = true
        "#,
        )
        .file("src/lib.rs", "")
        .build();
    p.cargo("build -v")
        .with_stderr(
            "\
[COMPILING] test v0.0.0 ([CWD])
[RUNNING] `rustc --crate-name test src/lib.rs [..]--crate-type lib \
        --emit=[..]link[..]\
        -C opt-level=1[..]\
        -C debug-assertions=on \
        -C metadata=[..] \
        -C rpath \
        --out-dir [..] \
        -L dependency=[CWD]/target/debug/deps`
[FINISHED] dev [optimized] target(s) in [..]
",
        )
        .run();
}

#[cargo_test]
fn opt_level_override_0() {
    let p = project()
        .file(
            "Cargo.toml",
            r#"
            [package]

            name = "test"
            version = "0.0.0"
            authors = []

            [profile.dev]
            opt-level = 0
        "#,
        )
        .file("src/lib.rs", "")
        .build();
    p.cargo("build -v")
        .with_stderr(
            "\
[COMPILING] test v0.0.0 ([CWD])
[RUNNING] `rustc --crate-name test src/lib.rs [..]--crate-type lib \
        --emit=[..]link[..]\
        -C debuginfo=2 \
        -C metadata=[..] \
        --out-dir [..] \
        -L dependency=[CWD]/target/debug/deps`
[FINISHED] [..] target(s) in [..]
",
        )
        .run();
}

#[cargo_test]
fn debug_override_1() {
    let p = project()
        .file(
            "Cargo.toml",
            r#"
            [package]
            name = "test"
            version = "0.0.0"
            authors = []

            [profile.dev]
            debug = 1
        "#,
        )
        .file("src/lib.rs", "")
        .build();
    p.cargo("build -v")
        .with_stderr(
            "\
[COMPILING] test v0.0.0 ([CWD])
[RUNNING] `rustc --crate-name test src/lib.rs [..]--crate-type lib \
        --emit=[..]link[..]\
        -C debuginfo=1 \
        -C metadata=[..] \
        --out-dir [..] \
        -L dependency=[CWD]/target/debug/deps`
[FINISHED] [..] target(s) in [..]
",
        )
        .run();
}

fn check_opt_level_override(profile_level: &str, rustc_level: &str) {
    let p = project()
        .file(
            "Cargo.toml",
            &format!(
                r#"
            [package]

            name = "test"
            version = "0.0.0"
            authors = []

            [profile.dev]
            opt-level = {level}
        "#,
                level = profile_level
            ),
        )
        .file("src/lib.rs", "")
        .build();
    p.cargo("build -v")
        .with_stderr(&format!(
            "\
[COMPILING] test v0.0.0 ([CWD])
[RUNNING] `rustc --crate-name test src/lib.rs [..]--crate-type lib \
        --emit=[..]link \
        -C opt-level={level}[..]\
        -C debuginfo=2 \
        -C debug-assertions=on \
        -C metadata=[..] \
        --out-dir [..] \
        -L dependency=[CWD]/target/debug/deps`
[FINISHED] [..] target(s) in [..]
",
            level = rustc_level
        ))
        .run();
}

#[cargo_test]
fn opt_level_overrides() {
    for &(profile_level, rustc_level) in &[
        ("1", "1"),
        ("2", "2"),
        ("3", "3"),
        ("\"s\"", "s"),
        ("\"z\"", "z"),
    ] {
        check_opt_level_override(profile_level, rustc_level)
    }
}

#[cargo_test]
fn top_level_overrides_deps() {
    let p = project()
        .file(
            "Cargo.toml",
            r#"
            [package]

            name = "test"
            version = "0.0.0"
            authors = []

            [profile.release]
            opt-level = 1
            debug = true

            [dependencies.foo]
            path = "foo"
        "#,
        )
        .file("src/lib.rs", "")
        .file(
            "foo/Cargo.toml",
            r#"
            [package]

            name = "foo"
            version = "0.0.0"
            authors = []

            [profile.release]
            opt-level = 0
            debug = false

            [lib]
            name = "foo"
            crate_type = ["dylib", "rlib"]
        "#,
        )
        .file("foo/src/lib.rs", "")
        .build();
    p.cargo("build -v --release")
        .with_stderr(&format!(
            "\
[COMPILING] foo v0.0.0 ([CWD]/foo)
[RUNNING] `rustc --crate-name foo foo/src/lib.rs [..]\
        --crate-type dylib --crate-type rlib \
        --emit=[..]link \
        -C prefer-dynamic \
        -C opt-level=1[..]\
        -C debuginfo=2 \
        -C metadata=[..] \
        --out-dir [CWD]/target/release/deps \
        -L dependency=[CWD]/target/release/deps`
[COMPILING] test v0.0.0 ([CWD])
[RUNNING] `rustc --crate-name test src/lib.rs [..]--crate-type lib \
        --emit=[..]link \
        -C opt-level=1[..]\
        -C debuginfo=2 \
        -C metadata=[..] \
        --out-dir [..] \
        -L dependency=[CWD]/target/release/deps \
        --extern foo=[CWD]/target/release/deps/\
                     {prefix}foo[..]{suffix} \
        --extern foo=[CWD]/target/release/deps/libfoo.rlib`
[FINISHED] release [optimized + debuginfo] target(s) in [..]
",
            prefix = env::consts::DLL_PREFIX,
            suffix = env::consts::DLL_SUFFIX
        ))
        .run();
}

#[cargo_test]
fn profile_in_non_root_manifest_triggers_a_warning() {
    let p = project()
        .file(
            "Cargo.toml",
            r#"
            [project]
            name = "foo"
            version = "0.1.0"
            authors = []

            [workspace]
            members = ["bar"]

            [profile.dev]
            debug = false
        "#,
        )
        .file("src/main.rs", "fn main() {}")
        .file(
            "bar/Cargo.toml",
            r#"
            [project]
            name = "bar"
            version = "0.1.0"
            authors = []
            workspace = ".."

            [profile.dev]
            opt-level = 1
        "#,
        )
        .file("bar/src/main.rs", "fn main() {}")
        .build();

    p.cargo("build -v")
        .cwd("bar")
        .with_stderr(
            "\
[WARNING] profiles for the non root package will be ignored, specify profiles at the workspace root:
package:   [..]
workspace: [..]
[COMPILING] bar v0.1.0 ([..])
[RUNNING] `rustc [..]`
[FINISHED] dev [unoptimized] target(s) in [..]",
        )
        .run();
}

#[cargo_test]
fn profile_in_virtual_manifest_works() {
    let p = project()
        .file(
            "Cargo.toml",
            r#"
            [workspace]
            members = ["bar"]

            [profile.dev]
            opt-level = 1
            debug = false
        "#,
        )
        .file("src/main.rs", "fn main() {}")
        .file(
            "bar/Cargo.toml",
            r#"
            [project]
            name = "bar"
            version = "0.1.0"
            authors = []
            workspace = ".."
        "#,
        )
        .file("bar/src/main.rs", "fn main() {}")
        .build();

    p.cargo("build -v")
        .cwd("bar")
        .with_stderr(
            "\
[COMPILING] bar v0.1.0 ([..])
[RUNNING] `rustc [..]`
[FINISHED] dev [optimized] target(s) in [..]",
        )
        .run();
}

#[cargo_test]
fn profile_panic_test_bench() {
    let p = project()
        .file(
            "Cargo.toml",
            r#"
            [package]
            name = "foo"
            version = "0.0.1"

            [profile.test]
            panic = "abort"

            [profile.bench]
            panic = "abort"
        "#,
        )
        .file("src/lib.rs", "")
        .build();

    p.cargo("build")
        .with_stderr_contains(
            "\
[WARNING] `panic` setting is ignored for `bench` profile
[WARNING] `panic` setting is ignored for `test` profile
",
        )
        .run();
}

#[cargo_test]
fn profile_doc_deprecated() {
    let p = project()
        .file(
            "Cargo.toml",
            r#"
            [package]
            name = "foo"
            version = "0.0.1"

            [profile.doc]
            opt-level = 0
        "#,
        )
        .file("src/lib.rs", "")
        .build();

    p.cargo("build")
        .with_stderr_contains("[WARNING] profile `doc` is deprecated and has no effect")
        .run();
}

#[cargo_test]
fn panic_unwind_does_not_build_twice() {
    // Check for a bug where `lib` was built twice, once with panic set and
    // once without. Since "unwind" is the default, they are the same and
    // should only be built once.
    let p = project()
        .file(
            "Cargo.toml",
            r#"
            [package]
            name = "foo"
            version = "0.1.0"

            [profile.dev]
            panic = "unwind"
            "#,
        )
        .file("src/lib.rs", "")
        .file("src/main.rs", "fn main() {}")
        .file("tests/t1.rs", "")
        .build();

    p.cargo("test -v --tests --no-run")
        .with_stderr_unordered(
            "\
[COMPILING] foo [..]
[RUNNING] `rustc --crate-name foo src/lib.rs [..]--crate-type lib [..]
[RUNNING] `rustc --crate-name foo src/lib.rs [..] --test [..]
[RUNNING] `rustc --crate-name foo src/main.rs [..]--crate-type bin [..]
[RUNNING] `rustc --crate-name foo src/main.rs [..] --test [..]
[RUNNING] `rustc --crate-name t1 tests/t1.rs [..]
[FINISHED] [..]
",
        )
        .run();
}

#[cargo_test]
fn debug_0_report() {
    // The finished line handles 0 correctly.
    let p = project()
        .file(
            "Cargo.toml",
            r#"
            [package]
            name = "foo"
            version = "0.1.0"

            [profile.dev]
            debug = 0
            "#,
        )
        .file("src/lib.rs", "")
        .build();

    p.cargo("build -v")
        .with_stderr(
            "\
[COMPILING] foo v0.1.0 [..]
[RUNNING] `rustc --crate-name foo src/lib.rs [..]-C debuginfo=0 [..]
[FINISHED] dev [unoptimized] target(s) in [..]
",
        )
        .run();
}

#[cargo_test]
fn thin_lto_works() {
    let p = project()
        .file(
            "Cargo.toml",
            r#"
            [project]
            name = "top"
            version = "0.5.0"
            authors = []

            [profile.release]
            lto = 'thin'
        "#,
        )
        .file("src/main.rs", "fn main() {}")
        .build();

    p.cargo("build --release -v")
        .with_stderr(
            "\
[COMPILING] top [..]
[RUNNING] `rustc [..] -C lto=thin [..]`
[FINISHED] [..]
",
        )
        .run();
}

#[cargo_test]
fn strip_works() {
    let p = project()
        .file(
            "Cargo.toml",
            r#"
            cargo-features = ["strip"]

            [package]
            name = "foo"
            version = "0.1.0"

            [profile.release]
            strip = 'symbols'
        "#,
        )
        .file("src/main.rs", "fn main() {}")
        .build();

    p.cargo("build --release -v")
        .masquerade_as_nightly_cargo()
        .with_stderr(
            "\
[COMPILING] foo [..]
[RUNNING] `rustc [..] -Z strip=symbols [..]`
[FINISHED] [..]
",
        )
        .run();
}
<|endoftext|>


<commit_before>#![feature(plugin, custom_derive, custom_attribute)]
#![plugin(rocket_codegen)]
extern crate rocket;
extern crate serde;
extern crate itertools;

#[macro_use]
extern crate serde_derive;
extern crate rocket_contrib;

#[cfg(test)]
extern crate serde_json;

use rocket_contrib::Json;

mod automata;
use automata::{Nfa, Unsanitary};

#[get("/")]
fn index() -> &'static str {
    //Render home page
    "Hello, World!"
}

// The type to represent the ID of a message.
type ID = usize;

#[derive(Serialize, Deserialize)]
struct Message {
        id: Option<ID>,
            contents: String
}

// curl -X POST --data '{"id": 1, "contents":"asdf"}' http://localhost:8000/test -H "Content-Type: application/json"
#[post("/test", format="application/json", data="<data>")]
fn test_thing(data: Json<Message>) -> &'static str {
    "it works!"
}

#[post("/submit", format="application/json", data="<data>")]
fn submit_nfa(data: Json<Nfa<Unsanitary>>) -> &'static str {
    "it works!"
}

fn main() {
    rocket::ignite().mount("/", routes![index, test_thing, submit_nfa]).launch();
}
<commit_msg>submit returns json<dfa><commit_after>#![feature(plugin, custom_derive, custom_attribute)]
#![plugin(rocket_codegen)]
extern crate rocket;
extern crate serde;
extern crate itertools;

#[macro_use]
extern crate serde_derive;
extern crate rocket_contrib;

#[cfg(test)]
extern crate serde_json;

#[cfg(test)]

use rocket_contrib::Json;

mod automata;
use automata::{Nfa, Dfa, Unsanitary};

#[get("/")]
fn index() -> &'static str {
    //Render home page
    "Hello, World!"
}

#[post("/submit", format="application/json", data="<data>")]
fn submit_nfa(data: Json<Nfa<Unsanitary>>) -> Json<Dfa> {
    Json(data.into_inner().check().unwrap().make_deterministic())
}

fn main() {
    rocket::ignite().mount("/", routes![index, submit_nfa]).launch();
}
<|endoftext|>


<commit_before>use std::cell::RefCell;
use std::collections::HashMap;
use std::fs;
use std::fs::{File, OpenOptions};
use std::io;
use std::io::Write;
use std::path::PathBuf;
use std::rc::Rc;
use time::Tm;

use libtiny_common::{ChanName, ChanNameRef, MsgTarget};

#[macro_use]
extern crate log;

#[derive(Clone)]
pub struct Logger {
    inner: Rc<RefCell<LoggerInner>>,
}

#[derive(Debug)]
pub enum LoggerInitError {
    CouldNotCreateDir { dir_path: PathBuf, err: io::Error },
}

impl Logger {
    pub fn new(
        log_dir: PathBuf,
        report_err: Box<dyn Fn(String)>,
    ) -> Result<Logger, LoggerInitError> {
        Ok(Logger {
            inner: Rc::new(RefCell::new(LoggerInner::new(log_dir, report_err)?)),
        })
    }
}

macro_rules! delegate {
    ( $name:ident ( $( $x:ident: $t:ty, )* ) ) => {
        pub fn $name(&self, $($x: $t,)*) {
            self.inner.borrow_mut().$name( $( $x, )* )
        }
    }
}

impl Logger {
    delegate!(new_server_tab(serv: &str,));
    delegate!(close_server_tab(serv: &str,));
    delegate!(new_chan_tab(serv: &str, chan: &ChanNameRef,));
    delegate!(close_chan_tab(serv: &str, chan: &ChanNameRef,));
    delegate!(close_user_tab(serv: &str, nick: &str,));
    delegate!(add_client_msg(msg: &str, target: &MsgTarget,));
    delegate!(add_msg(msg: &str, ts: Tm, target: &MsgTarget,));
    delegate!(add_privmsg(
        sender: &str,
        msg: &str,
        ts: Tm,
        target: &MsgTarget,
        highlight: bool,
        is_action: bool,
    ));
    delegate!(add_nick(nick: &str, ts: Option<Tm>, target: &MsgTarget,));
    delegate!(remove_nick(nick: &str, ts: Option<Tm>, target: &MsgTarget,));
    delegate!(rename_nick(
        old_nick: &str,
        new_nick: &str,
        ts: Tm,
        target: &MsgTarget,
    ));
    delegate!(set_topic(
        topic: &str,
        ts: Tm,
        serv: &str,
        chan: &ChanNameRef,
    ));
}

struct LoggerInner {
    /// Log file directory
    log_dir: PathBuf,

    /// Maps server names to their fds
    servers: HashMap<String, ServerLogs>,

    /// Callback used when reporting errors
    report_err: Box<dyn Fn(String)>,
}

struct ServerLogs {
    fd: File,
    chans: HashMap<ChanName, File>,
    users: HashMap<String, File>,
}

fn print_header(fd: &mut File) -> io::Result<()> {
    writeln!(fd)?;
    writeln!(
        fd,
        "*** Logging started at {}",
        time::strftime("%Y-%m-%d %H:%M:%S", &time::now()).unwrap()
    )?;
    writeln!(fd)
}

macro_rules! report_io_err {
    ( $f:expr, $e:expr ) => {
        match $e {
            Err(err) => {
                info!("{:?}", err);
                $f(format!("{:?}", err));
                return;
            }
            Ok(ok) => ok,
        }
    };
}

// '/' is valid in channel names but we can't use it in file names, so we replace it with '-'.
// According to RFC 2812 nick names can't contain '/', but we still use this in nicks just to be
// safe. Other special characters mentioned in the RFC ("[]\`^{|}") can be used in file names so we
// don't replace those.
fn replace_forward_slash(path: &str) -> String {
    path.replace('/', "-")
}

fn try_open_log_file(path: &PathBuf, report_err: &dyn Fn(String)) -> Option<File> {
    match OpenOptions::new().create(true).append(true).open(path) {
        Ok(fd) => Some(fd),
        Err(err) => {
            report_err(format!("Couldn't open file {:?}: {}", path, err));
            None
        }
    }
}

impl LoggerInner {
    fn new(
        log_dir: PathBuf,
        report_err: Box<dyn Fn(String)>,
    ) -> Result<LoggerInner, LoggerInitError> {
        if let Err(err) = fs::create_dir_all(&log_dir) {
            if err.kind() != io::ErrorKind::AlreadyExists {
                return Err(LoggerInitError::CouldNotCreateDir {
                    dir_path: log_dir,
                    err,
                });
            }
        }

        Ok(LoggerInner {
            log_dir,
            servers: HashMap::new(),
            report_err,
        })
    }

    fn new_server_tab(&mut self, serv: &str) {
        let mut path = self.log_dir.clone();
        path.push(&format!("{}.txt", serv));
        if let Some(mut fd) = try_open_log_file(&path, &*self.report_err) {
            report_io_err!(self.report_err, print_header(&mut fd));
            self.servers.insert(
                serv.to_string(),
                ServerLogs {
                    fd,
                    chans: HashMap::new(),
                    users: HashMap::new(),
                },
            );
        }
    }

    fn close_server_tab(&mut self, serv: &str) {
        self.servers.remove(serv);
    }

    fn new_chan_tab(&mut self, serv: &str, chan: &ChanNameRef) {
        match self.servers.get_mut(serv) {
            None => {
                info!("new_chan_tab: can't find server: {}", serv);
            }
            Some(server) => {
                let mut path = self.log_dir.clone();
                let chan_name_normalized = chan.normalized();
                path.push(&format!(
                    "{}_{}.txt",
                    serv,
                    replace_forward_slash(&chan_name_normalized)
                ));
                if let Some(mut fd) = try_open_log_file(&path, &*self.report_err) {
                    report_io_err!(self.report_err, print_header(&mut fd));
                    server.chans.insert(ChanName::new(chan_name_normalized), fd);
                }
            }
        }
    }

    fn close_chan_tab(&mut self, serv: &str, chan: &ChanNameRef) {
        match self.servers.get_mut(serv) {
            None => {
                info!("close_chan_tab: can't find server: {}", serv);
            }
            Some(server) => {
                server.chans.remove(chan);
            }
        }
    }

    fn close_user_tab(&mut self, serv: &str, nick: &str) {
        match self.servers.get_mut(serv) {
            None => {
                info!("close_user_tab: can't find server: {}", serv);
            }
            Some(server) => {
                server.users.remove(nick);
            }
        }
    }

    fn add_client_msg(&mut self, msg: &str, target: &MsgTarget) {
        let now = now();
        self.apply_to_target(target, |fd: &mut File, report_err: &dyn Fn(String)| {
            report_io_err!(report_err, writeln!(fd, "[{}] [client] {}", now, msg));
        });
    }

    fn add_msg(&mut self, msg: &str, ts: Tm, target: &MsgTarget) {
        self.apply_to_target(target, |fd: &mut File, report_err: &dyn Fn(String)| {
            report_io_err!(report_err, writeln!(fd, "[{}] {}", strf(&ts), msg));
        });
    }

    fn add_privmsg(
        &mut self,
        sender: &str,
        msg: &str,
        ts: Tm,
        target: &MsgTarget,
        _highlight: bool,
        is_action: bool,
    ) {
        self.apply_to_target(target, |fd: &mut File, report_err: &dyn Fn(String)| {
            let io_ret = if is_action {
                writeln!(fd, "[{}] {} {}", strf(&ts), sender, msg)
            } else {
                writeln!(fd, "[{}] {}: {}", strf(&ts), sender, msg)
            };
            report_io_err!(report_err, io_ret);
        });
    }

    fn add_nick(&mut self, nick: &str, ts: Option<Tm>, target: &MsgTarget) {
        if let Some(_ts) = ts {
            // This method is only called when a user joins a chan
            self.apply_to_target(target, |fd: &mut File, report_err: &dyn Fn(String)| {
                report_io_err!(
                    report_err,
                    writeln!(fd, "[{}] {} joined the channel.", now(), nick)
                );
            });
        }
    }

    fn remove_nick(&mut self, nick: &str, ts: Option<Tm>, target: &MsgTarget) {
        if let Some(_ts) = ts {
            // TODO: Did the user leave a channel or the server? Currently we can't tell.
            self.apply_to_target(target, |fd: &mut File, report_err: &dyn Fn(String)| {
                report_io_err!(report_err, writeln!(fd, "[{}] {} left.", now(), nick));
            });
        }
    }

    fn rename_nick(&mut self, old_nick: &str, new_nick: &str, ts: Tm, target: &MsgTarget) {
        self.apply_to_target(target, |fd: &mut File, report_err: &dyn Fn(String)| {
            report_io_err!(
                report_err,
                writeln!(
                    fd,
                    "[{}] {} is now known as {}.",
                    strf(&ts),
                    old_nick,
                    new_nick
                )
            );
        });
    }

    fn set_topic(&mut self, topic: &str, ts: Tm, serv: &str, chan: &ChanNameRef) {
        let target = MsgTarget::Chan { serv, chan };
        self.apply_to_target(&target, |fd: &mut File, report_err: &dyn Fn(String)| {
            report_io_err!(
                report_err,
                writeln!(fd, "[{}] Channel topic: {}.", strf(&ts), topic)
            );
        });
    }

    fn apply_to_target(&mut self, target: &MsgTarget, f: impl Fn(&mut File, &dyn Fn(String))) {
        match *target {
            MsgTarget::Server { serv } => match self.servers.get_mut(serv) {
                None => {
                    info!("Can't find server: {}", serv);
                }
                Some(ServerLogs { ref mut fd, .. }) => {
                    f(fd, &*self.report_err);
                }
            },
            MsgTarget::Chan { serv, chan } => match self.servers.get_mut(serv) {
                None => {
                    info!("Can't find server: {}", serv);
                }
                Some(ServerLogs { ref mut chans, .. }) => match chans.get_mut(chan) {
                    None => {
                        // Create a file for the channel. FIXME Code copied from new_chan_tab:
                        // can't reuse it because of borrowchk issues.
                        let mut path = self.log_dir.clone();
                        let chan_name_normalized = chan.normalized();
                        path.push(&format!(
                            "{}_{}.txt",
                            serv,
                            replace_forward_slash(&chan_name_normalized)
                        ));
                        if let Some(mut fd) = try_open_log_file(&path, &*self.report_err) {
                            report_io_err!(self.report_err, print_header(&mut fd));
                            f(&mut fd, &*self.report_err);
                            chans.insert(ChanName::new(chan_name_normalized), fd);
                        }
                    }
                    Some(fd) => {
                        f(fd, &*self.report_err);
                    }
                },
            },
            MsgTarget::User { serv, nick } => {
                match self.servers.get_mut(serv) {
                    None => {
                        info!("Can't find server: {}", serv);
                    }
                    Some(ServerLogs { ref mut users, .. }) => {
                        match users.get_mut(nick) {
                            Some(fd) => {
                                f(fd, &*self.report_err);
                            }
                            None => {
                                // We don't have a `new_user_tab` trait method so user log files
                                // are created here
                                let mut path = self.log_dir.clone();
                                path.push(&format!("{}_{}.txt", serv, replace_forward_slash(nick)));
                                if let Some(mut fd) = try_open_log_file(&path, &*self.report_err) {
                                    report_io_err!(self.report_err, print_header(&mut fd));
                                    f(&mut fd, &*self.report_err);
                                    users.insert(nick.to_owned(), fd);
                                }
                            }
                        }
                    }
                }
            }
            MsgTarget::AllServTabs { serv } => match self.servers.get_mut(serv) {
                None => {
                    info!("Can't find server: {}", serv);
                }
                Some(ServerLogs {
                    ref mut fd,
                    ref mut chans,
                    ref mut users,
                    ..
                }) => {
                    f(fd, &*self.report_err);
                    for (_, fd) in chans.iter_mut() {
                        f(fd, &*self.report_err);
                    }
                    for (_, fd) in users.iter_mut() {
                        f(fd, &*self.report_err);
                    }
                }
            },
            MsgTarget::CurrentTab => {
                // Probably a cmd error; these are ignored
            }
        }
    }
}

fn now() -> String {
    time::strftime("%H:%M:%S", &time::now()).unwrap()
}

fn strf(tm: &Tm) -> String {
    time::strftime("%H:%M:%S", tm).unwrap()
}
<commit_msg>Generate "Logging ended at ..." lines in logger when closing tabs<commit_after>use std::cell::RefCell;
use std::collections::HashMap;
use std::fs;
use std::fs::{File, OpenOptions};
use std::io;
use std::io::Write;
use std::path::PathBuf;
use std::rc::Rc;
use time::Tm;

use libtiny_common::{ChanName, ChanNameRef, MsgTarget};

#[macro_use]
extern crate log;

#[derive(Clone)]
pub struct Logger {
    inner: Rc<RefCell<LoggerInner>>,
}

#[derive(Debug)]
pub enum LoggerInitError {
    CouldNotCreateDir { dir_path: PathBuf, err: io::Error },
}

impl Logger {
    pub fn new(
        log_dir: PathBuf,
        report_err: Box<dyn Fn(String)>,
    ) -> Result<Logger, LoggerInitError> {
        Ok(Logger {
            inner: Rc::new(RefCell::new(LoggerInner::new(log_dir, report_err)?)),
        })
    }
}

macro_rules! delegate {
    ( $name:ident ( $( $x:ident: $t:ty, )* ) ) => {
        pub fn $name(&self, $($x: $t,)*) {
            self.inner.borrow_mut().$name( $( $x, )* )
        }
    }
}

impl Logger {
    delegate!(new_server_tab(serv: &str,));
    delegate!(close_server_tab(serv: &str,));
    delegate!(new_chan_tab(serv: &str, chan: &ChanNameRef,));
    delegate!(close_chan_tab(serv: &str, chan: &ChanNameRef,));
    delegate!(close_user_tab(serv: &str, nick: &str,));
    delegate!(add_client_msg(msg: &str, target: &MsgTarget,));
    delegate!(add_msg(msg: &str, ts: Tm, target: &MsgTarget,));
    delegate!(add_privmsg(
        sender: &str,
        msg: &str,
        ts: Tm,
        target: &MsgTarget,
        highlight: bool,
        is_action: bool,
    ));
    delegate!(add_nick(nick: &str, ts: Option<Tm>, target: &MsgTarget,));
    delegate!(remove_nick(nick: &str, ts: Option<Tm>, target: &MsgTarget,));
    delegate!(rename_nick(
        old_nick: &str,
        new_nick: &str,
        ts: Tm,
        target: &MsgTarget,
    ));
    delegate!(set_topic(
        topic: &str,
        ts: Tm,
        serv: &str,
        chan: &ChanNameRef,
    ));
}

struct LoggerInner {
    /// Log file directory
    log_dir: PathBuf,

    /// Maps server names to their fds
    servers: HashMap<String, ServerLogs>,

    /// Callback used when reporting errors
    report_err: Box<dyn Fn(String)>,
}

impl Drop for LoggerInner {
    fn drop(&mut self) {
        for (_, server) in self.servers.drain() {
            close_server_tabs(server, &self.report_err);
        }
    }
}

struct ServerLogs {
    fd: File,
    chans: HashMap<ChanName, File>,
    users: HashMap<String, File>,
}

fn print_header(fd: &mut File) -> io::Result<()> {
    writeln!(fd)?;
    writeln!(
        fd,
        "*** Logging started at {}",
        time::strftime("%Y-%m-%d %H:%M:%S", &time::now()).unwrap()
    )?;
    writeln!(fd)
}

fn print_footer(fd: &mut File) -> io::Result<()> {
    writeln!(fd)?;
    writeln!(
        fd,
        "*** Logging ended at {}",
        time::strftime("%Y-%m-%d %H:%M:%S", &time::now()).unwrap()
    )?;
    writeln!(fd)
}

macro_rules! report_io_err {
    ( $f:expr, $e:expr ) => {
        match $e {
            Err(err) => {
                info!("{:?}", err);
                $f(format!("{:?}", err));
                return;
            }
            Ok(ok) => ok,
        }
    };
}

// '/' is valid in channel names but we can't use it in file names, so we replace it with '-'.
// According to RFC 2812 nick names can't contain '/', but we still use this in nicks just to be
// safe. Other special characters mentioned in the RFC ("[]\`^{|}") can be used in file names so we
// don't replace those.
fn replace_forward_slash(path: &str) -> String {
    path.replace('/', "-")
}

fn try_open_log_file(path: &PathBuf, report_err: &dyn Fn(String)) -> Option<File> {
    match OpenOptions::new().create(true).append(true).open(path) {
        Ok(fd) => Some(fd),
        Err(err) => {
            report_err(format!("Couldn't open file {:?}: {}", path, err));
            None
        }
    }
}

fn close_server_tabs(server: ServerLogs, report_err: &dyn Fn(String)) {
    let ServerLogs {
        mut fd,
        chans,
        users,
    } = server;
    report_io_err!(report_err, print_footer(&mut fd));
    for (_, mut fd) in chans.into_iter() {
        report_io_err!(report_err, print_footer(&mut fd));
    }
    for (_, mut fd) in users.into_iter() {
        report_io_err!(report_err, print_footer(&mut fd));
    }
}

impl LoggerInner {
    fn new(
        log_dir: PathBuf,
        report_err: Box<dyn Fn(String)>,
    ) -> Result<LoggerInner, LoggerInitError> {
        if let Err(err) = fs::create_dir_all(&log_dir) {
            if err.kind() != io::ErrorKind::AlreadyExists {
                return Err(LoggerInitError::CouldNotCreateDir {
                    dir_path: log_dir,
                    err,
                });
            }
        }

        Ok(LoggerInner {
            log_dir,
            servers: HashMap::new(),
            report_err,
        })
    }

    fn new_server_tab(&mut self, serv: &str) {
        let mut path = self.log_dir.clone();
        path.push(&format!("{}.txt", serv));
        if let Some(mut fd) = try_open_log_file(&path, &*self.report_err) {
            report_io_err!(self.report_err, print_header(&mut fd));
            self.servers.insert(
                serv.to_string(),
                ServerLogs {
                    fd,
                    chans: HashMap::new(),
                    users: HashMap::new(),
                },
            );
        }
    }

    fn close_server_tab(&mut self, serv: &str) {
        match self.servers.remove(serv) {
            None => {
                info!("close_server_tab: can't find server: {:?}", serv);
            }
            Some(server) => {
                close_server_tabs(server, &self.report_err);
            }
        }
    }

    fn new_chan_tab(&mut self, serv: &str, chan: &ChanNameRef) {
        match self.servers.get_mut(serv) {
            None => {
                info!("new_chan_tab: can't find server: {:?}", serv);
            }
            Some(server) => {
                let mut path = self.log_dir.clone();
                let chan_name_normalized = chan.normalized();
                path.push(&format!(
                    "{}_{}.txt",
                    serv,
                    replace_forward_slash(&chan_name_normalized)
                ));
                if let Some(mut fd) = try_open_log_file(&path, &*self.report_err) {
                    report_io_err!(self.report_err, print_header(&mut fd));
                    server.chans.insert(ChanName::new(chan_name_normalized), fd);
                }
            }
        }
    }

    fn close_chan_tab(&mut self, serv: &str, chan: &ChanNameRef) {
        match self.servers.get_mut(serv) {
            None => {
                info!("close_chan_tab: can't find server: {:?}", serv);
            }
            Some(server) => match server.chans.remove(chan) {
                None => {
                    info!(
                        "close_chan_tab: can't find chan {:?} in server {:?}",
                        chan.display(),
                        serv
                    );
                }
                Some(mut fd) => {
                    report_io_err!(self.report_err, print_footer(&mut fd));
                }
            },
        }
    }

    fn close_user_tab(&mut self, serv: &str, nick: &str) {
        match self.servers.get_mut(serv) {
            None => {
                info!("close_user_tab: can't find server: {:?}", serv);
            }
            Some(server) => match server.users.remove(nick) {
                None => {
                    info!(
                        "close_user_tab: can't find user {:?} in server {:?}",
                        nick, serv
                    );
                }
                Some(mut fd) => {
                    report_io_err!(self.report_err, print_footer(&mut fd));
                }
            },
        }
    }

    fn add_client_msg(&mut self, msg: &str, target: &MsgTarget) {
        let now = now();
        self.apply_to_target(target, |fd: &mut File, report_err: &dyn Fn(String)| {
            report_io_err!(report_err, writeln!(fd, "[{}] [client] {}", now, msg));
        });
    }

    fn add_msg(&mut self, msg: &str, ts: Tm, target: &MsgTarget) {
        self.apply_to_target(target, |fd: &mut File, report_err: &dyn Fn(String)| {
            report_io_err!(report_err, writeln!(fd, "[{}] {}", strf(&ts), msg));
        });
    }

    fn add_privmsg(
        &mut self,
        sender: &str,
        msg: &str,
        ts: Tm,
        target: &MsgTarget,
        _highlight: bool,
        is_action: bool,
    ) {
        self.apply_to_target(target, |fd: &mut File, report_err: &dyn Fn(String)| {
            let io_ret = if is_action {
                writeln!(fd, "[{}] {} {}", strf(&ts), sender, msg)
            } else {
                writeln!(fd, "[{}] {}: {}", strf(&ts), sender, msg)
            };
            report_io_err!(report_err, io_ret);
        });
    }

    fn add_nick(&mut self, nick: &str, ts: Option<Tm>, target: &MsgTarget) {
        if let Some(_ts) = ts {
            // This method is only called when a user joins a chan
            self.apply_to_target(target, |fd: &mut File, report_err: &dyn Fn(String)| {
                report_io_err!(
                    report_err,
                    writeln!(fd, "[{}] {} joined the channel.", now(), nick)
                );
            });
        }
    }

    fn remove_nick(&mut self, nick: &str, ts: Option<Tm>, target: &MsgTarget) {
        if let Some(_ts) = ts {
            // TODO: Did the user leave a channel or the server? Currently we can't tell.
            self.apply_to_target(target, |fd: &mut File, report_err: &dyn Fn(String)| {
                report_io_err!(report_err, writeln!(fd, "[{}] {} left.", now(), nick));
            });
        }
    }

    fn rename_nick(&mut self, old_nick: &str, new_nick: &str, ts: Tm, target: &MsgTarget) {
        self.apply_to_target(target, |fd: &mut File, report_err: &dyn Fn(String)| {
            report_io_err!(
                report_err,
                writeln!(
                    fd,
                    "[{}] {} is now known as {}.",
                    strf(&ts),
                    old_nick,
                    new_nick
                )
            );
        });
    }

    fn set_topic(&mut self, topic: &str, ts: Tm, serv: &str, chan: &ChanNameRef) {
        let target = MsgTarget::Chan { serv, chan };
        self.apply_to_target(&target, |fd: &mut File, report_err: &dyn Fn(String)| {
            report_io_err!(
                report_err,
                writeln!(fd, "[{}] Channel topic: {}.", strf(&ts), topic)
            );
        });
    }

    fn apply_to_target(&mut self, target: &MsgTarget, f: impl Fn(&mut File, &dyn Fn(String))) {
        match *target {
            MsgTarget::Server { serv } => match self.servers.get_mut(serv) {
                None => {
                    info!("Can't find server: {:?}", serv);
                }
                Some(ServerLogs { ref mut fd, .. }) => {
                    f(fd, &*self.report_err);
                }
            },
            MsgTarget::Chan { serv, chan } => match self.servers.get_mut(serv) {
                None => {
                    info!("Can't find server: {:?}", serv);
                }
                Some(ServerLogs { ref mut chans, .. }) => match chans.get_mut(chan) {
                    None => {
                        // Create a file for the channel. FIXME Code copied from new_chan_tab:
                        // can't reuse it because of borrowchk issues.
                        let mut path = self.log_dir.clone();
                        let chan_name_normalized = chan.normalized();
                        path.push(&format!(
                            "{}_{}.txt",
                            serv,
                            replace_forward_slash(&chan_name_normalized)
                        ));
                        if let Some(mut fd) = try_open_log_file(&path, &*self.report_err) {
                            report_io_err!(self.report_err, print_header(&mut fd));
                            f(&mut fd, &*self.report_err);
                            chans.insert(ChanName::new(chan_name_normalized), fd);
                        }
                    }
                    Some(fd) => {
                        f(fd, &*self.report_err);
                    }
                },
            },
            MsgTarget::User { serv, nick } => {
                match self.servers.get_mut(serv) {
                    None => {
                        info!("Can't find server: {:?}", serv);
                    }
                    Some(ServerLogs { ref mut users, .. }) => {
                        match users.get_mut(nick) {
                            Some(fd) => {
                                f(fd, &*self.report_err);
                            }
                            None => {
                                // We don't have a `new_user_tab` trait method so user log files
                                // are created here
                                let mut path = self.log_dir.clone();
                                path.push(&format!("{}_{}.txt", serv, replace_forward_slash(nick)));
                                if let Some(mut fd) = try_open_log_file(&path, &*self.report_err) {
                                    report_io_err!(self.report_err, print_header(&mut fd));
                                    f(&mut fd, &*self.report_err);
                                    users.insert(nick.to_owned(), fd);
                                }
                            }
                        }
                    }
                }
            }
            MsgTarget::AllServTabs { serv } => match self.servers.get_mut(serv) {
                None => {
                    info!("Can't find server: {:?}", serv);
                }
                Some(ServerLogs {
                    ref mut fd,
                    ref mut chans,
                    ref mut users,
                    ..
                }) => {
                    f(fd, &*self.report_err);
                    for (_, fd) in chans.iter_mut() {
                        f(fd, &*self.report_err);
                    }
                    for (_, fd) in users.iter_mut() {
                        f(fd, &*self.report_err);
                    }
                }
            },
            MsgTarget::CurrentTab => {
                // Probably a cmd error; these are ignored
            }
        }
    }
}

fn now() -> String {
    time::strftime("%H:%M:%S", &time::now()).unwrap()
}

fn strf(tm: &Tm) -> String {
    time::strftime("%H:%M:%S", tm).unwrap()
}
<|endoftext|>