<commit_before>extern crate walkdir;
extern crate chrono;

use std::path;
use self::walkdir::{WalkDir, DirEntry, Error as WalkDirError};
use std::fs::File;
use std::io::BufReader;
use std::io::BufRead;
use self::chrono::{DateTime, UTC};
use std::error::Error;
use std::str::FromStr;
use std::net::SocketAddrV4;

#[derive(Debug)]
pub struct ELBRecord {
    timestamp: DateTime<UTC>,
    elb_name: String,
    client_address: SocketAddrV4,
    backend_address: SocketAddrV4,
    request_processing_time: f32,
    backend_processing_time: f32,
    response_processing_time: f32,
    elb_status_code: u16,
    backend_status_code: u16,
    received_bytes: u64,
    sent_bytes: u64,
    request_method: String,
    request_url: String,
    request_http_version: String
}

pub fn file_list(dir: &path::Path, filenames: &mut Vec<DirEntry>) -> Result<usize, WalkDirError> {
    for entry in WalkDir::new(dir).min_depth(1) {
        match entry {
            Err(err) => return Err(err),
            Ok(entry) => filenames.push(entry),
        }
    }
    Ok(filenames.len())
}

//TODO Reconsider logging based on the standard interfaces included with Rust.
//TODO We really want to accept a function to handle the parsed lines.
pub fn process_files(runtime_context: &::RuntimeContext, filenames: Vec<walkdir::DirEntry>) -> usize {
    let debug = runtime_context.debug;
    let mut record_count = 0;
    for filename in filenames {
        debug!(debug, "Processing file {}.", filename.path().display());
        match File::open(filename.path()) {
            Ok(file) => {
                let buffered_file = BufReader::new(&file);
                let recs: Vec<_> = buffered_file.lines()
                    .map(|possible_line| {
                        match possible_line {
                            Ok(record) => parse_record(record),

                            Err(_) => {
                                Err(ParsingErrors {
                                    record: "".to_string(),
                                    errors: vec![ELBRecordParsingErrors::LineReadError]
                                })
                            }
                        }
                    })
                    .collect();
                record_count += recs.len();
                debug!(debug, "Found {} records in file {}.", recs.len(), filename.path().display());
            },
            Err(e) => {
                println!("ERROR: {}", e);
            }
        }
    }

    record_count
}

//TODO Take a look at the error handling once again.  This doesn't feel write given code you've read.

#[derive(Debug)]
pub struct ParsingErrors {
    record: String,
    errors: Vec<ELBRecordParsingErrors>,
}

