<commit_before>use rocksdb::{ColumnFamilyDescriptor, DBRecoveryMode, IteratorMode, Options, DB};
use std::{collections::HashMap, convert::TryInto, io::Cursor, mem, path::Path, sync::Arc};

use crate::{
    asset::Asset,
    constants::TX_EXPIRY_TIME,
    crypto::ScriptHash,
    serializer::*,
    tx::{OwnerTx, TxId, TxVariant},
    util,
};

const CF_BLOCK_BYTE_POS: &str = "block_byte_pos";
const CF_ADDR_BAL: &str = "address_balance";
const CF_TX_EXPIRY: &str = "tx_expiry";

const KEY_NET_OWNER: &[u8] = b"network_owner";
const KEY_CHAIN_HEIGHT: &[u8] = b"chain_height";
const KEY_TOKEN_SUPPLY: &[u8] = b"token_supply";

pub struct Indexer {
    db: DB,
}

impl Indexer {
    pub fn new(path: &Path) -> Indexer {
        let mut db_opts = Options::default();
        db_opts.create_missing_column_families(true);
        db_opts.create_if_missing(true);
        db_opts.set_wal_recovery_mode(DBRecoveryMode::AbsoluteConsistency);

        let col_families = vec![
            ColumnFamilyDescriptor::new(CF_BLOCK_BYTE_POS, Options::default()),
            ColumnFamilyDescriptor::new(CF_ADDR_BAL, Options::default()),
            ColumnFamilyDescriptor::new(CF_TX_EXPIRY, Options::default()),
        ];
        let db = DB::open_cf_descriptors(&db_opts, path, col_families).unwrap();
        Indexer { db }
    }

    pub fn get_block_byte_pos(&self, height: u64) -> Option<u64> {
        let cf = self.db.cf_handle(CF_BLOCK_BYTE_POS).unwrap();
        let buf = self.db.get_pinned_cf(cf, height.to_be_bytes()).unwrap()?;

        Some(u64_from_buf!(buf))
    }

    pub fn get_chain_height(&self) -> u64 {
        match self.db.get_pinned(KEY_CHAIN_HEIGHT).unwrap() {
            Some(val) => u64_from_buf!(val),
            None => 0,
        }
    }

    pub fn get_owner(&self) -> Option<OwnerTx> {
        let tx_buf = self.db.get_pinned(KEY_NET_OWNER).unwrap()?;
        let cur = &mut Cursor::<&[u8]>::new(&tx_buf);
        let tx = TxVariant::deserialize(cur).unwrap();
        match tx {
            TxVariant::OwnerTx(owner) => Some(owner),
            _ => panic!("expected owner transaction"),
        }
    }

    pub fn get_balance(&self, hash: &ScriptHash) -> Option<Asset> {
        let cf = self.db.cf_handle(CF_ADDR_BAL).unwrap();
        let bal_buf = self.db.get_pinned_cf(cf, hash.as_ref()).unwrap()?;
        let cur = &mut Cursor::<&[u8]>::new(&bal_buf);
        let bal = cur.take_asset().unwrap();
        Some(bal)
    }

    pub fn get_token_supply(&self) -> Asset {
        let supply_buf = self.db.get_pinned(KEY_TOKEN_SUPPLY).unwrap();
        match supply_buf {
            Some(supply_buf) => {
                let cur = &mut Cursor::<&[u8]>::new(&supply_buf);
                cur.take_asset().unwrap()
            }
            None => Asset::default(),
        }
    }
}

pub struct WriteBatch {
    indexer: Arc<Indexer>,
    block_byte_pos: HashMap<u64, u64>,
    chain_height: Option<u64>,
    owner: Option<OwnerTx>,
    balances: HashMap<ScriptHash, Asset>,
    token_supply: Option<Asset>,
}

impl WriteBatch {
    pub fn new(indexer: Arc<Indexer>) -> Self {
        WriteBatch {
            indexer,
            block_byte_pos: HashMap::with_capacity(1),
            chain_height: None,
            owner: None,
            balances: HashMap::with_capacity(64),
            token_supply: None,
        }
    }

    pub fn commit(self) {
        let mut batch = rocksdb::WriteBatch::default();

        {
            let cf = self.indexer.db.cf_handle(CF_BLOCK_BYTE_POS).unwrap();
            for (height, pos) in self.block_byte_pos {
                let height = height.to_be_bytes();
                let pos = pos.to_be_bytes();
                batch.put_cf(cf, &height, &pos).unwrap();
            }
        }

        if let Some(height) = self.chain_height {
            batch.put(KEY_CHAIN_HEIGHT, height.to_be_bytes()).unwrap();
        }

        if let Some(owner) = self.owner {
            let val = {
                let mut vec = Vec::with_capacity(4096);
                TxVariant::OwnerTx(owner).serialize(&mut vec);
                vec
            };
            batch.put(KEY_NET_OWNER, &val).unwrap();
        }

        if let Some(token_supply) = self.token_supply {
            let val = {
                let mut buf = Vec::with_capacity(mem::size_of::<Asset>());
                buf.push_asset(token_supply);
                buf
            };
            batch.put(KEY_TOKEN_SUPPLY, &val).unwrap();
        }

        {
            let cf = self.indexer.db.cf_handle(CF_ADDR_BAL).unwrap();
            let mut buf = Vec::with_capacity(mem::size_of::<Asset>());
            for (addr, bal) in self.balances {
                buf.push_asset(bal);
                batch.put_cf(cf, addr.as_ref(), &buf).unwrap();
                buf.clear();
            }
        }

        self.indexer.db.write(batch).unwrap();
    }

    pub fn set_block_byte_pos(&mut self, height: u64, pos: u64) {
        self.block_byte_pos.insert(height, pos);
    }

    pub fn set_chain_height(&mut self, height: u64) {
        self.chain_height = Some(height);
    }

    pub fn set_owner(&mut self, owner: OwnerTx) {
        self.owner = Some(owner);
    }

    pub fn add_token_supply(&mut self, amount: Asset) {
        match self.token_supply.as_mut() {
            Some(token_supply) => {
                *token_supply = token_supply.add(amount).unwrap();
            }
            None => {
                let amt = self.indexer.get_token_supply().add(amount).unwrap();
                self.token_supply = Some(amt);
            }
        }
    }

    pub fn add_bal(&mut self, addr: &ScriptHash, amount: Asset) {
        match self.balances.get_mut(addr) {
            Some(bal) => {
                *bal = bal.add(amount).unwrap();
            }
            None => {
                let bal = self
                    .indexer
                    .get_balance(addr)
                    .unwrap_or_else(Default::default)
                    .add(amount)
                    .unwrap();
                self.balances.insert(addr.clone(), bal);
            }
        }
    }

    pub fn sub_bal(&mut self, addr: &ScriptHash, amount: Asset) {
        match self.balances.get_mut(addr) {
            Some(bal) => {
                *bal = bal.sub(amount).unwrap();
            }
            None => {
                let bal = self
                    .indexer
                    .get_balance(addr)
                    .unwrap_or_else(Default::default)
                    .sub(amount)
                    .unwrap();
                self.balances.insert(addr.clone(), bal);
            }
        }
    }
}

pub struct TxManager {
    indexer: Arc<Indexer>,
}

impl TxManager {
    pub fn new(indexer: Arc<Indexer>) -> Self {
        Self { indexer }
    }

    pub fn has(&self, id: &TxId) -> bool {
        let db = &self.indexer.db;
        let cf = db.cf_handle(CF_TX_EXPIRY).unwrap();
        self.indexer.db.get_cf(cf, id).unwrap().is_some()
    }

    pub fn insert(&self, id: &TxId, ts: u64) {
        let db = &self.indexer.db;
        let cf = db.cf_handle(CF_TX_EXPIRY).unwrap();
        db.put_cf(cf, id, ts.to_be_bytes()).unwrap();
    }