#[derive(Debug, PartialEq)]
enum ELBRecordParsingErrors {
    MalformedRecord,
    ParsingError { property: &'static str, description: String },
    LineReadError
}

const ELB_RECORD_FIELD_COUNT: usize = 14;
const TIMESTAMP: &'static str = "timestamp";
const CLIENT_ADDRESS: &'static str = "client address";
const BACKEND_ADDRESS: &'static str = "backend address";
const REQUEST_PROCESSING_TIME: &'static str = "request processing time";
const BACKEND_PROCESSING_TIME: &'static str = "backend processing time";
const RESPONSE_PROCESSING_TIME: &'static str = "response processing time";
const ELB_STATUS_CODE: &'static str = "ELB status code";
const BE_STATUS_CODE: &'static str = "backend status code";
const RECEIVED_BYTES: &'static str = "received bytes";
const SENT_BYTES: &'static str = "sent bytes";

pub fn parse_record(record: String) -> Result<Box<ELBRecord>, ParsingErrors> {
    let mut errors: Vec<ELBRecordParsingErrors> = Vec::new();

    {
        //record is borrowed by the split method which means ownership of record cannot be
        //transferred to ParsingErrors until the borrow is complete.
        //Scoping this section of code seems more readable than creating a separate function
        //just to mitigate the borrow.
        let split_line: Vec<&str> = record.split(' ').collect();
        if split_line.len() != ELB_RECORD_FIELD_COUNT {
            errors.push(ELBRecordParsingErrors::MalformedRecord);
            None
        } else {
            //TODO consider an enum representing the indices
            let ts = parse_property::<DateTime<UTC>>(split_line[0], TIMESTAMP, &mut errors);
            let clnt_addr = parse_property::<SocketAddrV4>(split_line[2], CLIENT_ADDRESS, &mut errors);
            let be_addr = parse_property::<SocketAddrV4>(split_line[3], BACKEND_ADDRESS, &mut errors);
            let req_proc_time = parse_property::<f32>(split_line[4], REQUEST_PROCESSING_TIME, &mut errors);
            let be_proc_time = parse_property::<f32>(split_line[5], BACKEND_PROCESSING_TIME, &mut errors);
            let res_proc_time = parse_property::<f32>(split_line[6], RESPONSE_PROCESSING_TIME, &mut errors);
            let elb_sc = parse_property::<u16>(split_line[7], ELB_STATUS_CODE, &mut errors);
            let be_sc = parse_property::<u16>(split_line[8], BE_STATUS_CODE, &mut errors);
            let bytes_received = parse_property::<u64>(split_line[9], RECEIVED_BYTES, &mut errors);
            let bytes_sent = parse_property::<u64>(split_line[10], SENT_BYTES, &mut errors);

            if errors.is_empty() {
                //If errors is empty it is more than likely parsing was successful and unwrap is safe.
                Some(
                    ELBRecord {
                        timestamp: ts.unwrap(),
                        elb_name: split_line[1].to_string(),
                        client_address: clnt_addr.unwrap(),
                        backend_address: be_addr.unwrap(),
                        request_processing_time: req_proc_time.unwrap(),
                        backend_processing_time: be_proc_time.unwrap(),
                        response_processing_time: res_proc_time.unwrap(),
                        elb_status_code: elb_sc.unwrap(),
                        backend_status_code: be_sc.unwrap(),
                        received_bytes: bytes_received.unwrap(),
                        sent_bytes: bytes_sent.unwrap(),
                        request_method: split_line[11].trim_matches('"').to_string(),
                        request_url: split_line[12].to_string(),
                        request_http_version: split_line[13].trim_matches('"').to_string()
                    }
                )
            } else {
                None
            }
        }
    }.map( |elb_rec|
        Ok(Box::new(elb_rec))
    ).unwrap_or_else( ||
        Err(ParsingErrors {
            record: record,
            errors: errors
        })
    )
}

//TODO Try to implement this on &str
fn parse_property<T>(raw_prop: &str, prop_name: &'static str, errors: &mut Vec<ELBRecordParsingErrors>) -> Option<T>
    where T: FromStr,
    T::Err: Error + 'static,
{
    match raw_prop.parse::<T>() {
        Ok(parsed) => Some(parsed),

        Err(e) => {
            errors.push(
                ELBRecordParsingErrors::ParsingError {
                    property: prop_name,
                    description: e.description().to_string(),
                }
            );
            None
        }
    }
}

//TODO test the error case.
#[cfg(test)]
mod tests {
    use super::parse_record;
    use super::ELBRecordParsingErrors;

    const TEST_RECORD: &'static str = "2015-08-15T23:43:05.302180Z elb-name 172.16.1.6:54814 \
    172.16.1.5:9000 0.000039 0.145507 0.00003 200 200 0 7582 \
    \"GET http://some.domain.com:80/path0/path1?param0=p0&param1=p1 HTTP/1.1\"\
    ";

    #[test]
	fn parse_record_returns_a_malformed_record_error_for_records_short_on_values() {
        let short_record = "2015-08-15T23:43:05.302180Z elb-name 172.16.1.6:54814 \
        172.16.1.5:9000 0.000039 200 200 0 7582 \
        \"GET http://some.domain.com:80/path0/path1?param0=p0&param1=p1 HTTP/1.1\" \
        ";

        let malformed_error = parse_record(short_record.to_string()).unwrap_err().errors.pop();

		assert_eq!(malformed_error, Some(ELBRecordParsingErrors::MalformedRecord))
	}

    #[test]
	fn parse_record_returns_a_record_with_the_request_http_version() {
        let elb_record = parse_record(TEST_RECORD.to_string()).unwrap();

		assert_eq!(elb_record.request_http_version, "HTTP/1.1")
	}

    #[test]
	fn parse_record_returns_a_record_with_the_request_url() {
        let elb_record = parse_record(TEST_RECORD.to_string()).unwrap();

		assert_eq!(elb_record.request_url, "http://some.domain.com:80/path0/path1?param0=p0&param1=p1")
	}

    #[test]
	fn parse_record_returns_a_record_with_the_request_method() {
        let elb_record = parse_record(TEST_RECORD.to_string()).unwrap();

		assert_eq!(elb_record.request_method, "GET")
	}

    #[test]
	fn parse_record_returns_a_record_with_the_sent_bytes() {
        let elb_record = parse_record(TEST_RECORD.to_string()).unwrap();

		assert_eq!(elb_record.sent_bytes, 7582)
	}

    #[test]
	fn parse_record_returns_a_record_with_the_received_bytes() {
        let elb_record = parse_record(TEST_RECORD.to_string()).unwrap();

		assert_eq!(elb_record.received_bytes, 0)
	}

    #[test]
	fn parse_record_returns_a_record_with_the_backend_status_code() {
        let elb_record = parse_record(TEST_RECORD.to_string()).unwrap();

		assert_eq!(elb_record.backend_status_code, 200)
	}

    #[test]
	fn parse_record_returns_a_record_with_the_elb_status_code() {
        let elb_record = parse_record(TEST_RECORD.to_string()).unwrap();

		assert_eq!(elb_record.elb_status_code, 200)
	}

    #[test]
	fn parse_record_returns_a_record_with_the_response_processing_time() {
        let elb_record = parse_record(TEST_RECORD.to_string()).unwrap();

		assert_eq!(elb_record.response_processing_time, 0.00003)
	}

    #[test]
	fn parse_record_returns_a_record_with_the_backend_processing_time() {
        let elb_record = parse_record(TEST_RECORD.to_string()).unwrap();

		assert_eq!(elb_record.backend_processing_time, 0.145507)
	}

    #[test]
	fn parse_record_returns_a_record_with_the_request_processing_time() {
        let elb_record = parse_record(TEST_RECORD.to_string()).unwrap();

		assert_eq!(elb_record.request_processing_time, 0.000039)
	}

    #[test]
	fn parse_record_returns_a_record_with_the_backend_address() {
        let elb_record = parse_record(TEST_RECORD.to_string()).unwrap();

		assert_eq!(elb_record.backend_address, "172.16.1.5:9000".parse().unwrap())
	}

    #[test]
	fn parse_record_returns_a_record_with_the_client_address() {
        let elb_record = parse_record(TEST_RECORD.to_string()).unwrap();

		assert_eq!(elb_record.client_address, "172.16.1.6:54814".parse().unwrap())
	}

    #[test]
	fn parse_record_returns_a_record_with_the_timestamp() {
        let elb_record = parse_record(TEST_RECORD.to_string()).unwrap();

		assert_eq!(format!("{:?}", elb_record.timestamp), "2015-08-15T23:43:05.302180Z")
	}

    #[test]
	fn parse_record_returns_a_record_with_the_elb_name() {
        let elb_record = parse_record(TEST_RECORD.to_string()).unwrap();

		assert_eq!(elb_record.elb_name, "elb-name")
	}
}
<commit_msg>Created an enum to use as the indexes on the record split.<commit_after>extern crate walkdir;
extern crate chrono;

use std::path;
use self::walkdir::{WalkDir, DirEntry, Error as WalkDirError};
use std::fs::File;
use std::io::BufReader;
use std::io::BufRead;
use self::chrono::{DateTime, UTC};
use std::error::Error;
use std::str::FromStr;
use std::net::SocketAddrV4;

#[derive(Debug)]
pub struct ELBRecord {
    timestamp: DateTime<UTC>,
    elb_name: String,
    client_address: SocketAddrV4,
    backend_address: SocketAddrV4,
    request_processing_time: f32,
    backend_processing_time: f32,
    response_processing_time: f32,
    elb_status_code: u16,
    backend_status_code: u16,
    received_bytes: u64,
    sent_bytes: u64,
    request_method: String,
    request_url: String,
    request_http_version: String
}

pub fn file_list(dir: &path::Path, filenames: &mut Vec<DirEntry>) -> Result<usize, WalkDirError> {
    for entry in WalkDir::new(dir).min_depth(1) {
        match entry {
            Err(err) => return Err(err),
            Ok(entry) => filenames.push(entry),
        }
    }
    Ok(filenames.len())
}

//TODO Reconsider logging based on the standard interfaces included with Rust.
//TODO We really want to accept a function to handle the parsed lines.
pub fn process_files(runtime_context: &::RuntimeContext, filenames: Vec<walkdir::DirEntry>) -> usize {
    let debug = runtime_context.debug;
    let mut record_count = 0;
    for filename in filenames {
        debug!(debug, "Processing file {}.", filename.path().display());
        match File::open(filename.path()) {
            Ok(file) => {
                let buffered_file = BufReader::new(&file);
                let recs: Vec<_> = buffered_file.lines()
                    .map(|possible_line| {
                        match possible_line {
                            Ok(record) => parse_record(record),

                            Err(_) => {
                                Err(ParsingErrors {
                                    record: "".to_string(),
                                    errors: vec![ELBRecordParsingErrors::LineReadError]
                                })
                            }
                        }
                    })
                    .collect();
                record_count += recs.len();
                debug!(debug, "Found {} records in file {}.", recs.len(), filename.path().display());
            },
            Err(e) => {
                println!("ERROR: {}", e);
            }
        }
    }

    record_count
}

//TODO Take a look at the error handling once again.  This doesn't feel write given code you've read.

#[derive(Debug)]
pub struct ParsingErrors {
    record: String,
    errors: Vec<ELBRecordParsingErrors>,
}

#[derive(Debug, PartialEq)]
enum ELBRecordParsingErrors {
    MalformedRecord,
    ParsingError { property: &'static str, description: String },
    LineReadError
}

const ELB_RECORD_FIELD_COUNT: usize = 14;
const TIMESTAMP: &'static str = "timestamp";
const CLIENT_ADDRESS: &'static str = "client address";
const BACKEND_ADDRESS: &'static str = "backend address";
const REQUEST_PROCESSING_TIME: &'static str = "request processing time";
const BACKEND_PROCESSING_TIME: &'static str = "backend processing time";
const RESPONSE_PROCESSING_TIME: &'static str = "response processing time";
const ELB_STATUS_CODE: &'static str = "ELB status code";
const BE_STATUS_CODE: &'static str = "backend status code";
const RECEIVED_BYTES: &'static str = "received bytes";
const SENT_BYTES: &'static str = "sent bytes";

#[derive(Debug, PartialEq)]
enum ELBRecordFields {
    Timestamp = 0,
    ELBName,
    ClientAddress,
    BackendAddress,
    RequestProcessingTime,
    BackendProcessingTime,
    ResponseProcessingTime,
    ELBStatusCode,
    BackendStatusCode,
    ReceivedBytes,
    SentBytes,
    RequestMethod,
    RequestURL,
    RequestHTTPVersion
}

impl ELBRecordFields {

  fn idx(self) -> usize {
    self as usize
  }
}

pub fn parse_record(record: String) -> Result<Box<ELBRecord>, ParsingErrors> {
    let mut errors: Vec<ELBRecordParsingErrors> = Vec::new();

    {
        //record is borrowed by the split method which means ownership of record cannot be
        //transferred to ParsingErrors until the borrow is complete.
        //Scoping this section of code seems more readable than creating a separate function
        //just to mitigate the borrow.
        let split_line: Vec<&str> = record.split(' ').collect();
        if split_line.len() != ELB_RECORD_FIELD_COUNT {
            errors.push(ELBRecordParsingErrors::MalformedRecord);
            None
        } else {
            let ts = parse_property::<DateTime<UTC>>(split_line[ELBRecordFields::Timestamp.idx()], TIMESTAMP, &mut errors);
            let clnt_addr = parse_property::<SocketAddrV4>(split_line[ELBRecordFields::ClientAddress.idx()], CLIENT_ADDRESS, &mut errors);
            let be_addr = parse_property::<SocketAddrV4>(split_line[ELBRecordFields::BackendAddress.idx()], BACKEND_ADDRESS, &mut errors);
            let req_proc_time = parse_property::<f32>(split_line[ELBRecordFields::RequestProcessingTime.idx()], REQUEST_PROCESSING_TIME, &mut errors);
            let be_proc_time = parse_property::<f32>(split_line[ELBRecordFields::BackendProcessingTime.idx()], BACKEND_PROCESSING_TIME, &mut errors);
            let res_proc_time = parse_property::<f32>(split_line[ELBRecordFields::ResponseProcessingTime.idx()], RESPONSE_PROCESSING_TIME, &mut errors);
            let elb_sc = parse_property::<u16>(split_line[ELBRecordFields::ELBStatusCode.idx()], ELB_STATUS_CODE, &mut errors);
            let be_sc = parse_property::<u16>(split_line[ELBRecordFields::BackendStatusCode.idx()], BE_STATUS_CODE, &mut errors);
            let bytes_received = parse_property::<u64>(split_line[ELBRecordFields::ReceivedBytes.idx()], RECEIVED_BYTES, &mut errors);
            let bytes_sent = parse_property::<u64>(split_line[ELBRecordFields::SentBytes.idx()], SENT_BYTES, &mut errors);

            if errors.is_empty() {
                //If errors is empty it is more than likely parsing was successful and unwrap is safe.
                Some(
                    ELBRecord {
                        timestamp: ts.unwrap(),
                        elb_name: split_line[ELBRecordFields::ELBName.idx()].to_string(),
                        client_address: clnt_addr.unwrap(),
                        backend_address: be_addr.unwrap(),
                        request_processing_time: req_proc_time.unwrap(),
                        backend_processing_time: be_proc_time.unwrap(),
                        response_processing_time: res_proc_time.unwrap(),
                        elb_status_code: elb_sc.unwrap(),
                        backend_status_code: be_sc.unwrap(),
                        received_bytes: bytes_received.unwrap(),
                        sent_bytes: bytes_sent.unwrap(),
                        request_method: split_line[ELBRecordFields::RequestMethod.idx()].trim_matches('"').to_string(),
                        request_url: split_line[ELBRecordFields::RequestURL.idx()].to_string(),
                        request_http_version: split_line[ELBRecordFields::RequestHTTPVersion.idx()].trim_matches('"').to_string()
                    }
                )
            } else {
                None
            }
        }
    }.map( |elb_rec|
        Ok(Box::new(elb_rec))
    ).unwrap_or_else( ||
        Err(ParsingErrors {
            record: record,
            errors: errors
        })
    )
}

//TODO Try to implement this on &str
fn parse_property<T>(raw_prop: &str, prop_name: &'static str, errors: &mut Vec<ELBRecordParsingErrors>) -> Option<T>
    where T: FromStr,
    T::Err: Error + 'static,
{
    match raw_prop.parse::<T>() {
        Ok(parsed) => Some(parsed),

        Err(e) => {
            errors.push(
                ELBRecordParsingErrors::ParsingError {
                    property: prop_name,
                    description: e.description().to_string(),
                }
            );
            None
        }
    }
}

//TODO test the error case.
#[cfg(test)]
mod tests {
    use super::parse_record;
    use super::ELBRecordParsingErrors;

    const TEST_RECORD: &'static str = "2015-08-15T23:43:05.302180Z elb-name 172.16.1.6:54814 \
    172.16.1.5:9000 0.000039 0.145507 0.00003 200 200 0 7582 \
    \"GET http://some.domain.com:80/path0/path1?param0=p0&param1=p1 HTTP/1.1\"\
    ";

    #[test]
	fn parse_record_returns_a_malformed_record_error_for_records_short_on_values() {
        let short_record = "2015-08-15T23:43:05.302180Z elb-name 172.16.1.6:54814 \
        172.16.1.5:9000 0.000039 200 200 0 7582 \
        \"GET http://some.domain.com:80/path0/path1?param0=p0&param1=p1 HTTP/1.1\" \
        ";

        let malformed_error = parse_record(short_record.to_string()).unwrap_err().errors.pop();

		assert_eq!(malformed_error, Some(ELBRecordParsingErrors::MalformedRecord))
	}

    #[test]
	fn parse_record_returns_a_record_with_the_request_http_version() {
        let elb_record = parse_record(TEST_RECORD.to_string()).unwrap();

		assert_eq!(elb_record.request_http_version, "HTTP/1.1")
	}

    #[test]
	fn parse_record_returns_a_record_with_the_request_url() {
        let elb_record = parse_record(TEST_RECORD.to_string()).unwrap();

		assert_eq!(elb_record.request_url, "http://some.domain.com:80/path0/path1?param0=p0&param1=p1")
	}

    #[test]
	fn parse_record_returns_a_record_with_the_request_method() {
        let elb_record = parse_record(TEST_RECORD.to_string()).unwrap();

		assert_eq!(elb_record.request_method, "GET")
	}

    #[test]
	fn parse_record_returns_a_record_with_the_sent_bytes() {
        let elb_record = parse_record(TEST_RECORD.to_string()).unwrap();

		assert_eq!(elb_record.sent_bytes, 7582)
	}

    #[test]
	fn parse_record_returns_a_record_with_the_received_bytes() {
        let elb_record = parse_record(TEST_RECORD.to_string()).unwrap();

		assert_eq!(elb_record.received_bytes, 0)
	}

    #[test]
	fn parse_record_returns_a_record_with_the_backend_status_code() {
        let elb_record = parse_record(TEST_RECORD.to_string()).unwrap();

		assert_eq!(elb_record.backend_status_code, 200)
	}

    #[test]
	fn parse_record_returns_a_record_with_the_elb_status_code() {
        let elb_record = parse_record(TEST_RECORD.to_string()).unwrap();

		assert_eq!(elb_record.elb_status_code, 200)
	}

    #[test]
	fn parse_record_returns_a_record_with_the_response_processing_time() {
        let elb_record = parse_record(TEST_RECORD.to_string()).unwrap();

		assert_eq!(elb_record.response_processing_time, 0.00003)
	}

    #[test]
	fn parse_record_returns_a_record_with_the_backend_processing_time() {
        let elb_record = parse_record(TEST_RECORD.to_string()).unwrap();

		assert_eq!(elb_record.backend_processing_time, 0.145507)
	}

    #[test]
	fn parse_record_returns_a_record_with_the_request_processing_time() {
        let elb_record = parse_record(TEST_RECORD.to_string()).unwrap();

		assert_eq!(elb_record.request_processing_time, 0.000039)
	}

    #[test]
	fn parse_record_returns_a_record_with_the_backend_address() {
        let elb_record = parse_record(TEST_RECORD.to_string()).unwrap();

		assert_eq!(elb_record.backend_address, "172.16.1.5:9000".parse().unwrap())
	}

    #[test]
	fn parse_record_returns_a_record_with_the_client_address() {
        let elb_record = parse_record(TEST_RECORD.to_string()).unwrap();

		assert_eq!(elb_record.client_address, "172.16.1.6:54814".parse().unwrap())
	}

    #[test]
	fn parse_record_returns_a_record_with_the_timestamp() {
        let elb_record = parse_record(TEST_RECORD.to_string()).unwrap();

		assert_eq!(format!("{:?}", elb_record.timestamp), "2015-08-15T23:43:05.302180Z")
	}

    #[test]
	fn parse_record_returns_a_record_with_the_elb_name() {
        let elb_record = parse_record(TEST_RECORD.to_string()).unwrap();

		assert_eq!(elb_record.elb_name, "elb-name")
	}
}
<|endoftext|>


<commit_before>use std::io::Read;
use std::path::PathBuf;

use mailparse::{MailHeader, parse_mail};

use libimagref::hasher::Hasher;
use libimagref::hasher::DefaultHasher;
use libimagref::error::RefErrorKind as REK;
use libimagref::error::MapErrInto;
use libimagref::result::Result as RResult;
use libimagerror::into::IntoError;

use error::MailErrorKind as MEK;

pub struct MailHasher {
    defaulthasher: DefaultHasher,
}

impl MailHasher {

    pub fn new() -> MailHasher {
        MailHasher { defaulthasher: DefaultHasher::new() }
    }

}

impl Hasher for MailHasher {

    fn hash_name(&self) -> &'static str {
        "default_mail_hasher"
    }

    fn create_hash<R: Read>(&mut self, pb: &PathBuf, c: &mut R) -> RResult<String> {
        use filters::filter::Filter;

        let mut s = String::new();
        try!(c.read_to_string(&mut s).map_err_into(REK::UTF8Error).map_err_into(REK::IOError));

        parse_mail(&s.as_bytes())
            .map_err(Box::new)
            .map_err(|e| MEK::MailParsingError.into_error_with_cause(e))
            .map_err_into(REK::RefHashingError)
            .and_then(|mail| {
                let has_key = |hdr: &MailHeader, exp: &str|
                    hdr.get_key().map(|s| s == exp).unwrap_or(false);

                let subject_filter = |hdr: &MailHeader| has_key(hdr, "Subject");
                let from_filter    = |hdr: &MailHeader| has_key(hdr, "From");
                let to_filter      = |hdr: &MailHeader| has_key(hdr, "To");

                let filter = subject_filter.or(from_filter).or(to_filter);

                let s : String = mail.headers
                    .iter()
                    .filter(|item| filter.filter(item))
                    .filter_map(|hdr| hdr.get_value().ok()) // TODO: Do not hide error here
                    .collect::<Vec<String>>()
                    .join("");

                self.defaulthasher.create_hash(pb, &mut s.as_bytes())
            })
    }

}
<commit_msg>Resolve TODO: Do not hide error, but instantly return it<commit_after>use std::io::Read;
use std::path::PathBuf;

use mailparse::{MailHeader, parse_mail};

use libimagref::hasher::Hasher;
use libimagref::hasher::DefaultHasher;
use libimagref::error::RefErrorKind as REK;
use libimagref::error::MapErrInto;
use libimagref::result::Result as RResult;
use libimagerror::into::IntoError;

use error::MailErrorKind as MEK;

pub struct MailHasher {
    defaulthasher: DefaultHasher,
}

impl MailHasher {

    pub fn new() -> MailHasher {
        MailHasher { defaulthasher: DefaultHasher::new() }
    }

}

impl Hasher for MailHasher {

    fn hash_name(&self) -> &'static str {
        "default_mail_hasher"
    }

    fn create_hash<R: Read>(&mut self, pb: &PathBuf, c: &mut R) -> RResult<String> {
        use filters::filter::Filter;

        let mut s = String::new();
        try!(c.read_to_string(&mut s).map_err_into(REK::UTF8Error).map_err_into(REK::IOError));

        parse_mail(&s.as_bytes())
            .map_err(Box::new)
            .map_err(|e| MEK::MailParsingError.into_error_with_cause(e))
            .map_err_into(REK::RefHashingError)
            .and_then(|mail| {
                let has_key = |hdr: &MailHeader, exp: &str|
                    hdr.get_key().map(|s| s == exp).unwrap_or(false);

                let subject_filter = |hdr: &MailHeader| has_key(hdr, "Subject");
                let from_filter    = |hdr: &MailHeader| has_key(hdr, "From");
                let to_filter      = |hdr: &MailHeader| has_key(hdr, "To");

                let filter = subject_filter.or(from_filter).or(to_filter);

                let mut v = vec![];
                for hdr in mail.headers.iter().filter(|item| filter.filter(item)) {
                    let s = try!(hdr.get_value()
                        .map_err(Box::new)
                        .map_err(|e| REK::RefHashingError.into_error_with_cause(e)));

                    v.push(s);
                }
                let s : String = v.join("");

                self.defaulthasher.create_hash(pb, &mut s.as_bytes())
            })
    }

}
<|endoftext|>


<commit_before>// Copyright 2017 Serde Developers
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::ascii::AsciiExt;
use std::str::FromStr;

use self::RenameRule::*;

#[derive(Debug, PartialEq)]
pub enum RenameRule {
    /// Don't apply a default rename rule.
    None,
    /// Rename direct children to "lowercase" style.
    LowerCase,
    /// Rename direct children to "PascalCase" style, as typically used for enum variants.
    PascalCase,
    /// Rename direct children to "camelCase" style.
    CamelCase,
    /// Rename direct children to "snake_case" style, as commonly used for fields.
    SnakeCase,
    /// Rename direct children to "SCREAMING_SNAKE_CASE" style, as commonly used for constants.
    ScreamingSnakeCase,
    /// Rename direct children to "kebab-case" style.
    KebabCase,
    /// Rename direct children to "SCREAMING-KEBAB-CASE" style.
    ScreamingKebabCase
}

impl RenameRule {
    pub fn apply_to_variant(&self, variant: &str) -> String {
        match *self {
            None | PascalCase => variant.to_owned(),
            LowerCase => variant.to_ascii_lowercase(),
            CamelCase => variant[..1].to_ascii_lowercase() + &variant[1..],
            SnakeCase => {
                let mut snake = String::new();
                for (i, ch) in variant.char_indices() {
                    if i > 0 && ch.is_uppercase() {
                        snake.push('_');
                    }
                    snake.push(ch.to_ascii_lowercase());
                }
                snake
            }
            ScreamingSnakeCase => SnakeCase.apply_to_variant(variant).to_ascii_uppercase(),
            KebabCase => SnakeCase.apply_to_variant(variant).replace('_', "-"),
            ScreamingKebabCase => ScreamingSnakeCase.apply_to_variant(variant).replace('_', "-")
        }
    }

    pub fn apply_to_field(&self, field: &str) -> String {
        match *self {
            None | LowerCase | SnakeCase => field.to_owned(),
            PascalCase => {
                let mut pascal = String::new();
                let mut capitalize = true;
                for ch in field.chars() {
                    if ch == '_' {
                        capitalize = true;
                    } else if capitalize {
                        pascal.push(ch.to_ascii_uppercase());
                        capitalize = false;
                    } else {
                        pascal.push(ch);
                    }
                }
                pascal
            }
            CamelCase => {
                let pascal = PascalCase.apply_to_field(field);
                pascal[..1].to_ascii_lowercase() + &pascal[1..]
            }
            ScreamingSnakeCase => field.to_ascii_uppercase(),
            KebabCase => field.replace('_', "-"),
            ScreamingKebabCase => ScreamingSnakeCase.apply_to_field(field).replace('_', "-")
        }
    }
}

impl FromStr for RenameRule {
    type Err = ();

    fn from_str(rename_all_str: &str) -> Result<Self, Self::Err> {
        match rename_all_str {
            "lowercase" => Ok(LowerCase),
            "PascalCase" => Ok(PascalCase),
            "camelCase" => Ok(CamelCase),
            "snake_case" => Ok(SnakeCase),
            "SCREAMING_SNAKE_CASE" => Ok(ScreamingSnakeCase),
            "kebab-case" => Ok(KebabCase),
            "SCREAMING-KEBAB-CASE" => Ok(ScreamingKebabCase),
            _ => Err(()),
        }
    }
}

#[test]
fn rename_variants() {
    for &(original, lower, camel, snake, screaming, kebab, screaming_kebab) in
        &[
            ("Outcome", "outcome", "outcome", "outcome", "OUTCOME", "outcome", "OUTCOME"),
            ("VeryTasty", "verytasty", "veryTasty", "very_tasty", "VERY_TASTY", "very-tasty", "VERY-TASTY"),
            ("A", "a", "a", "a", "A", "a", "A"),
            ("Z42", "z42", "z42", "z42", "Z42", "z42", "Z42"),
        ] {
        assert_eq!(None.apply_to_variant(original), original);
        assert_eq!(LowerCase.apply_to_variant(original), lower);
        assert_eq!(PascalCase.apply_to_variant(original), original);
        assert_eq!(CamelCase.apply_to_variant(original), camel);
        assert_eq!(SnakeCase.apply_to_variant(original), snake);
        assert_eq!(ScreamingSnakeCase.apply_to_variant(original), screaming);
        assert_eq!(KebabCase.apply_to_variant(original), kebab);
        assert_eq!(ScreamingKebabCase.apply_to_variant(original), screaming_kebab);
    }
}

#[test]
fn rename_fields() {
    for &(original, pascal, camel, screaming, kebab, screaming_kebab) in
        &[
            ("outcome", "Outcome", "outcome", "OUTCOME", "outcome", "OUTCOME"),
            ("very_tasty", "VeryTasty", "veryTasty", "VERY_TASTY", "very-tasty", "VERY-TASTY"),
            ("a", "A", "a", "A", "a", "A"),
            ("z42", "Z42", "z42", "Z42", "z42", "Z42"),
        ] {
        assert_eq!(None.apply_to_field(original), original);
        assert_eq!(PascalCase.apply_to_field(original), pascal);
        assert_eq!(CamelCase.apply_to_field(original), camel);
        assert_eq!(SnakeCase.apply_to_field(original), original);
        assert_eq!(ScreamingSnakeCase.apply_to_field(original), screaming);
        assert_eq!(KebabCase.apply_to_field(original), kebab);
        assert_eq!(ScreamingKebabCase.apply_to_field(original), screaming_kebab);
    }
}
<commit_msg>AsciiExt transition<commit_after>// Copyright 2017 Serde Developers
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// See https://users.rust-lang.org/t/psa-dealing-with-warning-unused-import-std-ascii-asciiext-in-today-s-nightly/13726
#[allow(unused_imports)]
use std::ascii::AsciiExt;

use std::str::FromStr;

use self::RenameRule::*;

#[derive(Debug, PartialEq)]
pub enum RenameRule {
    /// Don't apply a default rename rule.
    None,
    /// Rename direct children to "lowercase" style.
    LowerCase,
    /// Rename direct children to "PascalCase" style, as typically used for enum variants.
    PascalCase,
    /// Rename direct children to "camelCase" style.
    CamelCase,
    /// Rename direct children to "snake_case" style, as commonly used for fields.
    SnakeCase,
    /// Rename direct children to "SCREAMING_SNAKE_CASE" style, as commonly used for constants.
    ScreamingSnakeCase,
    /// Rename direct children to "kebab-case" style.
    KebabCase,
    /// Rename direct children to "SCREAMING-KEBAB-CASE" style.
    ScreamingKebabCase
}

impl RenameRule {
    pub fn apply_to_variant(&self, variant: &str) -> String {
        match *self {
            None | PascalCase => variant.to_owned(),
            LowerCase => variant.to_ascii_lowercase(),
            CamelCase => variant[..1].to_ascii_lowercase() + &variant[1..],
            SnakeCase => {
                let mut snake = String::new();
                for (i, ch) in variant.char_indices() {
                    if i > 0 && ch.is_uppercase() {
                        snake.push('_');
                    }
                    snake.push(ch.to_ascii_lowercase());
                }
                snake
            }
            ScreamingSnakeCase => SnakeCase.apply_to_variant(variant).to_ascii_uppercase(),
            KebabCase => SnakeCase.apply_to_variant(variant).replace('_', "-"),
            ScreamingKebabCase => ScreamingSnakeCase.apply_to_variant(variant).replace('_', "-")
        }
    }

    pub fn apply_to_field(&self, field: &str) -> String {
        match *self {
            None | LowerCase | SnakeCase => field.to_owned(),
            PascalCase => {
                let mut pascal = String::new();
                let mut capitalize = true;
                for ch in field.chars() {
                    if ch == '_' {
                        capitalize = true;
                    } else if capitalize {
                        pascal.push(ch.to_ascii_uppercase());
                        capitalize = false;
                    } else {
                        pascal.push(ch);
                    }
                }
                pascal
            }
            CamelCase => {
                let pascal = PascalCase.apply_to_field(field);
                pascal[..1].to_ascii_lowercase() + &pascal[1..]
            }
            ScreamingSnakeCase => field.to_ascii_uppercase(),
            KebabCase => field.replace('_', "-"),
            ScreamingKebabCase => ScreamingSnakeCase.apply_to_field(field).replace('_', "-")
        }
    }
}

impl FromStr for RenameRule {
    type Err = ();

    fn from_str(rename_all_str: &str) -> Result<Self, Self::Err> {
        match rename_all_str {
            "lowercase" => Ok(LowerCase),
            "PascalCase" => Ok(PascalCase),
            "camelCase" => Ok(CamelCase),
            "snake_case" => Ok(SnakeCase),
            "SCREAMING_SNAKE_CASE" => Ok(ScreamingSnakeCase),
            "kebab-case" => Ok(KebabCase),
            "SCREAMING-KEBAB-CASE" => Ok(ScreamingKebabCase),
            _ => Err(()),
        }
    }
}

#[test]
fn rename_variants() {
    for &(original, lower, camel, snake, screaming, kebab, screaming_kebab) in
        &[
            ("Outcome", "outcome", "outcome", "outcome", "OUTCOME", "outcome", "OUTCOME"),
            ("VeryTasty", "verytasty", "veryTasty", "very_tasty", "VERY_TASTY", "very-tasty", "VERY-TASTY"),
            ("A", "a", "a", "a", "A", "a", "A"),
            ("Z42", "z42", "z42", "z42", "Z42", "z42", "Z42"),
        ] {
        assert_eq!(None.apply_to_variant(original), original);
        assert_eq!(LowerCase.apply_to_variant(original), lower);
        assert_eq!(PascalCase.apply_to_variant(original), original);
        assert_eq!(CamelCase.apply_to_variant(original), camel);
        assert_eq!(SnakeCase.apply_to_variant(original), snake);
        assert_eq!(ScreamingSnakeCase.apply_to_variant(original), screaming);
        assert_eq!(KebabCase.apply_to_variant(original), kebab);
        assert_eq!(ScreamingKebabCase.apply_to_variant(original), screaming_kebab);
    }
}

#[test]
fn rename_fields() {
    for &(original, pascal, camel, screaming, kebab, screaming_kebab) in
        &[
            ("outcome", "Outcome", "outcome", "OUTCOME", "outcome", "OUTCOME"),
            ("very_tasty", "VeryTasty", "veryTasty", "VERY_TASTY", "very-tasty", "VERY-TASTY"),
            ("a", "A", "a", "A", "a", "A"),
            ("z42", "Z42", "z42", "Z42", "z42", "Z42"),
        ] {
        assert_eq!(None.apply_to_field(original), original);
        assert_eq!(PascalCase.apply_to_field(original), pascal);
        assert_eq!(CamelCase.apply_to_field(original), camel);
        assert_eq!(SnakeCase.apply_to_field(original), original);
        assert_eq!(ScreamingSnakeCase.apply_to_field(original), screaming);
        assert_eq!(KebabCase.apply_to_field(original), kebab);
        assert_eq!(ScreamingKebabCase.apply_to_field(original), screaming_kebab);
    }
}
<|endoftext|>


<commit_before>use std::thread;
use std::cell::UnsafeCell;
use std::sync::Arc;
use std::sync::mpsc::{channel, Sender, Receiver, RecvError};
use basetypes::*;
use chess_move::MoveStack;
use tt::*;
use position::Position;

pub enum Command {
    Search {
        search_id: usize,
        position: Position,
        depth: u8,
        lower_bound: Value,
        upper_bound: Value,
    },
    Stop,
    Exit,
}

pub enum Report {
    Progress {
        search_id: usize,
        searched_nodes: NodeCount,
        depth: u8,
    },
    Done {
        search_id: usize,
        searched_nodes: NodeCount,
        value: Option<Value>,
    },
}

pub fn run_deepening(tt: Arc<TranspositionTable>,
                     commands: Receiver<Command>,
                     reports: Sender<Report>) {
    let (slave_commands_tx, slave_commands_rx) = channel();
    let (slave_reports_tx, slave_reports_rx) = channel();
    let slave = thread::spawn(move || {
        run(tt, slave_commands_rx, slave_reports_tx);
    });
    let mut pending_command = None;
    loop {
        // If there is a pending command, we take it, otherwise we
        // block and wait to receive a new one.
        let command = match pending_command.take() {
            Some(cmd) => cmd,
            None => commands.recv().or::<RecvError>(Ok(Command::Exit)).unwrap(),
        };

        match command {
            Command::Search { search_id, position, depth, lower_bound, upper_bound } => {
                let mut searched_nodes_final = 0;
                let mut value_final = None;
                'depthloop: for n in 1..(depth + 1) {
                    slave_commands_tx.send(Command::Search {
                                         search_id: n as usize,
                                         position: position.clone(),
                                         depth: n,
                                         lower_bound: lower_bound,
                                         upper_bound: upper_bound,
                                     })
                                     .unwrap();
                    loop {
                        match slave_reports_rx.recv().unwrap() {
                            Report::Progress { searched_nodes, .. } => {
                                reports.send(Report::Progress {
                                           search_id: search_id,
                                           searched_nodes: searched_nodes_final + searched_nodes,
                                           depth: n,
                                       })
                                       .ok();
                                if pending_command.is_none() {
                                    pending_command = match commands.try_recv() {
                                        Ok(cmd) => {
                                            slave_commands_tx.send(Command::Stop).unwrap();
                                            Some(cmd)
                                        }
                                        _ => None,
                                    }
                                }
                            }
                            Report::Done { searched_nodes, value, .. } => {
                                searched_nodes_final += searched_nodes;
                                if n == depth {
                                    value_final = value;
                                }
                                if pending_command.is_some() {
                                    break 'depthloop;
                                }
                                break;
                            }
                        }
                    }
                }
                reports.send(Report::Done {
                           search_id: search_id,
                           searched_nodes: searched_nodes_final,
                           value: value_final,
                       })
                       .ok();
            }
            Command::Stop => {
                slave_commands_tx.send(Command::Stop).unwrap();
                continue;
            }
            Command::Exit => {
                slave_commands_tx.send(Command::Exit).unwrap();
                break;
            }
        }
    }
    slave.join().unwrap();
}

pub fn run(tt: Arc<TranspositionTable>, commands: Receiver<Command>, reports: Sender<Report>) {
    thread_local!(
        static MOVE_STACK: UnsafeCell<MoveStack> = UnsafeCell::new(MoveStack::new())
    );
    MOVE_STACK.with(|s| {
        let mut move_stack = unsafe { &mut *s.get() };
        let mut pending_command = None;
        loop {
            // If there is a pending command, we take it, otherwise we
            // block and wait to receive a new one.
            let command = match pending_command.take() {
                Some(cmd) => cmd,
                None => commands.recv().or::<RecvError>(Ok(Command::Exit)).unwrap(),
            };

            match command {
                Command::Search { search_id, mut position, depth, lower_bound, upper_bound } => {
                    let mut reported_nodes = 0;
                    let mut unreported_nodes = 0;
                    let value = search(&tt,
                                       &mut position,
                                       move_stack,
                                       &mut unreported_nodes,
                                       &mut |n| {
                                           reported_nodes += n;
                                           reports.send(Report::Progress {
                                                      search_id: search_id,
                                                      searched_nodes: reported_nodes,
                                                      depth: depth,
                                                  })
                                                  .ok();
                                           match commands.try_recv() {
                                               Ok(x) => {
                                                   pending_command = Some(x);
                                                   Err(TerminatedSearch)
                                               }
                                               _ => Ok(()),
                                           }
                                       },
                                       lower_bound,
                                       upper_bound,
                                       depth)
                                    .ok();
                    reported_nodes += unreported_nodes;
                    reports.send(Report::Progress {
                               search_id: search_id,
                               searched_nodes: reported_nodes,
                               depth: depth,
                           })
                           .ok();
                    reports.send(Report::Done {
                               search_id: search_id,
                               searched_nodes: reported_nodes,
                               value: value,
                           })
                           .ok();
                    move_stack.clear();
                }
                Command::Stop => continue,
                Command::Exit => break,
            }
        }
    })
}

// Represents a terminated search condition.
struct TerminatedSearch;

// Helper function for `run()`. It implements the principal variation
// search algorithm. When returning `Err(TerminatedSearch)`, this
// function may leave un-restored move lists in `moves`.
fn search(tt: &TranspositionTable,
          p: &mut Position,
          moves: &mut MoveStack,
          nc: &mut NodeCount,
          report: &mut FnMut(NodeCount) -> Result<(), TerminatedSearch>,
          mut alpha: Value, // lower bound
          beta: Value, // upper bound
          depth: u8)
          -> Result<Value, TerminatedSearch> {
    assert!(alpha < beta);
    if depth == 0 {
        // On leaf nodes, do quiescence search.
        let (value, nodes) = p.evaluate_quiescence(alpha, beta, None);
        *nc += nodes;
        Ok(value)
    } else {
        // Consult the transposition table.
        let hash_move16 = if let Some(entry) = tt.probe(p.hash()) {
            if entry.depth() >= depth && entry.bound() == BOUND_EXACT {
                // We already know the exact value for this position
                // for same depth or higher.
                return Ok(entry.value());
            }
            entry.move16()
        } else {
            0
        };

        moves.save();

        if hash_move16 != 0 {
            if let Some(m) = p.try_move_digest(hash_move16) {
                moves.push(m);
            }
        }
        let mut generated = false;

        let mut bound_type = BOUND_UPPER;
        let mut move16 = 0;
        let mut no_moves_yet = true;
        loop {
            // TODO: This is ugly, and probably inefficient.
            let m = match moves.remove_best_move() {
                None if generated => break,
                None => {
                    p.generate_moves(moves);
                    moves.remove_move(hash_move16);
                    generated = true;
                    continue;
                }
                Some(x) => x,
            };

            if p.do_move(m) {
                // From time to time, we report how many nodes had
                // been searched since the last report. This also
                // gives an opportunity for the search to be
                // terminated.
                *nc += 1;
                if *nc > NODE_COUNT_REPORT_INTERVAL {
                    try!(report(*nc));
                    *nc = 0;
                }

                let mut value = if no_moves_yet {
                    // The first move we analyze with a fully open
                    // window (alpha, beta).
                    -try!(search(tt, p, moves, nc, report, -beta, -alpha, depth - 1))
                } else {
                    // For the next moves we first try to prove that
                    // they are not better than our current best
                    // move. For this purpose we analyze them with a
                    // null window (alpha, alpha + 1). This is faster
                    // than a full window search. Only when we are
                    // certain that the move is better than our
                    // current best move, we do a full-window search.
                    match -try!(search(tt, p, moves, nc, report, -alpha - 1, -alpha, depth - 1)) {
                        x if x <= alpha => x,
                        _ => -try!(search(tt, p, moves, nc, report, -beta, -alpha, depth - 1)),
                    }
                };

                // A checkmate in fewer moves is better.
                if value > 20000 {
                    value -= 1;
                }
                if value < -20000 {
                    value += 1;
                }

                no_moves_yet = false;
                p.undo_move();

                if value >= beta {
                    // This move is too good, so that the opponent
                    // will not allow this line of play to
                    // happen. Therefore we can stop here.
                    alpha = beta;
                    bound_type = BOUND_LOWER;
                    move16 = m.digest();
                    break;
                }
                if value > alpha {
                    // We found ourselves a new best move.
                    alpha = value;
                    bound_type = BOUND_EXACT;
                    move16 = m.digest();
                }
            }
        }
        moves.restore();
        if no_moves_yet {
            // No legal moves -- this is a final position.
            alpha = p.evaluate_final();
            bound_type = BOUND_EXACT;
        }
        // TODO: Do not store values with too small `depth`.
        tt.store(p.hash(),
                 EntryData::new(alpha, bound_type, depth, move16, 0));
        Ok(alpha)
    }
}

const NODE_COUNT_REPORT_INTERVAL: NodeCount = 10000;

#[cfg(test)]
mod tests {
    use super::search;
    use chess_move::*;
    use tt::*;
    use position::Position;

    #[test]
    fn test_search() {
        let mut p = Position::from_fen("8/8/8/8/3q3k/7n/6PP/2Q2R1K b - - 0 1").ok().unwrap();
        let value = search(&TranspositionTable::new(),
                           &mut p,
                           &mut MoveStack::new(),
                           &mut 0,
                           &mut |_| Ok(()),
                           -30000,
                           30000,
                           2)
                        .ok()
                        .unwrap();
        assert!(value < -300);
        let value = search(&TranspositionTable::new(),
                           &mut p,
                           &mut MoveStack::new(),
                           &mut 0,
                           &mut |_| Ok(()),
                           -30000,
                           30000,
                           4)
                        .ok()
                        .unwrap();
        assert!(value >= 20000);
    }
}
<commit_msg>take lower/upper bounds from TT as well<commit_after>use std::thread;
use std::cell::UnsafeCell;
use std::sync::Arc;
use std::sync::mpsc::{channel, Sender, Receiver, RecvError};
use basetypes::*;
use chess_move::MoveStack;
use tt::*;
use position::Position;

pub enum Command {
    Search {
        search_id: usize,
        position: Position,
        depth: u8,
        lower_bound: Value,
        upper_bound: Value,
    },
    Stop,
    Exit,
}

pub enum Report {
    Progress {
        search_id: usize,
        searched_nodes: NodeCount,
        depth: u8,
    },
    Done {
        search_id: usize,
        searched_nodes: NodeCount,
        value: Option<Value>,
    },
}

pub fn run_deepening(tt: Arc<TranspositionTable>,
                     commands: Receiver<Command>,
                     reports: Sender<Report>) {
    let (slave_commands_tx, slave_commands_rx) = channel();
    let (slave_reports_tx, slave_reports_rx) = channel();
    let slave = thread::spawn(move || {
        run(tt, slave_commands_rx, slave_reports_tx);
    });
    let mut pending_command = None;
    loop {
        // If there is a pending command, we take it, otherwise we
        // block and wait to receive a new one.
        let command = match pending_command.take() {
            Some(cmd) => cmd,
            None => commands.recv().or::<RecvError>(Ok(Command::Exit)).unwrap(),
        };

        match command {
            Command::Search { search_id, position, depth, lower_bound, upper_bound } => {
                let mut searched_nodes_final = 0;
                let mut value_final = None;
                'depthloop: for n in 1..(depth + 1) {
                    slave_commands_tx.send(Command::Search {
                                         search_id: n as usize,
                                         position: position.clone(),
                                         depth: n,
                                         lower_bound: lower_bound,
                                         upper_bound: upper_bound,
                                     })
                                     .unwrap();
                    loop {
                        match slave_reports_rx.recv().unwrap() {
                            Report::Progress { searched_nodes, .. } => {
                                reports.send(Report::Progress {
                                           search_id: search_id,
                                           searched_nodes: searched_nodes_final + searched_nodes,
                                           depth: n,
                                       })
                                       .ok();
                                if pending_command.is_none() {
                                    pending_command = match commands.try_recv() {
                                        Ok(cmd) => {
                                            slave_commands_tx.send(Command::Stop).unwrap();
                                            Some(cmd)
                                        }
                                        _ => None,
                                    }
                                }
                            }
                            Report::Done { searched_nodes, value, .. } => {
                                searched_nodes_final += searched_nodes;
                                if n == depth {
                                    value_final = value;
                                }
                                if pending_command.is_some() {
                                    break 'depthloop;
                                }
                                break;
                            }
                        }
                    }
                }
                reports.send(Report::Done {
                           search_id: search_id,
                           searched_nodes: searched_nodes_final,
                           value: value_final,
                       })
                       .ok();
            }
            Command::Stop => {
                slave_commands_tx.send(Command::Stop).unwrap();
                continue;
            }
            Command::Exit => {
                slave_commands_tx.send(Command::Exit).unwrap();
                break;
            }
        }
    }
    slave.join().unwrap();
}