    pub fn purge_expired(&self) {
        let db = &self.indexer.db;
        let cf = db.cf_handle(CF_TX_EXPIRY).unwrap();
        let current_time = util::get_epoch_ms();

        let mut batch = rocksdb::WriteBatch::default();
        for (key, value) in db.iterator_cf(cf, IteratorMode::Start).unwrap() {
            let ts = u64::from_be_bytes(value.as_ref().try_into().unwrap());
            // We increase the expiry time by an extra second for extra assurance if system time
            // slightly adjusts.
            if ts < current_time - TX_EXPIRY_TIME - 1000 {
                batch.delete_cf(cf, key).unwrap();
            }
        }
        db.write(batch).unwrap();
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::crypto::Digest;
    use sodiumoxide::randombytes;
    use std::{env, fs, panic};

    #[test]
    fn test_get_block_pos() {
        run_test(|indexer| {
            let mut batch = WriteBatch::new(Arc::clone(&indexer));
            batch.set_block_byte_pos(1, 327);
            batch.commit();
            assert!(indexer.get_block_byte_pos(0).is_none());
            assert_eq!(indexer.get_block_byte_pos(1).unwrap(), 327);
        });
    }

    #[test]
    fn test_get_chain_height() {
        run_test(|indexer| {
            assert_eq!(indexer.get_chain_height(), 0);
            let mut batch = WriteBatch::new(Arc::clone(&indexer));
            batch.set_chain_height(42);
            batch.commit();
            assert_eq!(indexer.get_chain_height(), 42);
        });
    }

    #[test]
    fn test_tx_manager() {
        run_test(|indexer| {
            let id = TxId::from_digest(Digest::from_slice(&[0u8; 32]).unwrap());
            let ts = util::get_epoch_ms();
            let manager = TxManager::new(Arc::clone(&indexer));
            assert!(!manager.has(&id));
            manager.insert(&id, ts);
            assert!(manager.has(&id));

            let cf = indexer.db.cf_handle(CF_TX_EXPIRY).unwrap();
            indexer.db.delete_cf(cf, &id).unwrap();
            assert!(!manager.has(&id));

            manager.insert(&id, ts - TX_EXPIRY_TIME);
            manager.purge_expired();
            // The transaction has expired, but we give an additional second before purging it.
            assert!(manager.has(&id));

            let cf = indexer.db.cf_handle(CF_TX_EXPIRY).unwrap();
            indexer.db.delete_cf(cf, &id).unwrap();
            assert!(!manager.has(&id));
            manager.insert(&id, ts - TX_EXPIRY_TIME - 1000);
            assert!(manager.has(&id));
            manager.purge_expired();
            // Test that the expiry is completely over
            assert!(!manager.has(&id));
        });
    }

    fn run_test<F>(func: F)
    where
        F: FnOnce(Arc<Indexer>) -> () + panic::UnwindSafe,
    {
        let mut tmp_dir = env::temp_dir();
        {
            let mut s = String::from("godcoin_test_");
            let mut num: [u8; 8] = [0; 8];
            randombytes::randombytes_into(&mut num);
            s.push_str(&format!("{}", u64::from_be_bytes(num)));
            tmp_dir.push(s);
        }
        fs::create_dir(&tmp_dir).expect(&format!("Could not create temp dir {:?}", &tmp_dir));

        let result = panic::catch_unwind(|| {
            let indexer = Indexer::new(&tmp_dir);
            func(Arc::new(indexer));
        });

        fs::remove_dir_all(&tmp_dir).expect("Failed to rm dir");
        assert!(result.is_ok());
    }
}
<commit_msg>Fix potential racy condition in tx manager test<commit_after>use rocksdb::{ColumnFamilyDescriptor, DBRecoveryMode, IteratorMode, Options, DB};
use std::{collections::HashMap, convert::TryInto, io::Cursor, mem, path::Path, sync::Arc};

use crate::{
    asset::Asset,
    constants::TX_EXPIRY_TIME,
    crypto::ScriptHash,
    serializer::*,
    tx::{OwnerTx, TxId, TxVariant},
    util,
};

const CF_BLOCK_BYTE_POS: &str = "block_byte_pos";
const CF_ADDR_BAL: &str = "address_balance";
const CF_TX_EXPIRY: &str = "tx_expiry";

const KEY_NET_OWNER: &[u8] = b"network_owner";
const KEY_CHAIN_HEIGHT: &[u8] = b"chain_height";
const KEY_TOKEN_SUPPLY: &[u8] = b"token_supply";

pub struct Indexer {
    db: DB,
}

impl Indexer {
    pub fn new(path: &Path) -> Indexer {
        let mut db_opts = Options::default();
        db_opts.create_missing_column_families(true);
        db_opts.create_if_missing(true);
        db_opts.set_wal_recovery_mode(DBRecoveryMode::AbsoluteConsistency);

        let col_families = vec![
            ColumnFamilyDescriptor::new(CF_BLOCK_BYTE_POS, Options::default()),
            ColumnFamilyDescriptor::new(CF_ADDR_BAL, Options::default()),
            ColumnFamilyDescriptor::new(CF_TX_EXPIRY, Options::default()),
        ];
        let db = DB::open_cf_descriptors(&db_opts, path, col_families).unwrap();
        Indexer { db }
    }

    pub fn get_block_byte_pos(&self, height: u64) -> Option<u64> {
        let cf = self.db.cf_handle(CF_BLOCK_BYTE_POS).unwrap();
        let buf = self.db.get_pinned_cf(cf, height.to_be_bytes()).unwrap()?;

        Some(u64_from_buf!(buf))
    }

    pub fn get_chain_height(&self) -> u64 {
        match self.db.get_pinned(KEY_CHAIN_HEIGHT).unwrap() {
            Some(val) => u64_from_buf!(val),
            None => 0,
        }
    }

    pub fn get_owner(&self) -> Option<OwnerTx> {
        let tx_buf = self.db.get_pinned(KEY_NET_OWNER).unwrap()?;
        let cur = &mut Cursor::<&[u8]>::new(&tx_buf);
        let tx = TxVariant::deserialize(cur).unwrap();
        match tx {
            TxVariant::OwnerTx(owner) => Some(owner),
            _ => panic!("expected owner transaction"),
        }
    }

    pub fn get_balance(&self, hash: &ScriptHash) -> Option<Asset> {
        let cf = self.db.cf_handle(CF_ADDR_BAL).unwrap();
        let bal_buf = self.db.get_pinned_cf(cf, hash.as_ref()).unwrap()?;
        let cur = &mut Cursor::<&[u8]>::new(&bal_buf);
        let bal = cur.take_asset().unwrap();
        Some(bal)
    }

    pub fn get_token_supply(&self) -> Asset {
        let supply_buf = self.db.get_pinned(KEY_TOKEN_SUPPLY).unwrap();
        match supply_buf {
            Some(supply_buf) => {
                let cur = &mut Cursor::<&[u8]>::new(&supply_buf);
                cur.take_asset().unwrap()
            }
            None => Asset::default(),
        }
    }
}

pub struct WriteBatch {
    indexer: Arc<Indexer>,
    block_byte_pos: HashMap<u64, u64>,
    chain_height: Option<u64>,
    owner: Option<OwnerTx>,
    balances: HashMap<ScriptHash, Asset>,
    token_supply: Option<Asset>,
}

impl WriteBatch {
    pub fn new(indexer: Arc<Indexer>) -> Self {
        WriteBatch {
            indexer,
            block_byte_pos: HashMap::with_capacity(1),
            chain_height: None,
            owner: None,
            balances: HashMap::with_capacity(64),
            token_supply: None,
        }
    }

    pub fn commit(self) {
        let mut batch = rocksdb::WriteBatch::default();

        {
            let cf = self.indexer.db.cf_handle(CF_BLOCK_BYTE_POS).unwrap();
            for (height, pos) in self.block_byte_pos {
                let height = height.to_be_bytes();
                let pos = pos.to_be_bytes();
                batch.put_cf(cf, &height, &pos).unwrap();
            }
        }

        if let Some(height) = self.chain_height {
            batch.put(KEY_CHAIN_HEIGHT, height.to_be_bytes()).unwrap();
        }

        if let Some(owner) = self.owner {
            let val = {
                let mut vec = Vec::with_capacity(4096);
                TxVariant::OwnerTx(owner).serialize(&mut vec);
                vec
            };
            batch.put(KEY_NET_OWNER, &val).unwrap();
        }

        if let Some(token_supply) = self.token_supply {
            let val = {
                let mut buf = Vec::with_capacity(mem::size_of::<Asset>());
                buf.push_asset(token_supply);
                buf
            };
            batch.put(KEY_TOKEN_SUPPLY, &val).unwrap();
        }

        {
            let cf = self.indexer.db.cf_handle(CF_ADDR_BAL).unwrap();
            let mut buf = Vec::with_capacity(mem::size_of::<Asset>());
            for (addr, bal) in self.balances {
                buf.push_asset(bal);
                batch.put_cf(cf, addr.as_ref(), &buf).unwrap();
                buf.clear();
            }
        }

        self.indexer.db.write(batch).unwrap();
    }

    pub fn set_block_byte_pos(&mut self, height: u64, pos: u64) {
        self.block_byte_pos.insert(height, pos);
    }

    pub fn set_chain_height(&mut self, height: u64) {
        self.chain_height = Some(height);
    }

    pub fn set_owner(&mut self, owner: OwnerTx) {
        self.owner = Some(owner);
    }

    pub fn add_token_supply(&mut self, amount: Asset) {
        match self.token_supply.as_mut() {
            Some(token_supply) => {
                *token_supply = token_supply.add(amount).unwrap();
            }
            None => {
                let amt = self.indexer.get_token_supply().add(amount).unwrap();
                self.token_supply = Some(amt);
            }
        }
    }

    pub fn add_bal(&mut self, addr: &ScriptHash, amount: Asset) {
        match self.balances.get_mut(addr) {
            Some(bal) => {
                *bal = bal.add(amount).unwrap();
            }
            None => {
                let bal = self
                    .indexer
                    .get_balance(addr)
                    .unwrap_or_else(Default::default)
                    .add(amount)
                    .unwrap();
                self.balances.insert(addr.clone(), bal);
            }
        }
    }

    pub fn sub_bal(&mut self, addr: &ScriptHash, amount: Asset) {
        match self.balances.get_mut(addr) {
            Some(bal) => {
                *bal = bal.sub(amount).unwrap();
            }
            None => {
                let bal = self
                    .indexer
                    .get_balance(addr)
                    .unwrap_or_else(Default::default)
                    .sub(amount)
                    .unwrap();
                self.balances.insert(addr.clone(), bal);
            }
        }
    }
}

pub struct TxManager {
    indexer: Arc<Indexer>,
}

impl TxManager {
    pub fn new(indexer: Arc<Indexer>) -> Self {
        Self { indexer }
    }

    pub fn has(&self, id: &TxId) -> bool {
        let db = &self.indexer.db;
        let cf = db.cf_handle(CF_TX_EXPIRY).unwrap();
        self.indexer.db.get_cf(cf, id).unwrap().is_some()
    }

    pub fn insert(&self, id: &TxId, ts: u64) {
        let db = &self.indexer.db;
        let cf = db.cf_handle(CF_TX_EXPIRY).unwrap();
        db.put_cf(cf, id, ts.to_be_bytes()).unwrap();
    }

    pub fn purge_expired(&self) {
        let db = &self.indexer.db;
        let cf = db.cf_handle(CF_TX_EXPIRY).unwrap();
        let current_time = util::get_epoch_ms();

        let mut batch = rocksdb::WriteBatch::default();
        for (key, value) in db.iterator_cf(cf, IteratorMode::Start).unwrap() {
            let ts = u64::from_be_bytes(value.as_ref().try_into().unwrap());
            // We increase the expiry time by an extra second for extra assurance if system time
            // slightly adjusts.
            if ts < current_time - TX_EXPIRY_TIME - 1000 {
                batch.delete_cf(cf, key).unwrap();
            }
        }
        db.write(batch).unwrap();
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::crypto::Digest;
    use sodiumoxide::randombytes;
    use std::{env, fs, panic};

    #[test]
    fn test_get_block_pos() {
        run_test(|indexer| {
            let mut batch = WriteBatch::new(Arc::clone(&indexer));
            batch.set_block_byte_pos(1, 327);
            batch.commit();
            assert!(indexer.get_block_byte_pos(0).is_none());
            assert_eq!(indexer.get_block_byte_pos(1).unwrap(), 327);
        });
    }

    #[test]
    fn test_get_chain_height() {
        run_test(|indexer| {
            assert_eq!(indexer.get_chain_height(), 0);
            let mut batch = WriteBatch::new(Arc::clone(&indexer));
            batch.set_chain_height(42);
            batch.commit();
            assert_eq!(indexer.get_chain_height(), 42);
        });
    }

    #[test]
    fn test_tx_manager() {
        run_test(|indexer| {
            let id = TxId::from_digest(Digest::from_slice(&[0u8; 32]).unwrap());
            let ts = util::get_epoch_ms();
            let manager = TxManager::new(Arc::clone(&indexer));
            assert!(!manager.has(&id));
            manager.insert(&id, ts);
            assert!(manager.has(&id));

            let cf = indexer.db.cf_handle(CF_TX_EXPIRY).unwrap();
            indexer.db.delete_cf(cf, &id).unwrap();
            assert!(!manager.has(&id));

            manager.insert(&id, ts - TX_EXPIRY_TIME);
            manager.purge_expired();
            // The transaction has expired, but we give an additional second before purging it.
            assert!(manager.has(&id));

            let cf = indexer.db.cf_handle(CF_TX_EXPIRY).unwrap();
            indexer.db.delete_cf(cf, &id).unwrap();
            assert!(!manager.has(&id));
            manager.insert(&id, ts - TX_EXPIRY_TIME - 1100);
            assert!(manager.has(&id));
            manager.purge_expired();
            // Test that the expiry is completely over
            assert!(!manager.has(&id));
        });
    }

    fn run_test<F>(func: F)
    where
        F: FnOnce(Arc<Indexer>) -> () + panic::UnwindSafe,
    {
        let mut tmp_dir = env::temp_dir();
        {
            let mut s = String::from("godcoin_test_");
            let mut num: [u8; 8] = [0; 8];
            randombytes::randombytes_into(&mut num);
            s.push_str(&format!("{}", u64::from_be_bytes(num)));
            tmp_dir.push(s);
        }
        fs::create_dir(&tmp_dir).expect(&format!("Could not create temp dir {:?}", &tmp_dir));

        let result = panic::catch_unwind(|| {
            let indexer = Indexer::new(&tmp_dir);
            func(Arc::new(indexer));
        });

        fs::remove_dir_all(&tmp_dir).expect("Failed to rm dir");
        assert!(result.is_ok());
    }
}
<|endoftext|>


<commit_before>use prelude::*;

cfg_if! {
    if #[cfg(feature = "std")] {
        use std::fmt;
        use std::str;
    } else {
        use core::fmt;
        use core::str;
    }
}

impl fmt::Display for Uuid {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::LowerHex::fmt(self, f)
    }
}

impl fmt::Display for UuidVariant {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            UuidVariant::NCS => write!(f, "NCS"),
            UuidVariant::RFC4122 => write!(f, "RFC4122"),
            UuidVariant::Microsoft => write!(f, "Microsoft"),
            UuidVariant::Future => write!(f, "Future"),
        }
    }
}

impl fmt::LowerHex for Uuid {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        <super::Hyphenated as fmt::LowerHex>::fmt(&self.hyphenated(), f)
    }
}

impl fmt::UpperHex for Uuid {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        <super::Hyphenated as fmt::UpperHex>::fmt(&self.hyphenated(), f)
    }
}

impl str::FromStr for Uuid {
    type Err = super::ParseError;

    fn from_str(uuid_str: &str) -> Result<Uuid, super::ParseError> {
        Uuid::parse_str(uuid_str)
    }
}

impl Default for Uuid {
    fn default() -> Self {
        Uuid::nil()
    }
}

#[cfg(test)]
mod tests {
    use prelude::*;
    use test_util;

    macro_rules! check {
        ($buf:ident, $format:expr, $target:expr, $len:expr, $cond:expr) => {
            $buf.clear();
            write!($buf, $format, $target).unwrap();
            assert!($buf.len() == $len);
            assert!($buf.chars().all($cond), "{}", $buf);
        };
    }

    #[test]
    fn test_uuid_compare() {
        let uuid1 = test_util::new();
        let uuid2 = test_util::new2();

        assert_eq!(uuid1, uuid1);
        assert_eq!(uuid2, uuid2);

        assert_ne!(uuid1, uuid2);
        assert_ne!(uuid2, uuid1);
    }

    #[test]
    fn test_uuid_default() {
        let default_uuid = Uuid::default();
        let nil_uuid = Uuid::nil();

        assert_eq!(default_uuid, nil_uuid);
    }

    #[test]
    fn test_uuid_display() {
        use super::fmt::Write;

        let uuid = test_util::new();
        let s = uuid.to_string();
        let mut buffer = String::new();

        assert_eq!(s, uuid.hyphenated().to_string());

        check!(
            buffer,
            "{}",
            uuid,
            36,
            |c| c.is_lowercase() || c.is_digit(10) || c == '-'
        );
    }

    #[test]
    fn test_uuid_lowerhex() {
        use super::fmt::Write;

        let mut buffer = String::new();
        let uuid = test_util::new();

        check!(
            buffer,
            "{:x}",
            uuid,
            36,
            |c| c.is_lowercase() || c.is_digit(10) || c == '-'
        );
    }

    #[test]
    fn test_uuid_operator_eq() {
        let uuid1 = test_util::new();
        let uuid1_dup = uuid1.clone();
        let uuid2 = test_util::new2();

        assert!(uuid1 == uuid1);
        assert!(uuid1 == uuid1_dup);
        assert!(uuid1_dup == uuid1);

        assert!(uuid1 != uuid2);
        assert!(uuid2 != uuid1);
        assert!(uuid1_dup != uuid2);
        assert!(uuid2 != uuid1_dup);
    }

    #[test]
    fn test_uuid_to_string() {
        use super::fmt::Write;

        let uuid = test_util::new();
        let s = uuid.to_string();
        let mut buffer = String::new();

        assert_eq!(s.len(), 36);

        check!(buffer, "{}", s, 36, |c| c.is_lowercase() || c.is_digit(10)
            || c == '-');
    }

    #[test]
    fn test_uuid_upperhex() {
        use super::fmt::Write;

        let mut buffer = String::new();
        let uuid = test_util::new();

        check!(
            buffer,
            "{:X}",
            uuid,
            36,
            |c| c.is_uppercase() || c.is_digit(10) || c == '-'
        );
    }
}
<commit_msg>add missing prelude import<commit_after>use prelude::*;

cfg_if! {
    if #[cfg(feature = "std")] {
        use std::fmt;
        use std::str;
    } else {
        use core::fmt;
        use core::str;
    }
}

impl fmt::Display for Uuid {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::LowerHex::fmt(self, f)
    }
}

impl fmt::Display for UuidVariant {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            UuidVariant::NCS => write!(f, "NCS"),
            UuidVariant::RFC4122 => write!(f, "RFC4122"),
            UuidVariant::Microsoft => write!(f, "Microsoft"),
            UuidVariant::Future => write!(f, "Future"),
        }
    }
}