pub fn run(tt: Arc<TranspositionTable>, commands: Receiver<Command>, reports: Sender<Report>) {
    thread_local!(
        static MOVE_STACK: UnsafeCell<MoveStack> = UnsafeCell::new(MoveStack::new())
    );
    MOVE_STACK.with(|s| {
        let mut move_stack = unsafe { &mut *s.get() };
        let mut pending_command = None;
        loop {
            // If there is a pending command, we take it, otherwise we
            // block and wait to receive a new one.
            let command = match pending_command.take() {
                Some(cmd) => cmd,
                None => commands.recv().or::<RecvError>(Ok(Command::Exit)).unwrap(),
            };

            match command {
                Command::Search { search_id, mut position, depth, lower_bound, upper_bound } => {
                    let mut reported_nodes = 0;
                    let mut unreported_nodes = 0;
                    let value = search(&tt,
                                       &mut position,
                                       move_stack,
                                       &mut unreported_nodes,
                                       &mut |n| {
                                           reported_nodes += n;
                                           reports.send(Report::Progress {
                                                      search_id: search_id,
                                                      searched_nodes: reported_nodes,
                                                      depth: depth,
                                                  })
                                                  .ok();
                                           match commands.try_recv() {
                                               Ok(x) => {
                                                   pending_command = Some(x);
                                                   Err(TerminatedSearch)
                                               }
                                               _ => Ok(()),
                                           }
                                       },
                                       lower_bound,
                                       upper_bound,
                                       depth)
                                    .ok();
                    reported_nodes += unreported_nodes;
                    reports.send(Report::Progress {
                               search_id: search_id,
                               searched_nodes: reported_nodes,
                               depth: depth,
                           })
                           .ok();
                    reports.send(Report::Done {
                               search_id: search_id,
                               searched_nodes: reported_nodes,
                               value: value,
                           })
                           .ok();
                    move_stack.clear();
                }
                Command::Stop => continue,
                Command::Exit => break,
            }
        }
    })
}