impl fmt::LowerHex for Uuid {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        <super::Hyphenated as fmt::LowerHex>::fmt(&self.hyphenated(), f)
    }
}

impl fmt::UpperHex for Uuid {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        <super::Hyphenated as fmt::UpperHex>::fmt(&self.hyphenated(), f)
    }
}

impl str::FromStr for Uuid {
    type Err = super::ParseError;

    fn from_str(uuid_str: &str) -> Result<Uuid, super::ParseError> {
        Uuid::parse_str(uuid_str)
    }
}

impl Default for Uuid {
    fn default() -> Self {
        Uuid::nil()
    }
}

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

    use std::prelude::*;
    use prelude::*;
    use test_util;

    macro_rules! check {
        ($buf:ident, $format:expr, $target:expr, $len:expr, $cond:expr) => {
            $buf.clear();
            write!($buf, $format, $target).unwrap();
            assert!($buf.len() == $len);
            assert!($buf.chars().all($cond), "{}", $buf);
        };
    }

    #[test]
    fn test_uuid_compare() {
        let uuid1 = test_util::new();
        let uuid2 = test_util::new2();

        assert_eq!(uuid1, uuid1);
        assert_eq!(uuid2, uuid2);

        assert_ne!(uuid1, uuid2);
        assert_ne!(uuid2, uuid1);
    }

    #[test]
    fn test_uuid_default() {
        let default_uuid = Uuid::default();
        let nil_uuid = Uuid::nil();

        assert_eq!(default_uuid, nil_uuid);
    }

    #[test]
    fn test_uuid_display() {
        use super::fmt::Write;

        let uuid = test_util::new();
        let s = uuid.to_string();
        let mut buffer = String::new();

        assert_eq!(s, uuid.hyphenated().to_string());

        check!(
            buffer,
            "{}",
            uuid,
            36,
            |c| c.is_lowercase() || c.is_digit(10) || c == '-'
        );
    }

    #[test]
    fn test_uuid_lowerhex() {
        use super::fmt::Write;

        let mut buffer = String::new();
        let uuid = test_util::new();

        check!(
            buffer,
            "{:x}",
            uuid,
            36,
            |c| c.is_lowercase() || c.is_digit(10) || c == '-'
        );
    }

    #[test]
    fn test_uuid_operator_eq() {
        let uuid1 = test_util::new();
        let uuid1_dup = uuid1.clone();
        let uuid2 = test_util::new2();

        assert!(uuid1 == uuid1);
        assert!(uuid1 == uuid1_dup);
        assert!(uuid1_dup == uuid1);

        assert!(uuid1 != uuid2);
        assert!(uuid2 != uuid1);
        assert!(uuid1_dup != uuid2);
        assert!(uuid2 != uuid1_dup);
    }

    #[test]
    fn test_uuid_to_string() {
        use super::fmt::Write;

        let uuid = test_util::new();
        let s = uuid.to_string();
        let mut buffer = String::new();

        assert_eq!(s.len(), 36);

        check!(buffer, "{}", s, 36, |c| c.is_lowercase() || c.is_digit(10)
            || c == '-');
    }

    #[test]
    fn test_uuid_upperhex() {
        use super::fmt::Write;

        let mut buffer = String::new();
        let uuid = test_util::new();

        check!(
            buffer,
            "{:X}",
            uuid,
            36,
            |c| c.is_uppercase() || c.is_digit(10) || c == '-'
        );
    }
}
<|endoftext|>


<commit_before>use geo::Point;
use std::cmp;

use codearea::CodeArea;

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

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

/// 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 separator 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() {
        if spos < SEPARATOR_POSITION {
            // Short codes cannot have padding
            return false;
        }
        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 lng = normalize_longitude(pt.lng());

    let trimmed_code_length = cmp::min(code_length, MAX_CODE_LENGTH);

    // 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(trimmed_code_length);
    }

    // Convert to integers.
    let mut lat_val =
        (((lat + LATITUDE_MAX) * LAT_INTEGER_MULTIPLIER as f64 * 1e6).round() / 1e6f64) as i64;
    let mut lng_val =
        (((lng + LONGITUDE_MAX) * LNG_INTEGER_MULTIPLIER as f64 * 1e6).round() / 1e6f64) as i64;

    // Compute the code digits. This largely ignores the requested length - it
    // generates either a 10 digit code, or a 15 digit code, and then truncates
    // it to the requested length.

    // Build up the code digits in reverse order.
    let mut rev_code = String::with_capacity(trimmed_code_length + 1);

    // First do the grid digits.
    if code_length > PAIR_CODE_LENGTH {
        for _i in 0..GRID_CODE_LENGTH {
            let lat_digit = lat_val % GRID_ROWS as i64;
            let lng_digit = lng_val % GRID_COLUMNS as i64;
            let ndx = (lat_digit * GRID_COLUMNS as i64 + lng_digit) as usize;
            rev_code.push(CODE_ALPHABET[ndx]);
            lat_val /= GRID_ROWS as i64;
            lng_val /= GRID_COLUMNS as i64;
        }
    } else {
        // Adjust latitude and longitude values to skip the grid digits.
        lat_val /= GRID_ROWS.pow(GRID_CODE_LENGTH as u32) as i64;
        lng_val /= GRID_COLUMNS.pow(GRID_CODE_LENGTH as u32) as i64;
    }
    // Compute the pair section of the code.
    for i in 0..PAIR_CODE_LENGTH / 2 {
        rev_code.push(CODE_ALPHABET[(lng_val % ENCODING_BASE as i64) as usize]);
        lng_val /= ENCODING_BASE as i64;
        rev_code.push(CODE_ALPHABET[(lat_val % ENCODING_BASE as i64) as usize]);
        lat_val /= ENCODING_BASE as i64;
        // If we are at the separator position, add the separator.
        if i == 0 {
            rev_code.push(SEPARATOR);
        }
    }
    let mut code: String;
    // If we need to pad the code, replace some of the digits.
    if code_length < SEPARATOR_POSITION {
        code = rev_code.chars().rev().take(code_length).collect();
        code.push_str(
            PADDING_CHAR_STR
                .repeat(SEPARATOR_POSITION - code_length)
                .as_str(),
        );
        code.push(SEPARATOR);
    } else {
        code = rev_code.chars().rev().take(code_length + 1).collect();
    }

    return 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 mut code = code
        .to_string()
        .replace(SEPARATOR, "")
        .replace(PADDING_CHAR_STR, "")
        .to_uppercase();
    if code.len() > MAX_CODE_LENGTH {
        code = code.chars().take(MAX_CODE_LENGTH).collect();
    }

    // Work out the values as integers and convert to floating point at the end.
    let mut lat: i64 = -90 * LAT_INTEGER_MULTIPLIER;
    let mut lng: i64 = -180 * LNG_INTEGER_MULTIPLIER;
    let mut lat_place_val: i64 = LAT_INTEGER_MULTIPLIER * ENCODING_BASE.pow(2) as i64;
    let mut lng_place_val: i64 = LNG_INTEGER_MULTIPLIER * ENCODING_BASE.pow(2) as i64;

    for (idx, chr) in code.chars().enumerate() {
        if idx < PAIR_CODE_LENGTH {
            if idx % 2 == 0 {
                lat_place_val /= ENCODING_BASE as i64;
                lat += lat_place_val * code_value(chr) as i64;
            } else {
                lng_place_val /= ENCODING_BASE as i64;
                lng += lng_place_val * code_value(chr) as i64;
            }
        } else {
            lat_place_val /= GRID_ROWS as i64;
            lng_place_val /= GRID_COLUMNS as i64;
            lat += lat_place_val * (code_value(chr) / GRID_COLUMNS) as i64;
            lng += lng_place_val * (code_value(chr) % GRID_COLUMNS) as i64;
        }
    }
    // Convert to floating point values.
    let lat_lo: f64 = lat as f64 / LAT_INTEGER_MULTIPLIER as f64;
    let lng_lo: f64 = lng as f64 / LNG_INTEGER_MULTIPLIER as f64;
    let lat_hi: f64 =
        (lat + lat_place_val) as f64 / (ENCODING_BASE.pow(3) * GRID_ROWS.pow(5)) as f64;
    let lng_hi: f64 =
        (lng + lng_place_val) as f64 / (ENCODING_BASE.pow(3) * GRID_COLUMNS.pow(5)) as f64;
    Ok(CodeArea::new(lat_lo, lng_lo, lat_hi, lng_hi, 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().to_uppercase());
        } 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;

    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,
    ))
}
<commit_msg>Remove unneeded return statement<commit_after>use geo::Point;
use std::cmp;

use codearea::CodeArea;

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

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

/// 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 separator 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() {
        if spos < SEPARATOR_POSITION {
            // Short codes cannot have padding
            return false;
        }
        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 lng = normalize_longitude(pt.lng());

    let trimmed_code_length = cmp::min(code_length, MAX_CODE_LENGTH);

    // 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(trimmed_code_length);
    }

    // Convert to integers.
    let mut lat_val =
        (((lat + LATITUDE_MAX) * LAT_INTEGER_MULTIPLIER as f64 * 1e6).round() / 1e6f64) as i64;
    let mut lng_val =
        (((lng + LONGITUDE_MAX) * LNG_INTEGER_MULTIPLIER as f64 * 1e6).round() / 1e6f64) as i64;

    // Compute the code digits. This largely ignores the requested length - it
    // generates either a 10 digit code, or a 15 digit code, and then truncates
    // it to the requested length.

    // Build up the code digits in reverse order.
    let mut rev_code = String::with_capacity(trimmed_code_length + 1);

    // First do the grid digits.
    if code_length > PAIR_CODE_LENGTH {
        for _i in 0..GRID_CODE_LENGTH {
            let lat_digit = lat_val % GRID_ROWS as i64;
            let lng_digit = lng_val % GRID_COLUMNS as i64;
            let ndx = (lat_digit * GRID_COLUMNS as i64 + lng_digit) as usize;
            rev_code.push(CODE_ALPHABET[ndx]);
            lat_val /= GRID_ROWS as i64;
            lng_val /= GRID_COLUMNS as i64;
        }
    } else {
        // Adjust latitude and longitude values to skip the grid digits.
        lat_val /= GRID_ROWS.pow(GRID_CODE_LENGTH as u32) as i64;
        lng_val /= GRID_COLUMNS.pow(GRID_CODE_LENGTH as u32) as i64;
    }
    // Compute the pair section of the code.
    for i in 0..PAIR_CODE_LENGTH / 2 {
        rev_code.push(CODE_ALPHABET[(lng_val % ENCODING_BASE as i64) as usize]);
        lng_val /= ENCODING_BASE as i64;
        rev_code.push(CODE_ALPHABET[(lat_val % ENCODING_BASE as i64) as usize]);
        lat_val /= ENCODING_BASE as i64;
        // If we are at the separator position, add the separator.
        if i == 0 {
            rev_code.push(SEPARATOR);
        }
    }
    let mut code: String;
    // If we need to pad the code, replace some of the digits.
    if code_length < SEPARATOR_POSITION {
        code = rev_code.chars().rev().take(code_length).collect();
        code.push_str(
            PADDING_CHAR_STR
                .repeat(SEPARATOR_POSITION - code_length)
                .as_str(),
        );
        code.push(SEPARATOR);
    } else {
        code = rev_code.chars().rev().take(code_length + 1).collect();
    }

    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 mut code = code
        .to_string()
        .replace(SEPARATOR, "")
        .replace(PADDING_CHAR_STR, "")
        .to_uppercase();
    if code.len() > MAX_CODE_LENGTH {
        code = code.chars().take(MAX_CODE_LENGTH).collect();
    }

    // Work out the values as integers and convert to floating point at the end.
    let mut lat: i64 = -90 * LAT_INTEGER_MULTIPLIER;
    let mut lng: i64 = -180 * LNG_INTEGER_MULTIPLIER;
    let mut lat_place_val: i64 = LAT_INTEGER_MULTIPLIER * ENCODING_BASE.pow(2) as i64;
    let mut lng_place_val: i64 = LNG_INTEGER_MULTIPLIER * ENCODING_BASE.pow(2) as i64;

    for (idx, chr) in code.chars().enumerate() {
        if idx < PAIR_CODE_LENGTH {
            if idx % 2 == 0 {
                lat_place_val /= ENCODING_BASE as i64;
                lat += lat_place_val * code_value(chr) as i64;
            } else {
                lng_place_val /= ENCODING_BASE as i64;
                lng += lng_place_val * code_value(chr) as i64;
            }
        } else {
            lat_place_val /= GRID_ROWS as i64;
            lng_place_val /= GRID_COLUMNS as i64;
            lat += lat_place_val * (code_value(chr) / GRID_COLUMNS) as i64;
            lng += lng_place_val * (code_value(chr) % GRID_COLUMNS) as i64;
        }
    }
    // Convert to floating point values.
    let lat_lo: f64 = lat as f64 / LAT_INTEGER_MULTIPLIER as f64;
    let lng_lo: f64 = lng as f64 / LNG_INTEGER_MULTIPLIER as f64;
    let lat_hi: f64 =
        (lat + lat_place_val) as f64 / (ENCODING_BASE.pow(3) * GRID_ROWS.pow(5)) as f64;
    let lng_hi: f64 =
        (lng + lng_place_val) as f64 / (ENCODING_BASE.pow(3) * GRID_COLUMNS.pow(5)) as f64;
    Ok(CodeArea::new(lat_lo, lng_lo, lat_hi, lng_hi, 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().to_uppercase());
        } 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;

    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>
use std::cmp::PartialEq;

//

pub fn append_vectors_once<'a, T : PartialEq + Copy, F : FnMut(T)>( dst : &'a mut Vec<T>, src : &'a Vec<Vec<T>>, on_evaluate : Option<F> ) -> &'a mut Vec<T>
{
  if on_evaluate.is_none()
  {
    for vec in src
    {
      for el in vec
      {
        if !dst.contains( el )
        {
          dst.push( *el );
        }
      }
    }
  }
  else
  {
    unimplemented!( "callbacks is not implemented" );
  }

  dst
}

//

pub fn left_index<T : PartialEq + Copy, F : FnMut(T)>( src : &Vec<T>, el : T, on_evaluate1 : Option<F> ) -> Option<usize>
{
  if on_evaluate1.is_none()
  {
    return src.iter().position( | &x | x == el );
  }
  else
  {
    unimplemented!( "not implemented for callbacks" );
  }
}

<commit_msg>Extend routine `left_index` by implementation for equalizer<commit_after>
use std::cmp::PartialEq;

//

pub fn append_vectors_once<'a, T : PartialEq + Copy, F : FnMut(T)>( dst : &'a mut Vec<T>, src : &'a Vec<Vec<T>>, on_evaluate : Option<F> ) -> &'a mut Vec<T>
{
  if on_evaluate.is_none()
  {
    for vec in src
    {
      for el in vec
      {
        if !dst.contains( el )
        {
          dst.push( *el );
        }
      }
    }
  }
  else
  {
    unimplemented!( "callbacks is not implemented" );
  }

  dst
}

//

pub fn left_index<T : PartialEq + Copy, F : FnMut(T, T) -> bool>( src : &Vec<T>, el : T, on_equalize : Option<F> ) -> Option<usize>
{
  if on_equalize.is_none()
  {
    return src.iter().position( | &x | x == el );
  }
  else
  {
    let mut equalizer = on_equalize.unwrap();
    for x in 0..src.len()
    {
      if equalizer( src[ x ], el )
      {
        return Some( x );
      }
    }
    return None
    // unimplemented!( "not implemented for callbacks" );
  }
}

<|endoftext|>


<commit_before>use value::{Value};
use std::str::FromStr;
use std::f64;

#[derive(Clone, Debug, Copy)]
pub enum Primitive {
    LT,
    LTE,
    NEQ,
    Add,
    Subtract,
    Multiply,
    Round,
    Split,
    Concat,
    ParseFloat,
    Count,
    Contains,
    Sum,
    Mean,
    StandardDeviation,
    Empty,
}

pub fn resolve_as_scalar<'a>(mut ix: usize, constants: &'a [Value], outer: &'a [Value]) -> &'a Value {
    if ix < constants.len() {
        return &constants[ix];
    } else {
        ix = ix - constants.len()
    }
    return &outer[ix];
}

pub fn resolve_as_vector<'a>(mut ix: usize, constants: &'a [Value], outer: &'a [Value], inner: &'a [Vec<Value>]) -> Vec<&'a Value> {
    if ix < constants.len() {
        return vec![&constants[ix]; inner.len()];
    } else {
        ix = ix - constants.len()
    }
    if ix < outer.len() {
        return vec![&outer[ix]; inner.len()];
    } else {
        ix = ix - outer.len()
    }
    return inner.iter().map(|values| &values[ix]).collect();
}