// Represents a terminated search condition.
struct TerminatedSearch;

// Helper function for `run()`. It implements the principal variation
// search algorithm. When returning `Err(TerminatedSearch)`, this
// function may leave un-restored move lists in `moves`.
fn search(tt: &TranspositionTable,
          p: &mut Position,
          moves: &mut MoveStack,
          nc: &mut NodeCount,
          report: &mut FnMut(NodeCount) -> Result<(), TerminatedSearch>,
          mut alpha: Value, // lower bound
          beta: Value, // upper bound
          depth: u8)
          -> Result<Value, TerminatedSearch> {
    assert!(alpha < beta);
    if depth == 0 {
        // On leaf nodes, do quiescence search.
        let (value, nodes) = p.evaluate_quiescence(alpha, beta, None);
        *nc += nodes;
        Ok(value)
    } else {
        // Consult the transposition table.
        let hash_move16 = if let Some(entry) = tt.probe(p.hash()) {
            if entry.depth() >= depth {
                let value = entry.value();
                let bound = entry.bound();
                if bound == BOUND_EXACT {
                    return Ok(value);
                }
                if bound == BOUND_LOWER && value >= beta {
                    return Ok(beta);
                }
                if bound == BOUND_UPPER && value <= alpha {
                    return Ok(alpha);
                }
            }
            entry.move16()
        } else {
            0
        };

        moves.save();

        if hash_move16 != 0 {
            if let Some(m) = p.try_move_digest(hash_move16) {
                moves.push(m);
            }
        }
        let mut generated = false;

        let mut bound_type = BOUND_UPPER;
        let mut move16 = 0;
        let mut no_moves_yet = true;
        loop {
            // TODO: This is ugly, and probably inefficient.
            let m = match moves.remove_best_move() {
                None if generated => break,
                None => {
                    p.generate_moves(moves);
                    moves.remove_move(hash_move16);
                    generated = true;
                    continue;
                }
                Some(x) => x,
            };

            if p.do_move(m) {
                // From time to time, we report how many nodes had
                // been searched since the last report. This also
                // gives an opportunity for the search to be
                // terminated.
                *nc += 1;
                if *nc > NODE_COUNT_REPORT_INTERVAL {
                    try!(report(*nc));
                    *nc = 0;
                }

                let mut value = if no_moves_yet {
                    // The first move we analyze with a fully open
                    // window (alpha, beta).
                    -try!(search(tt, p, moves, nc, report, -beta, -alpha, depth - 1))
                } else {
                    // For the next moves we first try to prove that
                    // they are not better than our current best
                    // move. For this purpose we analyze them with a
                    // null window (alpha, alpha + 1). This is faster
                    // than a full window search. Only when we are
                    // certain that the move is better than our
                    // current best move, we do a full-window search.
                    match -try!(search(tt, p, moves, nc, report, -alpha - 1, -alpha, depth - 1)) {
                        x if x <= alpha => x,
                        _ => -try!(search(tt, p, moves, nc, report, -beta, -alpha, depth - 1)),
                    }
                };

                // A checkmate in fewer moves is better.
                if value > 20000 {
                    value -= 1;
                }
                if value < -20000 {
                    value += 1;
                }

                no_moves_yet = false;
                p.undo_move();

                if value >= beta {
                    // This move is too good, so that the opponent
                    // will not allow this line of play to
                    // happen. Therefore we can stop here.
                    alpha = beta;
                    bound_type = BOUND_LOWER;
                    move16 = m.digest();
                    break;
                }
                if value > alpha {
                    // We found ourselves a new best move.
                    alpha = value;
                    bound_type = BOUND_EXACT;
                    move16 = m.digest();
                }
            }
        }
        moves.restore();
        if no_moves_yet {
            // No legal moves -- this is a final position.
            alpha = p.evaluate_final();
            bound_type = BOUND_EXACT;
        }
        // TODO: Do not store values with too small `depth`.
        tt.store(p.hash(),
                 EntryData::new(alpha, bound_type, depth, move16, 0));
        Ok(alpha)
    }
}