impl Primitive {
    pub fn eval_from_join<'a>(&self, input_bindings: &[(usize, usize)], inputs: &[Value]) -> Vec<Vec<Value>> {
        use primitive::Primitive::*;
        use value::Value::*;
        let values = input_bindings.iter().enumerate().map(|(ix, &(field_ix, variable_ix))| {
            assert_eq!(ix, field_ix);
            &inputs[variable_ix]
        }).collect::<Vec<_>>();
        match (*self, &values[..]) {
            // NOTE be aware that arguments will be in alphabetical order by field id
            (LT, [ref a, ref b]) => if a < b {vec![vec![]]} else {vec![]},
            (LTE, [ref a, ref b]) => if a <= b {vec![vec![]]} else {vec![]},
            (NEQ, [ref a, ref b]) => if a != b {vec![vec![]]} else {vec![]},
            (Add, [&Float(a), &Float(b)]) => vec![vec![Float(a+b)]],
            (Subtract, [&Float(a), &Float(b)]) => vec![vec![Float(a-b)]],
            (Multiply, [&Float(a), &Float(b)]) => vec![vec![Float(a*b)]],
            (Round, [&Float(a), &Float(b)]) => vec![vec![Float((a*10f64.powf(b)).round()/10f64.powf(b))]],
            (Contains, [&String(ref inner), &String(ref outer)]) => {
              let inner_lower = &inner.to_lowercase();
              let outer_lower = &outer.to_lowercase();
              vec![vec![Bool(outer_lower.contains(inner_lower))]]
            },
            (Split, [&String(ref split), &String(ref string)]) => {
                string.split(split).enumerate().map(|(ix, segment)| vec![Float(ix as f64), String(segment.to_owned())]).collect()
            },
            (Concat, [&String(ref a), &String(ref b)]) => vec![vec![String(a.to_owned() + b)]],
            (Concat, [&String(ref a), &Float(ref b)]) => vec![vec![String(a.to_owned() + &format!("{}", b))]],
            (ParseFloat, [&String(ref a)]) => {
                match f64::from_str(&a) {
                    Ok(v) => vec![vec![Float(v), Bool(true)]],
                    _ => vec![vec![Float(f64::MAX), Bool(false)]]
                }
            },
            (Count, [&Column(ref column)]) => vec![vec![Float(column.len() as f64)]],
            (Sum, [&Column(ref column)]) => {
                let sum = column.iter().fold(0f64, |sum, value|
                    match *value {
                        Float(float) => sum + float,
                        _ => panic!("Type error while calling: {:?} {:?}", self, column),
                    });
                vec![vec![Float(sum)]]
            }
            (Mean, [&Column(ref column)]) => {
                let sum = column.iter().fold(0f64, |sum, value|
                    match *value {
                        Float(float) => sum + float,
                        _ => panic!("Type error while calling: {:?} {:?}", self, column),
                    });
                let mean = sum / (column.len() as f64);
                vec![vec![Float(mean)]]
            },
            (StandardDeviation, [&Column(ref column)]) => {
                let sum = column.iter().fold(0f64, |sum, value|
                    match *value {
                        Float(float) => sum + float,
                        _ => panic!("Type error while calling: {:?} {:?}", self, column),
                    });
                let sum_squares = column.iter().fold(0f64, |sum, value|
                    match *value {
                        Float(float) => sum + float.powi(2),
                        _ => panic!("Type error while calling: {:?} {:?}", self, column),
                    });
                let standard_deviation = ((sum_squares - sum.powi(2)) / (column.len() as f64)).sqrt();
                vec![vec![Float(standard_deviation)]]
            }
            (Empty, [&Column(ref column)]) => vec![vec![Bool(column.len() == 0)]],
            _ => panic!("Type error while calling: {:?} {:?}", self, values)
        }
    }

    pub fn eval_from_aggregate<'a>(&self, arguments: &[usize], constants: &[Value], outer: &[Value], inner: &[Vec<Value>]) -> Vec<Vec<Value>> {
        use primitive::Primitive::*;
        use value::Value::*;
        match (*self, arguments) {
            // NOTE be aware that arguments will be in alphabetical order by field id
            (LT, _) => panic!("Cannot use {:?} in an aggregate", self),
            (LTE, _) => panic!("Cannot use {:?} in an aggregate", self),
            (NEQ, _) => panic!("Cannot use {:?} in an aggregate", self),
            (Add, _) => panic!("Cannot use {:?} in an aggregate", self),
            (Subtract, _) => panic!("Cannot use {:?} in an aggregate", self),
            (Multiply, _) => panic!("Cannot use {:?} in an aggregate", self),
            (Round, _) => panic!("Cannot use {:?} in an aggregate", self),
            (Contains, _) => panic!("Cannot use {:?} in an aggregate", self),
            (Split, _) => panic!("Cannot use {:?} in an aggregate", self),
            (Concat, _) => panic!("Cannot use {:?} in an aggregate", self),
            (ParseFloat, _) => panic!("Cannot use {:?} in an aggregate", self),
            (Count, [_]) => {
                vec![vec![Float(inner.len() as f64)]]
            },
            (Sum, [in_ix]) => {
                let in_values = resolve_as_vector(in_ix, constants, outer, inner);
                let sum = in_values.iter().fold(0f64, |sum, value|
                    match **value {
                        Float(float) => sum + float,
                        _ => panic!("Type error while calling: {:?} {:?}", self, in_values),
                    });
                vec![vec![Float(sum)]]
            },
            (Mean, [in_ix]) => {
                let in_values = resolve_as_vector(in_ix, constants, outer, inner);
                let sum = in_values.iter().fold(0f64, |sum, value|
                    match **value {
                        Float(float) => sum + float,
                        _ => panic!("Type error while calling: {:?} {:?}", self, in_values),
                    });
                let mean = sum / (in_values.len() as f64);
                vec![vec![Float(mean)]]
            },
            (StandardDeviation, [in_ix]) => {
                let in_values = resolve_as_vector(in_ix, constants, outer, inner);
                let sum = in_values.iter().fold(0f64, |sum, value|
                    match **value {
                        Float(float) => sum + float,
                        _ => panic!("Type error while calling: {:?} {:?}", self, in_values),
                    });
                let sum_squares = in_values.iter().fold(0f64, |sum, value|
                    match **value {
                        Float(float) => sum + float.powi(2),
                        _ => panic!("Type error while calling: {:?} {:?}", self, in_values),
                    });
                let standard_deviation = ((sum_squares - sum.powi(2)) / (in_values.len() as f64)).sqrt();
                vec![vec![Float(standard_deviation)]]
            },
            (Empty, [in_ix]) => {
                let in_values = resolve_as_vector(in_ix, constants, outer, inner);
                vec![vec![Bool(in_values.len() == 0)]]
            },
            _ => panic!("Wrong number of arguments while calling: {:?} {:?}", self, arguments),
        }
    }

    pub fn from_str(string: &str) -> Self {
        match string {
            "<" => Primitive::LT,
            "<=" => Primitive::LTE,
            "!=" => Primitive::NEQ,
            "add" => Primitive::Add,
            "subtract" => Primitive::Subtract,
            "multiply" => Primitive::Multiply,
            "round" => Primitive::Round,
            "contains" => Primitive::Contains,
            "split" => Primitive::Split,
            "concat" => Primitive::Concat,
            "parse float" => Primitive::ParseFloat,
            "count" => Primitive::Count,
            "sum" => Primitive::Sum,
            "mean" => Primitive::Mean,
            "standard deviation" => Primitive::StandardDeviation,
            "empty" => Primitive::Empty,
            _ => panic!("Unknown primitive: {:?}", string),
        }
    }
}

pub fn primitives() -> Vec<(&'static str, Vec<&'static str>, Vec<&'static str>, Vec<&'static str>)> {
    vec![
        ("<", vec!["in A", "in B"], vec![], vec![]),
        ("<=", vec!["in A", "in B"], vec![], vec![]),
        ("!=", vec!["in A", "in B"], vec![], vec![]),
        ("add", vec!["in A", "in B"], vec![], vec!["out"]),
        ("subtract", vec!["in A", "in B"], vec![], vec!["out"]),
        ("multiply", vec!["in A", "in B"], vec![], vec!["out"]),
        ("round", vec!["in A", "in B"], vec![], vec!["out"]),
        ("contains", vec!["inner", "outer"], vec![], vec!["out"]),
        ("split", vec!["split", "string"], vec![], vec!["ix", "segment"]),
        ("concat", vec!["a", "b"], vec![], vec!["out"]),
        ("parse float", vec!["a"], vec![], vec!["out", "valid"]),
        ("count", vec![], vec!["in"], vec!["out"]),
        ("sum", vec![], vec!["in"], vec!["out"]),
        ("mean", vec![], vec!["in"], vec!["out"]),
        ("standard deviation", vec![], vec!["in"], vec!["out"]),
        ("empty", vec![], vec!["in"], vec!["out"]),
    ]
}
<commit_msg>Also concat Float then String<commit_after>use value::{Value};
use std::str::FromStr;
use std::f64;

#[derive(Clone, Debug, Copy)]
pub enum Primitive {
    LT,
    LTE,
    NEQ,
    Add,
    Subtract,
    Multiply,
    Round,
    Split,
    Concat,
    ParseFloat,
    Count,
    Contains,
    Sum,
    Mean,
    StandardDeviation,
    Empty,
}

pub fn resolve_as_scalar<'a>(mut ix: usize, constants: &'a [Value], outer: &'a [Value]) -> &'a Value {
    if ix < constants.len() {
        return &constants[ix];
    } else {
        ix = ix - constants.len()
    }
    return &outer[ix];
}

pub fn resolve_as_vector<'a>(mut ix: usize, constants: &'a [Value], outer: &'a [Value], inner: &'a [Vec<Value>]) -> Vec<&'a Value> {
    if ix < constants.len() {
        return vec![&constants[ix]; inner.len()];
    } else {
        ix = ix - constants.len()
    }
    if ix < outer.len() {
        return vec![&outer[ix]; inner.len()];
    } else {
        ix = ix - outer.len()
    }
    return inner.iter().map(|values| &values[ix]).collect();
}

impl Primitive {
    pub fn eval_from_join<'a>(&self, input_bindings: &[(usize, usize)], inputs: &[Value]) -> Vec<Vec<Value>> {
        use primitive::Primitive::*;
        use value::Value::*;
        let values = input_bindings.iter().enumerate().map(|(ix, &(field_ix, variable_ix))| {
            assert_eq!(ix, field_ix);
            &inputs[variable_ix]
        }).collect::<Vec<_>>();
        match (*self, &values[..]) {
            // NOTE be aware that arguments will be in alphabetical order by field id
            (LT, [ref a, ref b]) => if a < b {vec![vec![]]} else {vec![]},
            (LTE, [ref a, ref b]) => if a <= b {vec![vec![]]} else {vec![]},
            (NEQ, [ref a, ref b]) => if a != b {vec![vec![]]} else {vec![]},
            (Add, [&Float(a), &Float(b)]) => vec![vec![Float(a+b)]],
            (Subtract, [&Float(a), &Float(b)]) => vec![vec![Float(a-b)]],
            (Multiply, [&Float(a), &Float(b)]) => vec![vec![Float(a*b)]],
            (Round, [&Float(a), &Float(b)]) => vec![vec![Float((a*10f64.powf(b)).round()/10f64.powf(b))]],
            (Contains, [&String(ref inner), &String(ref outer)]) => {
              let inner_lower = &inner.to_lowercase();
              let outer_lower = &outer.to_lowercase();
              vec![vec![Bool(outer_lower.contains(inner_lower))]]
            },
            (Split, [&String(ref split), &String(ref string)]) => {
                string.split(split).enumerate().map(|(ix, segment)| vec![Float(ix as f64), String(segment.to_owned())]).collect()
            },
            (Concat, [&String(ref a), &String(ref b)]) => vec![vec![String(a.to_owned() + b)]],
            (Concat, [&String(ref a), &Float(ref b)]) => vec![vec![String(a.to_owned() + &format!("{}", b))]],
            (Concat, [&Float(ref a), &String(ref b)]) => vec![vec![String(format!("{}", a) + b)]],
            (ParseFloat, [&String(ref a)]) => {
                match f64::from_str(&a) {
                    Ok(v) => vec![vec![Float(v), Bool(true)]],
                    _ => vec![vec![Float(f64::MAX), Bool(false)]]
                }
            },
            (Count, [&Column(ref column)]) => vec![vec![Float(column.len() as f64)]],
            (Sum, [&Column(ref column)]) => {
                let sum = column.iter().fold(0f64, |sum, value|
                    match *value {
                        Float(float) => sum + float,
                        _ => panic!("Type error while calling: {:?} {:?}", self, column),
                    });
                vec![vec![Float(sum)]]
            }
            (Mean, [&Column(ref column)]) => {
                let sum = column.iter().fold(0f64, |sum, value|
                    match *value {
                        Float(float) => sum + float,
                        _ => panic!("Type error while calling: {:?} {:?}", self, column),
                    });
                let mean = sum / (column.len() as f64);
                vec![vec![Float(mean)]]
            },
            (StandardDeviation, [&Column(ref column)]) => {
                let sum = column.iter().fold(0f64, |sum, value|
                    match *value {
                        Float(float) => sum + float,
                        _ => panic!("Type error while calling: {:?} {:?}", self, column),
                    });
                let sum_squares = column.iter().fold(0f64, |sum, value|
                    match *value {
                        Float(float) => sum + float.powi(2),
                        _ => panic!("Type error while calling: {:?} {:?}", self, column),
                    });
                let standard_deviation = ((sum_squares - sum.powi(2)) / (column.len() as f64)).sqrt();
                vec![vec![Float(standard_deviation)]]
            }
            (Empty, [&Column(ref column)]) => vec![vec![Bool(column.len() == 0)]],
            _ => panic!("Type error while calling: {:?} {:?}", self, values)
        }
    }

    pub fn eval_from_aggregate<'a>(&self, arguments: &[usize], constants: &[Value], outer: &[Value], inner: &[Vec<Value>]) -> Vec<Vec<Value>> {
        use primitive::Primitive::*;
        use value::Value::*;
        match (*self, arguments) {
            // NOTE be aware that arguments will be in alphabetical order by field id
            (LT, _) => panic!("Cannot use {:?} in an aggregate", self),
            (LTE, _) => panic!("Cannot use {:?} in an aggregate", self),
            (NEQ, _) => panic!("Cannot use {:?} in an aggregate", self),
            (Add, _) => panic!("Cannot use {:?} in an aggregate", self),
            (Subtract, _) => panic!("Cannot use {:?} in an aggregate", self),
            (Multiply, _) => panic!("Cannot use {:?} in an aggregate", self),
            (Round, _) => panic!("Cannot use {:?} in an aggregate", self),
            (Contains, _) => panic!("Cannot use {:?} in an aggregate", self),
            (Split, _) => panic!("Cannot use {:?} in an aggregate", self),
            (Concat, _) => panic!("Cannot use {:?} in an aggregate", self),
            (ParseFloat, _) => panic!("Cannot use {:?} in an aggregate", self),
            (Count, [_]) => {
                vec![vec![Float(inner.len() as f64)]]
            },
            (Sum, [in_ix]) => {
                let in_values = resolve_as_vector(in_ix, constants, outer, inner);
                let sum = in_values.iter().fold(0f64, |sum, value|
                    match **value {
                        Float(float) => sum + float,
                        _ => panic!("Type error while calling: {:?} {:?}", self, in_values),
                    });
                vec![vec![Float(sum)]]
            },
            (Mean, [in_ix]) => {
                let in_values = resolve_as_vector(in_ix, constants, outer, inner);
                let sum = in_values.iter().fold(0f64, |sum, value|
                    match **value {
                        Float(float) => sum + float,
                        _ => panic!("Type error while calling: {:?} {:?}", self, in_values),
                    });
                let mean = sum / (in_values.len() as f64);
                vec![vec![Float(mean)]]
            },
            (StandardDeviation, [in_ix]) => {
                let in_values = resolve_as_vector(in_ix, constants, outer, inner);
                let sum = in_values.iter().fold(0f64, |sum, value|
                    match **value {
                        Float(float) => sum + float,
                        _ => panic!("Type error while calling: {:?} {:?}", self, in_values),
                    });
                let sum_squares = in_values.iter().fold(0f64, |sum, value|
                    match **value {
                        Float(float) => sum + float.powi(2),
                        _ => panic!("Type error while calling: {:?} {:?}", self, in_values),
                    });
                let standard_deviation = ((sum_squares - sum.powi(2)) / (in_values.len() as f64)).sqrt();
                vec![vec![Float(standard_deviation)]]
            },
            (Empty, [in_ix]) => {
                let in_values = resolve_as_vector(in_ix, constants, outer, inner);
                vec![vec![Bool(in_values.len() == 0)]]
            },
            _ => panic!("Wrong number of arguments while calling: {:?} {:?}", self, arguments),
        }
    }

    pub fn from_str(string: &str) -> Self {
        match string {
            "<" => Primitive::LT,
            "<=" => Primitive::LTE,
            "!=" => Primitive::NEQ,
            "add" => Primitive::Add,
            "subtract" => Primitive::Subtract,
            "multiply" => Primitive::Multiply,
            "round" => Primitive::Round,
            "contains" => Primitive::Contains,
            "split" => Primitive::Split,
            "concat" => Primitive::Concat,
            "parse float" => Primitive::ParseFloat,
            "count" => Primitive::Count,
            "sum" => Primitive::Sum,
            "mean" => Primitive::Mean,
            "standard deviation" => Primitive::StandardDeviation,
            "empty" => Primitive::Empty,
            _ => panic!("Unknown primitive: {:?}", string),
        }
    }
}

pub fn primitives() -> Vec<(&'static str, Vec<&'static str>, Vec<&'static str>, Vec<&'static str>)> {
    vec![
        ("<", vec!["in A", "in B"], vec![], vec![]),
        ("<=", vec!["in A", "in B"], vec![], vec![]),
        ("!=", vec!["in A", "in B"], vec![], vec![]),
        ("add", vec!["in A", "in B"], vec![], vec!["out"]),
        ("subtract", vec!["in A", "in B"], vec![], vec!["out"]),
        ("multiply", vec!["in A", "in B"], vec![], vec!["out"]),
        ("round", vec!["in A", "in B"], vec![], vec!["out"]),
        ("contains", vec!["inner", "outer"], vec![], vec!["out"]),
        ("split", vec!["split", "string"], vec![], vec!["ix", "segment"]),
        ("concat", vec!["a", "b"], vec![], vec!["out"]),
        ("parse float", vec!["a"], vec![], vec!["out", "valid"]),
        ("count", vec![], vec!["in"], vec!["out"]),
        ("sum", vec![], vec!["in"], vec!["out"]),
        ("mean", vec![], vec!["in"], vec!["out"]),
        ("standard deviation", vec![], vec!["in"], vec!["out"]),
        ("empty", vec![], vec!["in"], vec!["out"]),
    ]
}
<|endoftext|>


<commit_before>extern crate regex;

#[cfg(test)]
mod tests;

use regex::Regex;
use std::io::{self, Read};
use std::collections::HashMap;
use std::net::{TcpListener, TcpStream};
use std::thread;

#[derive(PartialEq, Debug)]
enum Method {
    Get,
    Put,
    Post,
    Delete,
    NoImpl,
}