const NODE_COUNT_REPORT_INTERVAL: NodeCount = 10000;

#[cfg(test)]
mod tests {
    use super::search;
    use chess_move::*;
    use tt::*;
    use position::Position;

    #[test]
    fn test_search() {
        let mut p = Position::from_fen("8/8/8/8/3q3k/7n/6PP/2Q2R1K b - - 0 1").ok().unwrap();
        let value = search(&TranspositionTable::new(),
                           &mut p,
                           &mut MoveStack::new(),
                           &mut 0,
                           &mut |_| Ok(()),
                           -30000,
                           30000,
                           2)
                        .ok()
                        .unwrap();
        assert!(value < -300);
        let value = search(&TranspositionTable::new(),
                           &mut p,
                           &mut MoveStack::new(),
                           &mut 0,
                           &mut |_| Ok(()),
                           -30000,
                           30000,
                           4)
                        .ok()
                        .unwrap();
        assert!(value >= 20000);
    }
}
<|endoftext|>


<commit_before>//! Macros to generate the implementation of the serialization traits for Rust
//! structs, as ASN.1 sequences.
//!
//! You can either use the shortcut `ber_sequence!` macro, or each of
//! `asn1_sequence_info!`, `ber_sequence_serialize!` and `ber_sequence_deserialize!`.
//!
//! ```
//! #[macro_use] extern crate asn1_cereal; fn main() {
//!   struct ShortSequence {
//!     z: u64,
//!     y: u32,
//!   }
//!
//!   ber_sequence!(
//!     ShortSequence,
//!     "SHORT_SEQUENCE",
//!     z;
//!     y;
//!   );
//!
//!   // OR
//!
//!   struct SomeSequence {
//!     a: u64,
//!     b: u32,
//!     c: String,
//!   }
//!
//!   asn1_sequence_info!(SomeSequence, "SOME_SEQUENCE");
//!   ber_sequence_serialize!(SomeSequence, a; b; c;);
//!   ber_sequence_deserialize!(SomeSequence, a; b; c;);
//! }
//! ```
//!
//! Note that these macros won't handle SEQUENCE OF.

#[macro_export]
/// This macro is a compact way of defining all three of the
/// Asn1 traits - Asn1Info, BerSerialize and BerDeserialize -
/// for a rust struct, that represents an ASN.1 sequence.
///
/// Note that the order the fields are placed in will affect the order
/// that they are encoded to, and decoded from ASN.1. If some form of
/// procedural macros are eventually stabilised, listing the fields
/// in the macro might no longer be required.
macro_rules! ber_sequence {
  ($rs_type:ident, $asn1_ty:expr, $($args:tt)*) => (
    asn1_sequence_info!($rs_type, $asn1_ty);
    ber_sequence_serialize!($rs_type, $($args)*);
    ber_sequence_deserialize!($rs_type, $($args)*);
  )
}

#[macro_export]
/// This macro defines the Asn1Info trait for a rust struct. This allows the other
/// traits to get information about this type. If you need to provide a custom
/// class or tag, consider using the asn1_info! macro.
macro_rules! asn1_sequence_info {
  ($rs_type:ident, $asn1_ty:expr) => (
    impl $crate::Asn1Info for $rs_type {
      fn asn1_tag() -> Option<$crate::tag::Tag> {
        Some($crate::tag::Tag {
          class: $crate::tag::Class::Universal,
          tagnum: (0x10 as u8).into(),
          constructed: true,
        })
      }

      fn asn1_type() -> $crate::tag::Type {
        $asn1_ty.into()
      }
    }
  )
}

#[macro_export]
/// This macro defines the BerSerialize trait for a rust struct. The code generated
/// will serialize the specified fields in the order that they are given.
macro_rules! ber_sequence_serialize {
  ($rs_type:ty, $($args:tt)*) => (
    impl $crate::BerSerialize for $rs_type {
      fn serialize_value<E: $crate::BerEncRules, W: std::io::Write>
          (&self, e: E, writer: &mut W) -> Result<(), $crate::err::EncodeError> {
        let mut bytes = Vec::new();
        let mut _count: u64 = 0;
        // For each declared sequence member, serialize it onto the stream.
        ber_sequence_serialize!(_ { self e writer bytes _count } $($args)*);
        Ok(())
      }
    }
  );

  // Handle field creation.
  // Expand a field with no options.
  (_ { $this:ident $e:ident $writer:ident $bytes:ident $count:ident }
      $item:ident; $($args:tt)*) => (
    ber_sequence_serialize!(_ { $this $e $writer $bytes $count } $item (); $($args)*);
  );
  // Expand an OPTIONAL field with no tag.
  (_ { $this:ident $e:ident $writer:ident $bytes:ident $count:ident }
      $item:ident (OPTIONAL); $($args:tt)*) => (
    ber_sequence_serialize!(_ { $this $e $writer $bytes $count } $item ([] OPTIONAL); $($args)*);
  );
  // Expand an OPTIONAL field with a tag.
  (_ { $this:ident $e:ident $writer:ident $bytes:ident $count:ident }
      $item:ident ([$($opts:tt)*] OPTIONAL); $($args:tt)*) => (
    let tag = asn1_spec_tag!({ $count } [$($opts)*]);
    if let Some(ref val) = $this.$item {
      ber_sequence_serialize!(_ { $this $e $writer $bytes $count tag, val } $($args)*);
    } else {
      ber_sequence_serialize!(_ { $this $e $writer $bytes $count } $($args)*);
    }
  );
  // Expand a field with a DEFAULT.
  (_ { $this:ident $e:ident $writer:ident $bytes:ident $count:ident }
      $item:ident (DEFAULT $default:expr); $($args:tt)*) => (
    ber_sequence_serialize!(_ { $this $e $writer $bytes $count } $item ([] DEFAULT $default); $($args)*);
  );
  // Expand a field with a DEFAULT and a tag.
  (_ { $this:ident $e:ident $writer:ident $bytes:ident $count:ident }
      $item:ident ([$($opts:tt)*] DEFAULT $default:expr); $($args:tt)*) => (
    let tag = asn1_spec_tag!({ $count } [$($opts)*]);
    if &$this.$item != &$default {
      ber_sequence_serialize!(_ { $this $e $writer $bytes $count tag, &$this.$item } $($args)*);
    } else {
      ber_sequence_serialize!(_ { $this $e $writer $bytes $count } $($args)*);
    }
  );
  // Expand a field that has options.
  (_ { $this:ident $e:ident $writer:ident $bytes:ident $count:ident }
      $item:ident ($($opts:tt)*); $($args:tt)*) => (
    let tag = asn1_spec_tag!({ $count } [$($opts)*]);

    // If encoding uses implicit tag, skip context-specific tag.
    if E::tag_rules() == $crate::ber::enc::TagEnc::Implicit {
      try!($crate::BerSerialize::serialize_enc(&$this.$item, $e, $writer));
      ber_sequence_serialize!(_ { $this $e $writer $bytes $count } $($args)*);
    // Otherwise encode the context-specific tag.
    } else {
      ber_sequence_serialize!(_ { $this $e $writer $bytes $count tag, &$this.$item } $($args)*);
    }
  );
  // Write a field with a tag.
  (_ { $this:ident $e:ident $writer:ident $bytes:ident $count:ident $tag:expr, $value:expr }
      $($args:tt)*) => (
    try!($crate::BerSerialize::serialize_enc($value, $e, &mut $bytes));
    let len: $crate::tag::Len = Some($bytes.len() as $crate::tag::LenNum).into();
    try!($crate::tag::write_taglen($tag, len, $writer));
    try!($writer.write_all(&mut $bytes));
    $bytes.clear();

    ber_sequence_serialize!(_ { $this $e $writer $bytes $count } $($args)*);
  );
  (_ { $($args:tt)* } ) => ();
}