#[derive(Debug)]
struct Request {
    method:  Method,
    path:    String,
    headers: HashMap<String, String>,
    payload: String,
}

impl Request {
    fn new() -> Request {
        Request {
            method:  Method::NoImpl,
            path:    String::new(),
            headers: HashMap::new(),
            payload: String::new(),
        }
    }

    pub fn from_str(rqstr: &str) -> Request {
        let mut req = Request::new();
        req.parse(rqstr);
        req
    }

    pub fn parse(&mut self, rqstr: &str) {
        let mut chunks: Vec<&str> = rqstr.splitn(2, "\r\n\r\n").collect();
        let mut header: Vec<&str> = chunks.pop().unwrap().split("\r\n").collect();
        let ask: Vec<&str> = header.pop().unwrap().splitn(3, ' ').collect();

        self.method = match ask[0] {
            "GET"           => Method::Get,
            "PUT" | "PATCH" => Method::Put,
            "POST"          => Method::Post,
            "DELETE"        => Method::Delete,
            _               => Method::NoImpl,
        };
        self.path = String::from(ask[1]);
        self.payload = match chunks.pop() {
            Some(x) => String::from(x),
            None    => String::new(),
        };

        for line in header {
            let hdr: Vec<&str> = line.splitn(2, ": ").collect();

            if hdr.len() == 2 {
                self.headers.insert(String::from(hdr[0]), String::from(hdr[1]));
            }
        }
    }
}

#[derive(Debug)]
pub struct Route {
    pathdef: String,
    matcher: Regex,
    params:  HashMap<String, String>,
}

impl Route {
    fn new(path: &str) -> Route {
        let re = Regex::new(r"^<(?:(int|str):)?([\w_][a-zA-Z0-9_]*)>$").unwrap();
        let mut parts: Vec<&str> = path.split('/').filter(|&s| s != "").collect();
        let mut matcher: String = String::from(r"^");
        let mut params: HashMap<String, String> = HashMap::new();

        for part in parts {
            let chunk: String = match re.is_match(part) {
                true  => {
                    let mut rc = String::new();
                    let caps = re.captures(part).unwrap();
                    let param = caps.at(2).unwrap().clone();
                    let ptype: String = match caps.at(1) {
                        Some(x)     => String::from(x),
                        None        => String::from("str"),
                    };

                    let mstr: String = match ptype.as_ref() {
                        "str" => String::from("(?:[^/])+"),
                        "int" => String::from("[0-9]+"),
                        _     => String::from("(?:[^/])+"),
                    };

                    rc.push_str("/(?P<");
                    rc.push_str(&param);
                    rc.push_str(">");
                    rc.push_str(&mstr);
                    rc.push_str(")");
                    params.insert(String::from(param), mstr.clone());

                    rc
                },
                false => String::from("/") + part,
            };

            matcher.push_str(&chunk);
        }

        /* end the regex with an optional final slash and a string terminator */
        matcher.push_str("/?$");

        Route {
            pathdef: String::from(path),
            matcher: Regex::new(&matcher).unwrap(),
            params:  params,
        }
    }

    fn is_match(&self, path: &str) -> bool {
        self.matcher.is_match(&path)
    }

    fn parse(&self, path: &str) -> Option<HashMap<String, String>> {
        let mut params: HashMap<String, String> = HashMap::new();

        return match self.matcher.is_match(&path) {
            true  => {
                let caps = self.matcher.captures(path).unwrap();
                for (param, _) in &self.params {
                    params.insert(param.clone(), String::from(caps.name(&param).unwrap()));
                }

                Some(params)
            },
            false => None,
        }
    }
}

fn handle_client(mut stream: TcpStream) {
    let mut rqstr: String = String::new();
    let mut buf: String = String::new();

    while stream.read_to_string(&mut buf).unwrap() > 0 {
        rqstr.push_str(&buf);
    }

    let req = Request::from_str(&rqstr);
}

fn main() {
    let rt = Route::new("/api/v1/foo/<foo_stuff>");
    assert_eq!("blahblahblah", rt.parse("/api/v1/foo/blahblahblah").unwrap().get("foo_stuff").unwrap());
    /*
    let listener = TcpListener::bind("127.0.0.1:8080").unwrap();

    for stream in listener.incoming() {
        match stream {
            Ok(stream)  => {
                thread::spawn(move || {
                    handle_client(stream)
                });
            },
            Err(e)      => {},
        }
    }

    drop(listener);
    */
}
<commit_msg>deal with a few warnings, make things a bit more idiomatic<commit_after>extern crate regex;

#[cfg(test)]
mod tests;

use regex::Regex;
use std::io::Read;
use std::collections::HashMap;
use std::net::{TcpListener, TcpStream};
use std::thread;

#[allow(dead_code)]
#[derive(PartialEq, Debug)]
enum Method {
    Get,
    Put,
    Post,
    Delete,
    NoImpl,
}

#[derive(Debug)]
struct Request {
    method:  Method,
    path:    String,
    headers: HashMap<String, String>,
    params:  Option<HashMap<String, String>>,
    payload: String,
}

impl Request {
    fn new() -> Request {
        Request {
            method:  Method::NoImpl,
            path:    String::new(),
            headers: HashMap::new(),
            params:  None,
            payload: String::new(),
        }
    }

    pub fn from_str(rqstr: &str) -> Request {
        let mut req = Request::new();
        req.parse(rqstr);
        req
    }

    pub fn parse(&mut self, rqstr: &str) {
        let mut chunks: Vec<&str> = rqstr.splitn(2, "\r\n\r\n").collect();
        let mut header: Vec<&str> = chunks.pop().unwrap().split("\r\n").collect();
        let ask: Vec<&str> = header.pop().unwrap().splitn(3, ' ').collect();

        self.method = match ask[0] {
            "GET"           => Method::Get,
            "PUT" | "PATCH" => Method::Put,
            "POST"          => Method::Post,
            "DELETE"        => Method::Delete,
            _               => Method::NoImpl,
        };
        self.path = String::from(ask[1]);
        self.payload = match chunks.pop() {
            Some(x) => String::from(x),
            None    => String::new(),
        };

        for line in header {
            let hdr: Vec<&str> = line.splitn(2, ": ").collect();

            if hdr.len() == 2 {
                self.headers.insert(String::from(hdr[0]), String::from(hdr[1]));
            }
        }
    }
}

#[derive(Debug)]
pub struct Route {
    pathdef: String,
    matcher: Regex,
    params:  HashMap<String, String>,
    handler: Option<fn(Request) -> String>,
}

impl Route {
    fn new(path: &str) -> Route {
        let re = Regex::new(r"^<(?:(int|str):)?([\w_][a-zA-Z0-9_]*)>$").unwrap();
        let parts: Vec<&str> = path.split('/').filter(|&s| s != "").collect();
        let mut matcher: String = String::from(r"^");
        let mut params: HashMap<String, String> = HashMap::new();

        for part in parts {
            let chunk: String = match re.is_match(part) {
                true  => {
                    let mut rc = String::new();
                    let caps = re.captures(part).unwrap();
                    let param = caps.at(2).unwrap().clone();
                    let ptype: String = match caps.at(1) {
                        Some(x)     => String::from(x),
                        None        => String::from("str"),
                    };

                    let mstr: String = match ptype.as_ref() {
                        "str" => String::from("(?:[^/])+"),
                        "int" => String::from("[0-9]+"),
                        _     => String::from("(?:[^/])+"),
                    };

                    rc.push_str("/(?P<");
                    rc.push_str(&param);
                    rc.push_str(">");
                    rc.push_str(&mstr);
                    rc.push_str(")");
                    params.insert(String::from(param), mstr.clone());

                    rc
                },
                false => String::from("/") + part,
            };

            matcher.push_str(&chunk);
        }

        /* end the regex with an optional final slash and a string terminator */
        matcher.push_str("/?$");

        Route {
            pathdef: String::from(path),
            matcher: Regex::new(&matcher).unwrap(),
            params:  params,
            handler: None,
        }
    }

    fn is_match(&self, path: &str) -> bool {
        self.matcher.is_match(&path)
    }

    fn parse(&self, path: &str) -> Option<HashMap<String, String>> {
        let mut params: HashMap<String, String> = HashMap::new();

        return match self.matcher.is_match(&path) {
            true  => {
                let caps = self.matcher.captures(path).unwrap();
                for (param, _) in &self.params {
                    params.insert(param.clone(), String::from(caps.name(&param).unwrap()));
                }

                Some(params)
            },
            false => None,
        }
    }
}

fn handle_client(mut stream: TcpStream) {
    let mut rqstr: String = String::new();
    let mut buf: String = String::new();

    while stream.read_to_string(&mut buf).unwrap() > 0 {
        rqstr.push_str(&buf);
    }

    let req = Request::from_str(&rqstr);
}

fn main() {
    let listener = TcpListener::bind("127.0.0.1:8080").unwrap();

    for stream in listener.incoming() {
        match stream {
            Ok(stream)  => {
                thread::spawn(move || {
                    handle_client(stream)
                });
            },
            Err(e)      => { println!("{}", e); },
        }
    }

    drop(listener);
}
<|endoftext|>