#[macro_export]
/// This macro defines the BerDeserialize trait for a rust struct. The code generated
/// will deserialize the specified fields in the order that they are given.
macro_rules! ber_sequence_deserialize {
  // Handle fields.
  // Expand a field with no options.
  (_ { $rs_type:ident $e:ident $reader:ident $count:ident $tag:ident [$($fields:ident)*] }
      $item:ident; $($args:tt)*) => (
    ber_sequence_deserialize!(
      _ { $rs_type $e $reader $count $tag [$($fields)*] } $item ([]); $($args)*
    );
  );
  // Expand a field with empty options.
  (_ { $rs_type:ident $e:ident $reader:ident $count:ident $tag:ident [$($fields:ident)*] }
      $item:ident (); $($args:tt)*) => (
    ber_sequence_deserialize!(
      _ { $rs_type $e $reader $count $tag [$($fields)*] } $item ([]); $($args)*
    );
  );
  // Expand a field with empty options and a tag.
  (_ { $rs_type:ident $e:ident $reader:ident $count:ident $tag:ident [$($fields:ident)*] }
      $field:ident ([$($opts:tt)*]); $($args:tt)*) => (
    let $field = {
      let tag = $tag.unwrap_or(try!($crate::tag::Tag::read_tag($reader)));
      $tag = None;
      let our_tag = asn1_spec_tag!({ $count } [$($opts)*]);

      ber_sequence_deserialize!(_ { $rs_type $e $reader $count } tag, our_tag )
    };
    ber_sequence_deserialize!(_ { $rs_type $e $reader $count $tag [$($fields)* $field] } $($args)*);
  );
  // Expand an OPTIONAL field with no tag.
  (_ { $rs_type:ident $e:ident $reader:ident $count:ident $tag:ident [$($fields:ident)*] }
      $item:ident (OPTIONAL); $($args:tt)*) => (
    ber_sequence_deserialize!(
      _ { $rs_type $e $reader $count $tag [$($fields)*] } $item ([] OPTIONAL); $($args)*
    );
  );
  // Expand an OPTIONAL field with a tag.
  (_ { $rs_type:ident $e:ident $reader:ident $count:ident $tag:ident [$($fields:ident)*] }
      $field:ident ([$($opts:tt)*] OPTIONAL); $($args:tt)*) => (
    let $field = {
      let tag = match $tag {
        Some(t) => t,
        None => {
          let t = try!($crate::tag::Tag::read_tag($reader));
          $tag = Some(t); t
        },
      };

      let our_tag = asn1_spec_tag!({ $count } [$($opts)*]);

      if tag == our_tag {
        $tag = None;
        let _ = try!($crate::tag::Len::read_len($reader));
        Some(try!($crate::BerDeserialize::deserialize_enc($e, $reader)))
      } else {
        None
      }
    };
    ber_sequence_deserialize!(_ { $rs_type $e $reader $count $tag [$($fields)* $field] } $($args)*);
  );
  // Create the implementation for a field.
  (_ { $rs_type:ident $e:ident $reader:ident $count:ident } $tag:expr, $our_tag:expr) => ({
    // If encoding uses implicit tagging, throw an error if this isn't an implicit tag.
    if E::tag_rules() == $crate::ber::enc::TagEnc::Implicit && $tag == $our_tag {
      return Err($crate::err::DecodeError::ExplicitTag);
    }

    let len = try!($crate::tag::Len::read_len($reader));

    // If the tag matches our tag, decode the len and call the normal deserialize function.
    if $tag == $our_tag {
      // We don't have anything to do with the len, technically we should use it to
      // check the length decoded.
      let _ = len;
      try!($crate::BerDeserialize::deserialize_enc($e, $reader))
    // Otherwise decode it as the inner type. (We give the tag that we
    // decoded, and the function will decode the length itself).
    } else {
      try!($crate::BerDeserialize::deserialize_with_tag($e, $reader, $tag, len))
    }
  });
  // When no fields are left, build the struct.
  (_ { $rs_type:ident $e:ident $reader:ident $count:ident $tag:ident [ $($field:ident)* ] }) => (
    return Ok($rs_type { $(
      $field: $field
    ),* })
  );

  ($rs_type:ident, $($args:tt)*) => (
    impl $crate::BerDeserialize for $rs_type {
      fn deserialize_value<E: $crate::BerEncRules, I: Iterator<Item=std::io::Result<u8>>>
          (e: E, reader: &mut I, _: $crate::tag::Len) -> Result<Self, $crate::err::DecodeError> {
        let mut _count: u64 = 0;
        let mut _tag: Option<$crate::tag::Tag> = None;
        ber_sequence_deserialize!(_ { $rs_type e reader _count _tag [ ] } $($args)*);
      }
    }
  );
}
<commit_msg>Fix bugs in DEFAULT and OPTIONAL for sequences<commit_after>//! Macros to generate the implementation of the serialization traits for Rust
//! structs, as ASN.1 sequences.
//!
//! You can either use the shortcut `ber_sequence!` macro, or each of
//! `asn1_sequence_info!`, `ber_sequence_serialize!` and `ber_sequence_deserialize!`.
//!
//! ```
//! #[macro_use] extern crate asn1_cereal; fn main() {
//!   struct ShortSequence {
//!     z: u64,
//!     y: u32,
//!   }
//!
//!   ber_sequence!(
//!     ShortSequence,
//!     "SHORT_SEQUENCE",
//!     z;
//!     y;
//!   );
//!
//!   // OR
//!
//!   struct SomeSequence {
//!     a: u64,
//!     b: u32,
//!     c: String,
//!   }
//!
//!   asn1_sequence_info!(SomeSequence, "SOME_SEQUENCE");
//!   ber_sequence_serialize!(SomeSequence, a; b; c;);
//!   ber_sequence_deserialize!(SomeSequence, a; b; c;);
//! }
//! ```
//!
//! Note that these macros won't handle SEQUENCE OF.

#[macro_export]
/// This macro is a compact way of defining all three of the
/// Asn1 traits - Asn1Info, BerSerialize and BerDeserialize -
/// for a rust struct, that represents an ASN.1 sequence.
///
/// Note that the order the fields are placed in will affect the order
/// that they are encoded to, and decoded from ASN.1. If some form of
/// procedural macros are eventually stabilised, listing the fields
/// in the macro might no longer be required.
macro_rules! ber_sequence {
  ($rs_type:ident, $asn1_ty:expr, $($args:tt)*) => (
    asn1_sequence_info!($rs_type, $asn1_ty);
    ber_sequence_serialize!($rs_type, $($args)*);
    ber_sequence_deserialize!($rs_type, $($args)*);
  )
}

#[macro_export]
/// This macro defines the Asn1Info trait for a rust struct. This allows the other
/// traits to get information about this type. If you need to provide a custom
/// class or tag, consider using the asn1_info! macro.
macro_rules! asn1_sequence_info {
  ($rs_type:ident, $asn1_ty:expr) => (
    impl $crate::Asn1Info for $rs_type {
      fn asn1_tag() -> Option<$crate::tag::Tag> {
        Some($crate::tag::Tag {
          class: $crate::tag::Class::Universal,
          tagnum: (0x10 as u8).into(),
          constructed: true,
        })
      }

      fn asn1_type() -> $crate::tag::Type {
        $asn1_ty.into()
      }
    }
  )
}

#[macro_export]
/// This macro defines the BerSerialize trait for a rust struct. The code generated
/// will serialize the specified fields in the order that they are given.
macro_rules! ber_sequence_serialize {
  ($rs_type:ty, $($args:tt)*) => (
    impl $crate::BerSerialize for $rs_type {
      fn serialize_value<E: $crate::BerEncRules, W: std::io::Write>
          (&self, e: E, writer: &mut W) -> Result<(), $crate::err::EncodeError> {
        let mut bytes = Vec::new();
        let mut _count: u64 = 0;
        // For each declared sequence member, serialize it onto the stream.
        ber_sequence_serialize!(_ { self e writer bytes _count } $($args)*);
        Ok(())
      }
    }
  );

  // Handle field creation.
  // Expand a field with no options.
  (_ { $this:ident $e:ident $writer:ident $bytes:ident $count:ident }
      $item:ident; $($args:tt)*) => (
    ber_sequence_serialize!(_ { $this $e $writer $bytes $count } $item (); $($args)*);
  );
  // Expand an OPTIONAL field with no tag.
  (_ { $this:ident $e:ident $writer:ident $bytes:ident $count:ident }
      $item:ident (OPTIONAL); $($args:tt)*) => (
    ber_sequence_serialize!(_ { $this $e $writer $bytes $count } $item ([] OPTIONAL); $($args)*);
  );
  // Expand an OPTIONAL field with a tag.
  (_ { $this:ident $e:ident $writer:ident $bytes:ident $count:ident }
      $item:ident ([$($opts:tt)*] OPTIONAL); $($args:tt)*) => (
    let tag = asn1_spec_tag!({ $count } [$($opts)*]);
    if let Some(ref val) = $this.$item {
      ber_sequence_serialize!(_ { $this $e $writer $bytes $count tag, val } $($args)*);
    } else {
      ber_sequence_serialize!(_ { $this $e $writer $bytes $count } $($args)*);
    }
  );
  // Expand a field with a DEFAULT.
  (_ { $this:ident $e:ident $writer:ident $bytes:ident $count:ident }
      $item:ident (DEFAULT $default:expr); $($args:tt)*) => (
    ber_sequence_serialize!(_ { $this $e $writer $bytes $count } $item ([] DEFAULT $default); $($args)*);
  );
  // Expand a field with a DEFAULT and a tag.
  (_ { $this:ident $e:ident $writer:ident $bytes:ident $count:ident }
      $item:ident ([$($opts:tt)*] DEFAULT $default:expr); $($args:tt)*) => (
    let tag = asn1_spec_tag!({ $count } [$($opts)*]);
    if &$this.$item != &$default {
      ber_sequence_serialize!(_ { $this $e $writer $bytes $count tag, &$this.$item } $($args)*);
    } else {
      ber_sequence_serialize!(_ { $this $e $writer $bytes $count } $($args)*);
    }
  );
  // Expand a field that has options.
  (_ { $this:ident $e:ident $writer:ident $bytes:ident $count:ident }
      $item:ident ($($opts:tt)*); $($args:tt)*) => (
    let tag = asn1_spec_tag!({ $count } [$($opts)*]);

    // If encoding uses implicit tag, skip context-specific tag.
    if E::tag_rules() == $crate::ber::enc::TagEnc::Implicit {
      try!($crate::BerSerialize::serialize_enc(&$this.$item, $e, $writer));
      ber_sequence_serialize!(_ { $this $e $writer $bytes $count } $($args)*);
    // Otherwise encode the context-specific tag.
    } else {
      ber_sequence_serialize!(_ { $this $e $writer $bytes $count tag, &$this.$item } $($args)*);
    }
  );
  // Write a field with a tag.
  (_ { $this:ident $e:ident $writer:ident $bytes:ident $count:ident $tag:expr, $value:expr }
      $($args:tt)*) => (
    try!($crate::BerSerialize::serialize_enc($value, $e, &mut $bytes));
    let len: $crate::tag::Len = Some($bytes.len() as $crate::tag::LenNum).into();
    try!($crate::tag::write_taglen($tag, len, $writer));
    try!($writer.write_all(&mut $bytes));
    $bytes.clear();

    ber_sequence_serialize!(_ { $this $e $writer $bytes $count } $($args)*);
  );
  (_ { $($args:tt)* } ) => ();
}

#[macro_export]
/// This macro defines the BerDeserialize trait for a rust struct. The code generated
/// will deserialize the specified fields in the order that they are given.
macro_rules! ber_sequence_deserialize {
  // Handle fields.
  // Expand a field with no options.
  (_ { $rs_type:ident $e:ident $reader:ident $count:ident $tag:ident [$($fields:ident)*] }
      $item:ident; $($args:tt)*) => (
    ber_sequence_deserialize!(
      _ { $rs_type $e $reader $count $tag [$($fields)*] } $item ([]); $($args)*
    );
  );
  // Expand a field with empty options.
  (_ { $rs_type:ident $e:ident $reader:ident $count:ident $tag:ident [$($fields:ident)*] }
      $item:ident (); $($args:tt)*) => (
    ber_sequence_deserialize!(
      _ { $rs_type $e $reader $count $tag [$($fields)*] } $item ([]); $($args)*
    );
  );
  // Expand a field with empty options and a tag.
  (_ { $rs_type:ident $e:ident $reader:ident $count:ident $tag:ident [$($fields:ident)*] }
      $field:ident ([$($opts:tt)*]); $($args:tt)*) => (
    let $field = {
      let tag = if let Some(t) = $tag {
        $tag = None;
        t
      } else {
        try!($crate::tag::Tag::read_tag($reader))
      };
      let our_tag = asn1_spec_tag!({ $count } [$($opts)*]);

      ber_sequence_deserialize!(_ { $rs_type $e $reader $count } tag, our_tag )
    };
    ber_sequence_deserialize!(_ { $rs_type $e $reader $count $tag [$($fields)* $field] } $($args)*);
  );
  // Expand an OPTIONAL field with no tag.
  (_ { $rs_type:ident $e:ident $reader:ident $count:ident $tag:ident [$($fields:ident)*] }
      $item:ident (OPTIONAL); $($args:tt)*) => (
    ber_sequence_deserialize!(
      _ { $rs_type $e $reader $count $tag [$($fields)*] } $item ([] OPTIONAL); $($args)*
    );
  );
  // Expand an OPTIONAL field with a tag.
  (_ { $rs_type:ident $e:ident $reader:ident $count:ident $tag:ident [$($fields:ident)*] }
      $field:ident ([$($opts:tt)*] OPTIONAL); $($args:tt)*) => (
    let $field = {
      let tag = match $tag {
        Some(t) => t,
        None => {
          let t = try!($crate::tag::Tag::read_tag($reader));
          $tag = Some(t); t
        },
      };

      let our_tag = asn1_spec_tag!({ $count } [$($opts)*]);

      if tag == our_tag {
        $tag = None;
        let _ = try!($crate::tag::Len::read_len($reader));
        Some(try!($crate::BerDeserialize::deserialize_enc($e, $reader)))
      } else {
        None
      }
    };
    ber_sequence_deserialize!(_ { $rs_type $e $reader $count $tag [$($fields)* $field] } $($args)*);
  );
  // Expand a DEFAULT field with no tag.
  (_ { $rs_type:ident $e:ident $reader:ident $count:ident $tag:ident [$($fields:ident)*] }
      $item:ident (DEFAULT $default:expr); $($args:tt)*) => (
    ber_sequence_deserialize!(
      _ { $rs_type $e $reader $count $tag [$($fields)*] } $item ([] DEFAULT $default); $($args)*
    );
  );
  // Expand a DEFAULT field with a tag.
  (_ { $rs_type:ident $e:ident $reader:ident $count:ident $tag:ident [$($fields:ident)*] }
      $field:ident ([$($opts:tt)*] DEFAULT $default:expr); $($args:tt)*) => (
    let $field = {
      let tag = match $tag {
        Some(t) => t,
        None => {
          let t = try!($crate::tag::Tag::read_tag($reader));
          $tag = Some(t); t
        },
      };

      let our_tag = asn1_spec_tag!({ $count } [$($opts)*]);

      if tag == our_tag {
        $tag = None;
        let _ = try!($crate::tag::Len::read_len($reader));
        try!($crate::BerDeserialize::deserialize_enc($e, $reader))
      } else {
        $default
      }
    };
    ber_sequence_deserialize!(_ { $rs_type $e $reader $count $tag [$($fields)* $field] } $($args)*);
  );
  // Create the implementation for a field.
  (_ { $rs_type:ident $e:ident $reader:ident $count:ident } $tag:expr, $our_tag:expr) => ({
    // If encoding uses implicit tagging, throw an error if this isn't an implicit tag.
    if E::tag_rules() == $crate::ber::enc::TagEnc::Implicit && $tag == $our_tag {
      return Err($crate::err::DecodeError::ExplicitTag);
    }

    let len = try!($crate::tag::Len::read_len($reader));

    // If the tag matches our tag, decode the len and call the normal deserialize function.
    if $tag == $our_tag {
      // We don't have anything to do with the len, technically we should use it to
      // check the length decoded.
      let _ = len;
      try!($crate::BerDeserialize::deserialize_enc($e, $reader))
    // Otherwise decode it as the inner type. (We give the tag that we
    // decoded, and the function will decode the length itself).
    } else {
      try!($crate::BerDeserialize::deserialize_with_tag($e, $reader, $tag, len))
    }
  });
  // When no fields are left, build the struct.
  (_ { $rs_type:ident $e:ident $reader:ident $count:ident $tag:ident [ $($field:ident)* ] }) => (
    return Ok($rs_type { $(
      $field: $field
    ),* })
  );

  ($rs_type:ident, $($args:tt)*) => (
    impl $crate::BerDeserialize for $rs_type {
      fn deserialize_value<E: $crate::BerEncRules, I: Iterator<Item=std::io::Result<u8>>>
          (e: E, reader: &mut I, _: $crate::tag::Len) -> Result<Self, $crate::err::DecodeError> {
        let mut _count: u64 = 0;
        let mut _tag: Option<$crate::tag::Tag> = None;
        ber_sequence_deserialize!(_ { $rs_type e reader _count _tag [ ] } $($args)*);
      }
    }
  );
}
<|endoftext|>


<commit_before>use std::cmp::Ordering;

use cmp;

pub fn borrow(num: &str, needed: char) -> Result<String, &'static str> {
    let chars = num.as_bytes();
    let mut _ret: Result<String, &str> = Err("FIXME1");
    for i in (0..chars.len()).rev() {
        match cmp(chars[i] as char, needed) {
            Ordering::Less => {
                //nop
            },
            Ordering::Equal => {
                _ret = Ok(num.to_string());
                break;
            },
            Ordering::Greater => {
                let (prefix, end) = num.split_at(i);
                let (expand_char, suffix) = end.split_at(1);
                match expand_char {
                    "X" => {
                        if needed == 'I' {
                            _ret = Ok(prefix.to_string() + "VIIIII");
                        } else {
                            _ret = Ok(prefix.to_string() + "VV" + suffix);
                        }
                    },
                    "V" => {
                        _ret = Ok(prefix.to_string() + "IIIII");
                    },
                    _ => {
                        _ret = Err("unknown digit");
                    }
                }
                break;
            }
        }
    }

    _ret
}

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

    #[test]
    fn cannot_get_something_from_nothing() {
        assert!(borrow("", 'I').is_err());
    }

    #[test]
    fn borrow_i_from_ii_returns_string_without_change() {
        assert_eq!("II", borrow("II", 'I').unwrap());
    }

    #[test]
    fn borrow_i_from_v() {
        assert_eq!("IIIII", borrow("V", 'I').unwrap());
    }

    #[test]
    fn borrow_v_from_x() {
        assert_eq!("VV", borrow("X", 'V').unwrap());
    }

    #[test]
    fn borrow_v_from_cx() {
        assert_eq!("CVV", borrow("CX", 'V').unwrap());
    }

    #[test]
    fn borrow_i_from_x_borrows_transitively() {
        assert_eq!("VIIIII", borrow("X", 'I').unwrap());
    }

    #[test]
    fn borrow_v_from_xx_only_borrows_once() {
        assert_eq!("XVV", borrow("XX", 'V').unwrap());
    }

    #[test]
    fn borrow_v_from_cxi_returns_the_tail_part_properly() {
        assert_eq!("CVVI", borrow("CXI", 'V').unwrap());
    }
}
<commit_msg>Refactoring borrow to use rfind instead of a loop<commit_after>use std::cmp::Ordering;

use cmp;

pub fn borrow(num: &str, needed: char) -> Result<String, &'static str> {
    match num.rfind(|c| cmp(c, needed) != Ordering::Less) {
        Some(i) => {
            let (prefix, end) = num.split_at(i);
            let (expand_char, suffix) = end.split_at(1);
            match expand_char {
                "X" => {
                    if needed == 'I' {
                        Ok(prefix.to_string() + "VIIIII")
                    } else {
                        Ok(prefix.to_string() + "VV" + suffix)
                    }
                },
                "V" => {
                    Ok(prefix.to_string() + "IIIII")
                },
                "I" => {
                    Ok(num.into())
                },
                _ => {
                    Err("unknown digit")
                }
            }
        },
        None => {
            Err("Would be a negative number")
        }
    }
}

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

    #[test]
    fn cannot_get_something_from_nothing() {
        assert!(borrow("", 'I').is_err());
    }

    #[test]
    fn borrow_i_from_ii_returns_string_without_change() {
        assert_eq!("II", borrow("II", 'I').unwrap());
    }

    #[test]
    fn borrow_i_from_v() {
        assert_eq!("IIIII", borrow("V", 'I').unwrap());
    }

    #[test]
    fn borrow_v_from_x() {
        assert_eq!("VV", borrow("X", 'V').unwrap());
    }

    #[test]
    fn borrow_v_from_cx() {
        assert_eq!("CVV", borrow("CX", 'V').unwrap());
    }

    #[test]
    fn borrow_i_from_x_borrows_transitively() {
        assert_eq!("VIIIII", borrow("X", 'I').unwrap());
    }

    #[test]
    fn borrow_v_from_xx_only_borrows_once() {
        assert_eq!("XVV", borrow("XX", 'V').unwrap());
    }

    #[test]
    fn borrow_v_from_cxi_returns_the_tail_part_properly() {
        assert_eq!("CVVI", borrow("CXI", 'V').unwrap());
    }
}
<|endoftext|>