<commit_before>use std::collections::BTreeMap;

use chrono::DateTime;
use chrono::UTC;
use serde_json::value::Value;
use uuid::Uuid;

use error::TaskError;
use error::TaskErrorKind;
use priority::TaskPriority;
use result::Result;

pub type Project = String;
pub type Status  = String;
pub type Tag     = String;
pub type Urgency = f64;

#[derive(Debug, Clone)]
pub struct Task {
    id: u64,
    desc: String,
    entry: DateTime<UTC>,
    modified: Option<DateTime<UTC>>,
    priority: Option<TaskPriority>,
    project: Option<Project>,
    status: Status,
    tags: Vec<Tag>,
    uuid: Uuid,
    urgency: Urgency,
}

/*
 * TODO: We do not fail if the JSON parsing fails. This panics. We rely on taskwarrior to be nice
 * to us. I guess this should be fixed.
 */
impl Task {

    pub fn new(id: u64,
                desc: String,
                entry: DateTime<UTC>,
                modified: Option<DateTime<UTC>>,
                priority: Option<TaskPriority>,
                project: Option<Project>,
                status: Status,
                tags: Vec<Tag>,
                uuid: Uuid,
                urgency: Urgency)
        -> Task
    {
        Task {
            id       : id,
            desc     : desc,
            entry    : entry,
            modified : modified,
            priority : priority,
            project  : project,
            status   : status,
            tags     : tags,
            uuid     : uuid,
            urgency  : urgency,
        }
    }

    pub fn from_value(v: Value) -> Result<Task> {
        if !v.is_object() {
            trace!("JSON Value is not an object, cannot parse into Task");
            return Err(TaskError::new(TaskErrorKind::ParserError, None));
        }
        trace!("Found object");

        let map = v.as_object().unwrap();
        trace!("map = {:?}", map);

        let entry_dt = get_entry(&map);
        trace!("entry_dt = {:?}", entry_dt);
        if entry_dt.is_err() {
            return Err(entry_dt.err().unwrap());
        }
        let entry_dt = entry_dt.unwrap();

        Ok(Task {
            id       : get_id(&map),
            desc     : get_desc(&map),
            entry    : entry_dt,
            modified : get_modified(&map),
            priority : get_priority(&map),
            project  : get_project(&map),
            status   : get_status(&map),
            tags     : get_tags(&map),
            uuid     : get_uuid(&map),
            urgency  : get_urgency(&map),
        })
    }

    pub fn id(&self) -> u64 {
        self.id
    }

    pub fn desc(&self) -> &String {
        &self.desc
    }

    pub fn entry(&self) -> &DateTime<UTC> {
        &self.entry
    }

    pub fn modified(&self) -> Option<&DateTime<UTC>> {
        self.modified.as_ref()
    }

    pub fn priority(&self) -> Option<&TaskPriority> {
        self.priority.as_ref()
    }

    pub fn project(&self) -> Option<&Project> {
        self.project.as_ref()
    }

    pub fn status(&self) -> &Status {
        &self.status
    }

    pub fn tags(&self) -> &Vec<Tag> {
        &self.tags
    }

    pub fn uuid(&self) -> &Uuid {
        &self.uuid
    }

    pub fn urgency(&self) -> Urgency {
        self.urgency
    }

}

impl Into<Value> for Task {

    fn into(self) -> Value {
        let id       = Value::U64(self.id);
        let desc     = Value::String(self.desc);
        let status   = Value::String(self.status);
        let tags     = Value::Array(self.tags
                                        .into_iter()
                                        .map(|s| Value::String(String::from(s)))
                                        .collect());
        let uuid     = Value::String(format!("{}", self.uuid));
        let urgency  = Value::F64(self.urgency);

        let mut map = BTreeMap::new();
        map.insert(String::from("id")       , id);
        map.insert(String::from("desc")     , desc);
        {
            let v = Value::String(format!("{:?}", self.entry));
            map.insert(String::from("entry"), v);
        }
        if self.modified.is_some() {
            let v = Value::String(format!("{:?}", self.modified.unwrap()));
            map.insert(String::from("modified"), v);
        }
        if self.priority.is_some() {
            map.insert(String::from("priority") , Value::String(self.priority.unwrap().into()));
        }
        if self.project.is_some() {
            map.insert(String::from("project"), Value::String(self.project.unwrap()));
        }
        map.insert(String::from("status")   , status);
        map.insert(String::from("tags")     , tags);
        map.insert(String::from("uuid")     , uuid);
        map.insert(String::from("urgency")  , urgency);

        Value::Object(map)
    }

}

fn get_id(map: &BTreeMap<String, Value>) -> u64 {
    map.get("id").unwrap().as_u64().unwrap()
}

fn get_desc(map: &BTreeMap<String, Value>) -> String {
    map.get("description").unwrap().as_string().map(String::from).unwrap()
}

fn get_entry(map: &BTreeMap<String, Value>) -> Result<DateTime<UTC>> {
    if let Some(s) = map.get("entry").unwrap().as_string() {
        trace!("Found 'entry': {:?}", s);
        String::from(s)
            .parse::<DateTime<UTC>>()
            .map_err(|e| TaskError::new(TaskErrorKind::ParserError, Some(Box::new(e))))
    } else {
        trace!("No 'entry'");
        Err(TaskError::new(TaskErrorKind::ParserError, None))
    }
}

fn get_modified(map: &BTreeMap<String, Value>) -> Option<DateTime<UTC>> {
    map.get("modified")
        .and_then(|m| m.as_string())
        .and_then(|s| String::from(s).parse::<DateTime<UTC>>().ok())
}

fn get_priority(map: &BTreeMap<String, Value>) -> Option<TaskPriority> {
    map.get("priority")
        .map(|m| m.as_string().map(String::from).unwrap())
        .map(|s| TaskPriority::from(&s[..]))
}

fn get_project(map: &BTreeMap<String, Value>) -> Option<String> {
    map.get("project")
        .map(|p| p.as_string().map(String::from).unwrap())
}

fn get_status(map: &BTreeMap<String, Value>) -> String {
    map.get("status").unwrap().as_string().map(String::from).unwrap()
}

fn get_tags(map: &BTreeMap<String, Value>) -> Vec<Tag> {
    map.get("tags")
        .unwrap()
        .as_array()
        .unwrap()
        .clone()
        .into_iter()
        .map(|item| item.as_string().map(String::from).unwrap())
        .collect()
}

fn get_uuid(map: &BTreeMap<String, Value>) -> Uuid {
    map.get("uuid")
        .unwrap()
        .as_string()
        .and_then(|s| Uuid::parse_str(s).ok())
        .unwrap()
}

fn get_urgency(map: &BTreeMap<String, Value>) -> f64 {
    map.get("urgency").unwrap().as_f64().unwrap()
}

#[cfg(test)]
mod test {
    extern crate env_logger;
    extern crate uuid;

    use chrono::DateTime;
    use chrono::UTC;
    use uuid::Uuid;

    use core::reader::Reader;
    use core::reader::JsonObjectReader;
    use super::Task;
    use priority::TaskPriority;
    use std::borrow::Borrow;

    #[test]
    fn test_from_json() {
        env_logger::init().unwrap();

        let json = String::from("{\"id\":1,\"description\":\"desc\",\"entry\":\"20150612T164806Z\",\"modified\":\"20160315T215656Z\",\"priority\":\"L\",\"project\":\"someproj\",\"status\":\"pending\",\"tags\":[\"test\",\"task\"],\"uuid\":\"93cfc5fa-2f0c-44e6-bede-c2b1ca7ceff3\",\"urgency\":1.0}");
        let bytes = json.into_bytes();
        let mut reader = JsonObjectReader::new(Reader::new(bytes.borrow()));

        let v = reader.next();
        assert!(v.is_some());
        let v = v.unwrap();

        debug!("{:?}", v);
        let t = Task::from_value(v);
        debug!("{:?}", t);
        assert!(t.is_ok());
        let t = t.unwrap();

        debug!("Task created successfully!");

        assert_eq!(t.id(), 1);
        assert_eq!(t.desc().clone(), String::from("desc"));
        assert_eq!(t.entry().clone(), String::from("20150612T164806Z").parse::<DateTime<UTC>>().unwrap());
        assert_eq!(t.modified().clone(), String::from("20160315T215656Z").parse::<DateTime<UTC>>().ok().as_ref());
        assert_eq!(t.priority(), Some(&TaskPriority::Low));
        assert_eq!(t.project().clone(), Some(&String::from("someproj")));
        assert_eq!(t.status().clone(), String::from("pending"));
        for n in ["test", "task"].iter() {
            assert!(t.tags().contains(&String::from(*n)));
        }
        assert_eq!(t.uuid().clone(), Uuid::parse_str("93cfc5fa-2f0c-44e6-bede-c2b1ca7ceff3").unwrap());
        assert_eq!(t.urgency() as u64, 1.0 as u64);
    }

}
<commit_msg>Reimplement date parsing with chrono<commit_after>use std::collections::BTreeMap;

use chrono::naive::datetime::NaiveDateTime;
use chrono::offset::TimeZone;
use chrono::offset::fixed::FixedOffset;
use chrono::UTC;
use serde_json::value::Value;
use uuid::Uuid;

use error::TaskError;
use error::TaskErrorKind;
use priority::TaskPriority;
use result::Result;

pub type Project = String;
pub type Status  = String;
pub type Tag     = String;
pub type Urgency = f64;

pub static TASKWARRIOR_DATETIME_TEMPLATE : &'static str = "%Y%m%dT%H%M%SZ";

#[derive(Debug, Clone)]
pub struct Task {
    id: u64,
    desc: String,
    entry: NaiveDateTime,
    modified: Option<NaiveDateTime>,
    priority: Option<TaskPriority>,
    project: Option<Project>,
    status: Status,
    tags: Vec<Tag>,
    uuid: Uuid,
    urgency: Urgency,
}

/*
 * TODO: We do not fail if the JSON parsing fails. This panics. We rely on taskwarrior to be nice
 * to us. I guess this should be fixed.
 */
impl Task {

    pub fn new(id: u64,
                desc: String,
                entry: NaiveDateTime,
                modified: Option<NaiveDateTime>,
                priority: Option<TaskPriority>,
                project: Option<Project>,
                status: Status,
                tags: Vec<Tag>,
                uuid: Uuid,
                urgency: Urgency)
        -> Task
    {
        Task {
            id       : id,
            desc     : desc,
            entry    : entry,
            modified : modified,
            priority : priority,
            project  : project,
            status   : status,
            tags     : tags,
            uuid     : uuid,
            urgency  : urgency,
        }
    }

    pub fn from_value(v: Value) -> Result<Task> {
        if !v.is_object() {
            trace!("JSON Value is not an object, cannot parse into Task");
            return Err(TaskError::new(TaskErrorKind::ParserError, None));
        }
        trace!("Found object");

        let map = v.as_object().unwrap();
        trace!("map = {:?}", map);

        let entry_dt = get_entry(&map);
        trace!("entry_dt = {:?}", entry_dt);
        if entry_dt.is_err() {
            return Err(entry_dt.err().unwrap());
        }
        let entry_dt = entry_dt.unwrap();

        let modified_dt = match get_modified(&map) {
            Some(Err(e)) => return Err(e),
            Some(Ok(x))  => Some(x),
            None         => None,
        };

        Ok(Task {
            id       : get_id(&map),
            desc     : get_desc(&map),
            entry    : entry_dt,
            modified : modified_dt,
            priority : get_priority(&map),
            project  : get_project(&map),
            status   : get_status(&map),
            tags     : get_tags(&map),
            uuid     : get_uuid(&map),
            urgency  : get_urgency(&map),
        })
    }

    pub fn id(&self) -> u64 {
        self.id
    }

    pub fn desc(&self) -> &String {
        &self.desc
    }

    pub fn entry(&self) -> &NaiveDateTime {
        &self.entry
    }

    pub fn modified(&self) -> Option<&NaiveDateTime> {
        self.modified.as_ref()
    }

    pub fn priority(&self) -> Option<&TaskPriority> {
        self.priority.as_ref()
    }

    pub fn project(&self) -> Option<&Project> {
        self.project.as_ref()
    }

    pub fn status(&self) -> &Status {
        &self.status
    }

    pub fn tags(&self) -> &Vec<Tag> {
        &self.tags
    }

    pub fn uuid(&self) -> &Uuid {
        &self.uuid
    }

    pub fn urgency(&self) -> Urgency {
        self.urgency
    }

}

impl Into<Value> for Task {

    fn into(self) -> Value {
        let id       = Value::U64(self.id);
        let desc     = Value::String(self.desc);
        let status   = Value::String(self.status);
        let tags     = Value::Array(self.tags
                                        .into_iter()
                                        .map(|s| Value::String(String::from(s)))
                                        .collect());
        let uuid     = Value::String(format!("{}", self.uuid));
        let urgency  = Value::F64(self.urgency);

        let mut map = BTreeMap::new();
        map.insert(String::from("id")       , id);
        map.insert(String::from("desc")     , desc);
        {
            let v = Value::String(format!("{:?}", self.entry));
            map.insert(String::from("entry"), v);
        }
        if self.modified.is_some() {
            let v = Value::String(format!("{:?}", self.modified.unwrap()));
            map.insert(String::from("modified"), v);
        }
        if self.priority.is_some() {
            map.insert(String::from("priority") , Value::String(self.priority.unwrap().into()));
        }
        if self.project.is_some() {
            map.insert(String::from("project"), Value::String(self.project.unwrap()));
        }
        map.insert(String::from("status")   , status);
        map.insert(String::from("tags")     , tags);
        map.insert(String::from("uuid")     , uuid);
        map.insert(String::from("urgency")  , urgency);

        Value::Object(map)
    }

}

fn get_id(map: &BTreeMap<String, Value>) -> u64 {
    map.get("id").unwrap().as_u64().unwrap()
}

fn get_desc(map: &BTreeMap<String, Value>) -> String {
    map.get("description").unwrap().as_string().map(String::from).unwrap()
}

fn get_entry(map: &BTreeMap<String, Value>) -> Result<NaiveDateTime> {
    if let Some(s) = map.get("entry").unwrap().as_string() {
        trace!("Found 'entry': {:?}", s);
        NaiveDateTime::parse_from_str(s, TASKWARRIOR_DATETIME_TEMPLATE)
            .map_err(|e| TaskError::new(TaskErrorKind::ParserError, Some(Box::new(e))))
    } else {
        trace!("No 'entry'");
        Err(TaskError::new(TaskErrorKind::ParserError, None))
    }
}

fn get_modified(map: &BTreeMap<String, Value>) -> Option<Result<NaiveDateTime>> {
    if let Some(modif) = map.get("modified").and_then(|m| m.as_string()) {
        Some(NaiveDateTime::parse_from_str(modif, TASKWARRIOR_DATETIME_TEMPLATE)
            .map_err(|e| TaskError::new(TaskErrorKind::ParserError, Some(Box::new(e)))))

    } else {
        None
    }
}

fn get_priority(map: &BTreeMap<String, Value>) -> Option<TaskPriority> {
    map.get("priority")
        .map(|m| m.as_string().map(String::from).unwrap())
        .map(|s| TaskPriority::from(&s[..]))
}

fn get_project(map: &BTreeMap<String, Value>) -> Option<String> {
    map.get("project")
        .map(|p| p.as_string().map(String::from).unwrap())
}

fn get_status(map: &BTreeMap<String, Value>) -> String {
    map.get("status").unwrap().as_string().map(String::from).unwrap()
}

fn get_tags(map: &BTreeMap<String, Value>) -> Vec<Tag> {
    map.get("tags")
        .unwrap()
        .as_array()
        .unwrap()
        .clone()
        .into_iter()
        .map(|item| item.as_string().map(String::from).unwrap())
        .collect()
}

fn get_uuid(map: &BTreeMap<String, Value>) -> Uuid {
    map.get("uuid")
        .unwrap()
        .as_string()
        .and_then(|s| Uuid::parse_str(s).ok())
        .unwrap()
}

fn get_urgency(map: &BTreeMap<String, Value>) -> f64 {
    map.get("urgency").unwrap().as_f64().unwrap()
}

#[cfg(test)]
mod test {
    extern crate env_logger;
    extern crate uuid;

    use chrono::NaiveDateTime;
    use chrono::UTC;
    use uuid::Uuid;

    use core::reader::Reader;
    use core::reader::JsonObjectReader;
    use super::Task;
    use super::TASKWARRIOR_DATETIME_TEMPLATE;
    use priority::TaskPriority;
    use std::borrow::Borrow;

    use std::error::Error;

    #[test]
    fn test_from_json() {
        env_logger::init().unwrap();

        let json = String::from("{\"id\":1,\"description\":\"desc\",\"entry\":\"20150612T164806Z\",\"modified\":\"20160315T215656Z\",\"priority\":\"L\",\"project\":\"someproj\",\"status\":\"pending\",\"tags\":[\"test\",\"task\"],\"uuid\":\"93cfc5fa-2f0c-44e6-bede-c2b1ca7ceff3\",\"urgency\":1.0}");
        let bytes = json.into_bytes();
        let mut reader = JsonObjectReader::new(Reader::new(bytes.borrow()));

        let v = reader.next();
        assert!(v.is_some());
        let v = v.unwrap();

        debug!("{:?}", v);
        let t = Task::from_value(v);
        debug!("{:?}", t);
        assert!(t.is_ok());
        let t = t.unwrap();

        debug!("Task created successfully!");

        assert_eq!(t.id(), 1);
        assert_eq!(t.desc().clone(), String::from("desc"));
        assert_eq!(t.entry().clone(), NaiveDateTime::parse_from_str("20150612T164806Z", TASKWARRIOR_DATETIME_TEMPLATE).unwrap());
        assert_eq!(t.modified().clone(), NaiveDateTime::parse_from_str("20160315T215656Z", TASKWARRIOR_DATETIME_TEMPLATE).ok().as_ref());
        assert_eq!(t.priority(), Some(&TaskPriority::Low));
        assert_eq!(t.project().clone(), Some(&String::from("someproj")));
        assert_eq!(t.status().clone(), String::from("pending"));
        for n in ["test", "task"].iter() {
            assert!(t.tags().contains(&String::from(*n)));
        }
        assert_eq!(t.uuid().clone(), Uuid::parse_str("93cfc5fa-2f0c-44e6-bede-c2b1ca7ceff3").unwrap());
        assert_eq!(t.urgency() as u64, 1.0 as u64);
    }

}
<|endoftext|>


<commit_before>use openssl::ssl::{self, SslMethod, SslConnectorBuilder};
use std::io::{Read, Write};
use std::net::{TcpStream, TcpListener};
use std::thread;

use super::*;

#[test]
fn connect_google() {
    let builder = TlsConnector::builder().unwrap().build().unwrap();
    let s = TcpStream::connect("google.com:443").unwrap();
    let mut socket = builder.connect("google.com", s).unwrap();

    socket.write_all(b"GET / HTTP/1.0\r\n\r\n").unwrap();
    let mut result = vec![];
    socket.read_to_end(&mut result).unwrap();

    println!("{}", String::from_utf8_lossy(&result));
    assert!(result.starts_with(b"HTTP/1.0"));
    assert!(result.ends_with(b"\r\n") || result.ends_with(b""));
}

#[test]
fn connect_bad_hostname() {
    let builder = TlsConnector::builder().unwrap().build().unwrap();
    let s = TcpStream::connect("wrong.host.badssl.com:443").unwrap();
    assert!(builder.connect("wrong.host.badssl.com", s).is_err());
}

#[test]
fn server() {
    let buf = include_bytes!("../test/identity.p12");
    let pkcs12 = Pkcs12::from_der(buf, "mypass").unwrap();
    let builder = TlsAcceptor::builder(pkcs12).unwrap().build().unwrap();

    let listener = TcpListener::bind("0.0.0.0:0").unwrap();
    let port = listener.local_addr().unwrap().port();

    thread::spawn(move || {
        let socket = listener.accept().unwrap().0;
        let mut socket = builder.accept(socket).unwrap();

        let mut buf = [0; 5];
        socket.read_exact(&mut buf).unwrap();
        assert_eq!(&buf, b"hello");

        socket.write_all(b"world").unwrap();
    });

    let socket = TcpStream::connect(("localhost", port)).unwrap();
    let mut builder = SslConnectorBuilder::new(SslMethod::tls()).unwrap();
    builder.builder_mut().set_ca_file("test/root-ca.pem").unwrap();
    let connector = builder.build();
    let mut socket = connector.connect("foobar.com", socket).unwrap();
    println!("{}", socket.ssl().current_cipher().unwrap().description());
    socket.write_all(b"hello").unwrap();
    let mut buf = vec![];
    socket.read_to_end(&mut buf).unwrap();
    assert_eq!(buf, b"world");
}

#[test]
fn server_tls11_only() {
    let buf = include_bytes!("../test/identity.p12");
    let pkcs12 = Pkcs12::from_der(buf, "mypass").unwrap();
    let mut builder = TlsAcceptor::builder(pkcs12).unwrap();
    builder.supported_protocols(&[Protocol::Tlsv11]).unwrap();
    let builder = builder.build().unwrap();

    let listener = TcpListener::bind("0.0.0.0:0").unwrap();
    let port = listener.local_addr().unwrap().port();

    let j = thread::spawn(move || {
        let socket = listener.accept().unwrap().0;
        let mut socket = builder.accept(socket).unwrap();

        let mut buf = [0; 5];
        socket.read_exact(&mut buf).unwrap();
        assert_eq!(&buf, b"hello");

        socket.write_all(b"world").unwrap();
    });

    let socket = TcpStream::connect(("localhost", port)).unwrap();
    let mut builder = SslConnectorBuilder::new(SslMethod::tls()).unwrap();
    builder.builder_mut().set_ca_file("test/root-ca.pem").unwrap();
    let options = ssl::SSL_OP_NO_SSLV3 | ssl::SSL_OP_NO_TLSV1 | ssl::SSL_OP_NO_TLSV1_2;
    builder.builder_mut().set_options(options);
    let connector = builder.build();
    let mut socket = connector.connect("foobar.com", socket).unwrap();
    println!("{}", socket.ssl().current_cipher().unwrap().description());
    socket.write_all(b"hello").unwrap();
    let mut buf = vec![];
    socket.read_to_end(&mut buf).unwrap();
    assert_eq!(buf, b"world");

    j.join().unwrap();
}

#[test]
fn server_no_shared_protocol() {
    let buf = include_bytes!("../test/identity.p12");
    let pkcs12 = Pkcs12::from_der(buf, "mypass").unwrap();
    let mut builder = TlsAcceptor::builder(pkcs12).unwrap();
    builder.supported_protocols(&[Protocol::Tlsv11]).unwrap();
    let builder = builder.build().unwrap();

    let listener = TcpListener::bind("0.0.0.0:0").unwrap();
    let port = listener.local_addr().unwrap().port();

    let j = thread::spawn(move || {
        let socket = listener.accept().unwrap().0;
        assert!(builder.accept(socket).is_err());
    });

    let socket = TcpStream::connect(("localhost", port)).unwrap();
    let mut builder = SslConnectorBuilder::new(SslMethod::tls()).unwrap();
    builder.builder_mut().set_ca_file("test/root-ca.pem").unwrap();
    let options = ssl::SSL_OP_NO_TLSV1_1;
    builder.builder_mut().set_options(options);
    let connector = builder.build();
    assert!(connector.connect("foobar.com", socket).is_err());

    j.join().unwrap();
}

#[test]
fn shutdown() {
    let buf = include_bytes!("../test/identity.p12");
    let pkcs12 = Pkcs12::from_der(buf, "mypass").unwrap();
    let builder = TlsAcceptor::builder(pkcs12).unwrap().build().unwrap();

    let listener = TcpListener::bind("0.0.0.0:0").unwrap();
    let port = listener.local_addr().unwrap().port();

    let j = thread::spawn(move || {
        let socket = listener.accept().unwrap().0;
        let mut socket = builder.accept(socket).unwrap();

        let mut buf = [0; 5];
        socket.read_exact(&mut buf).unwrap();
        assert_eq!(&buf, b"hello");

        assert_eq!(socket.read(&mut buf).unwrap(), 0);
    });

    let socket = TcpStream::connect(("localhost", port)).unwrap();
    let mut builder = SslConnectorBuilder::new(SslMethod::tls()).unwrap();
    builder.builder_mut().set_ca_file("test/root-ca.pem").unwrap();
    let connector = builder.build();
    let mut socket = connector.connect("foobar.com", socket).unwrap();
    println!("{}", socket.ssl().current_cipher().unwrap().description());
    socket.write_all(b"hello").unwrap();
    socket.shutdown().unwrap();

    j.join().unwrap();
}
<commit_msg>Stop unwrapping in tests<commit_after>use openssl::ssl::{self, SslMethod, SslConnectorBuilder};
use std::io::{Read, Write};
use std::net::{TcpStream, TcpListener};
use std::thread;

use super::*;

macro_rules! p {
    ($e:expr) => {
        match $e {
            Ok(r) => r,
            Err(e) => panic!("{:?}", e),
        }
    }
}

#[test]
fn connect_google() {
    let builder = p!(TlsConnector::builder());
    let builder = p!(builder.build());
    let s = p!(TcpStream::connect("google.com:443"));
    let mut socket = p!(builder.connect("google.com", s));

    p!(socket.write_all(b"GET / HTTP/1.0\r\n\r\n"));
    let mut result = vec![];
    p!(socket.read_to_end(&mut result));

    println!("{}", String::from_utf8_lossy(&result));
    assert!(result.starts_with(b"HTTP/1.0"));
    assert!(result.ends_with(b"\r\n") || result.ends_with(b""));
}

#[test]
fn connect_bad_hostname() {
    let builder = p!(TlsConnector::builder());
    let builder = p!(builder.build());
    let s = p!(TcpStream::connect("wrong.host.badssl.com:443"));
    assert!(builder.connect("wrong.host.badssl.com", s).is_err());
}

#[test]
fn server() {
    let buf = include_bytes!("../test/identity.p12");
    let pkcs12 = p!(Pkcs12::from_der(buf, "mypass"));
    let builder = p!(TlsAcceptor::builder(pkcs12));
    let builder = p!(builder.build());

    let listener = p!(TcpListener::bind("0.0.0.0:0"));
    let port = p!(listener.local_addr()).port();

    let j = thread::spawn(move || {
        let socket = p!(listener.accept()).0;
        let mut socket = p!(builder.accept(socket));

        let mut buf = [0; 5];
        p!(socket.read_exact(&mut buf));
        assert_eq!(&buf, b"hello");

        p!(socket.write_all(b"world"));
    });

    let socket = p!(TcpStream::connect(("localhost", port)));
    let mut builder = p!(SslConnectorBuilder::new(SslMethod::tls()));
    p!(builder.builder_mut().set_ca_file("test/root-ca.pem"));
    let connector = builder.build();
    let mut socket = p!(connector.connect("foobar.com", socket));
    println!("{}", socket.ssl().current_cipher().unwrap().description());
    p!(socket.write_all(b"hello"));
    let mut buf = vec![];
    p!(socket.read_to_end(&mut buf));
    assert_eq!(buf, b"world");

    p!(j.join());
}

#[test]
fn server_tls11_only() {
    let buf = include_bytes!("../test/identity.p12");
    let pkcs12 = p!(Pkcs12::from_der(buf, "mypass"));
    let mut builder = p!(TlsAcceptor::builder(pkcs12));
    p!(builder.supported_protocols(&[Protocol::Tlsv11]));
    let builder = p!(builder.build());

    let listener = p!(TcpListener::bind("0.0.0.0:0"));
    let port = p!(listener.local_addr()).port();

    let j = thread::spawn(move || {
        let socket = p!(listener.accept()).0;
        let mut socket = p!(builder.accept(socket));

        let mut buf = [0; 5];
        p!(socket.read_exact(&mut buf));
        assert_eq!(&buf, b"hello");

        p!(socket.write_all(b"world"));
    });

    let socket = p!(TcpStream::connect(("localhost", port)));
    let mut builder = p!(SslConnectorBuilder::new(SslMethod::tls()));
    p!(builder.builder_mut().set_ca_file("test/root-ca.pem"));
    let options = ssl::SSL_OP_NO_SSLV3 | ssl::SSL_OP_NO_TLSV1 | ssl::SSL_OP_NO_TLSV1_2;
    builder.builder_mut().set_options(options);
    let connector = builder.build();
    let mut socket = p!(connector.connect("foobar.com", socket));
    println!("{}", socket.ssl().current_cipher().unwrap().description());
    p!(socket.write_all(b"hello"));
    let mut buf = vec![];
    p!(socket.read_to_end(&mut buf));
    assert_eq!(buf, b"world");

    p!(j.join());
}

#[test]
fn server_no_shared_protocol() {
    let buf = include_bytes!("../test/identity.p12");
    let pkcs12 = p!(Pkcs12::from_der(buf, "mypass"));
    let mut builder = p!(TlsAcceptor::builder(pkcs12));
    p!(builder.supported_protocols(&[Protocol::Tlsv11]));
    let builder = p!(builder.build());

    let listener = p!(TcpListener::bind("0.0.0.0:0"));
    let port = p!(listener.local_addr()).port();

    let j = thread::spawn(move || {
        let socket = p!(listener.accept()).0;
        assert!(builder.accept(socket).is_err());
    });

    let socket = p!(TcpStream::connect(("localhost", port)));
    let mut builder = p!(SslConnectorBuilder::new(SslMethod::tls()));
    p!(builder.builder_mut().set_ca_file("test/root-ca.pem"));
    let options = ssl::SSL_OP_NO_TLSV1_1;
    builder.builder_mut().set_options(options);
    let connector = builder.build();
    assert!(connector.connect("foobar.com", socket).is_err());

    p!(j.join());
}

#[test]
fn shutdown() {
    let buf = include_bytes!("../test/identity.p12");
    let pkcs12 = p!(Pkcs12::from_der(buf, "mypass"));
    let builder = p!(TlsAcceptor::builder(pkcs12));
    let builder = p!(builder.build());

    let listener = p!(TcpListener::bind("0.0.0.0:0"));
    let port = p!(listener.local_addr()).port();

    let j = thread::spawn(move || {
        let socket = p!(listener.accept()).0;
        let mut socket = p!(builder.accept(socket));

        let mut buf = [0; 5];
        p!(socket.read_exact(&mut buf));
        assert_eq!(&buf, b"hello");

        assert_eq!(p!(socket.read(&mut buf)), 0);
    });

    let socket = p!(TcpStream::connect(("localhost", port)));
    let mut builder = p!(SslConnectorBuilder::new(SslMethod::tls()));
    p!(builder.builder_mut().set_ca_file("test/root-ca.pem"));
    let connector = builder.build();
    let mut socket = p!(connector.connect("foobar.com", socket));
    println!("{}", socket.ssl().current_cipher().unwrap().description());
    p!(socket.write_all(b"hello"));
    p!(socket.shutdown());

    p!(j.join());
}
<|endoftext|>


<commit_before>// Copyright (c) Aptos
// SPDX-License-Identifier: Apache-2.0

use anyhow::{anyhow, Result};
use aptos_rest_client::{
    aptos_api_types::{IdentifierWrapper, MoveResource, WriteSetChange},
    Client as RestClient, Transaction, VersionedNewBlockEvent,
};
use aptos_types::account_address::AccountAddress;
use std::str::FromStr;

#[derive(Eq, PartialEq, Clone, Copy, Debug)]
pub struct ValidatorInfo {
    pub address: AccountAddress,
    pub voting_power: u64,
    pub validator_index: u64,
}

pub struct EpochInfo {
    pub epoch: u64,
    pub blocks: Vec<VersionedNewBlockEvent>,
    pub validators: Vec<ValidatorInfo>,
    pub partial: bool,
}

pub struct FetchMetadata {}

impl FetchMetadata {
    fn get_validator_addresses(
        data: &MoveResource,
        field_name: &str,
    ) -> Result<Vec<ValidatorInfo>> {
        fn extract_validator_address(validator: &serde_json::Value) -> Result<ValidatorInfo> {
            Ok(ValidatorInfo {
                address: AccountAddress::from_hex_literal(
                    validator.get("addr").unwrap().as_str().unwrap(),
                )
                .map_err(|e| anyhow!("Cannot parse address {:?}", e))?,
                voting_power: validator
                    .get("voting_power")
                    .unwrap()
                    .as_str()
                    .unwrap()
                    .parse()
                    .map_err(|e| anyhow!("Cannot parse voting_power {:?}", e))?,
                validator_index: validator
                    .get("config")
                    .unwrap()
                    .get("validator_index")
                    .unwrap()
                    .as_str()
                    .unwrap()
                    .parse()
                    .map_err(|e| anyhow!("Cannot parse validator_index {:?}", e))?,
            })
        }

        let validators_json = data
            .data
            .0
            .get(&IdentifierWrapper::from_str(field_name).unwrap())
            .unwrap();
        if let serde_json::Value::Array(validators_array) = validators_json {
            let mut validators: Vec<ValidatorInfo> = vec![];
            for validator in validators_array {
                validators.push(extract_validator_address(validator)?);
            }
            Ok(validators)
        } else {
            Err(anyhow!("{} validators not in json", field_name))
        }
    }

    async fn get_transactions(
        client: &RestClient,
        start: u64,
        last: u64,
    ) -> Result<Vec<Transaction>> {
        let mut result = Vec::new();
        let mut cursor = start;
        while cursor < last {
            let limit = std::cmp::min(1000, last - cursor);
            let mut current = client
                .get_transactions(Some(cursor), Some(limit as u16))
                .await?
                .into_inner();
            result.append(&mut current);
            cursor += limit
        }
        Ok(result)
    }

    fn get_validators_from_transaction(transaction: &Transaction) -> Result<Vec<ValidatorInfo>> {
        if let Ok(info) = transaction.transaction_info() {
            for change in &info.changes {
                if let WriteSetChange::WriteResource(resource) = change {
                    if resource.data.typ.name.0.as_str() == "ValidatorSet" {
                        // No pending at epoch change
                        assert_eq!(
                            Vec::<ValidatorInfo>::new(),
                            FetchMetadata::get_validator_addresses(
                                &resource.data,
                                "pending_inactive"
                            )?
                        );
                        assert_eq!(
                            Vec::<ValidatorInfo>::new(),
                            FetchMetadata::get_validator_addresses(
                                &resource.data,
                                "pending_active"
                            )?
                        );
                        return FetchMetadata::get_validator_addresses(
                            &resource.data,
                            "active_validators",
                        );
                    }
                }
            }
        }
        Err(anyhow!("Couldn't find ValidatorSet in the transaction"))
    }

    pub async fn fetch_new_block_events(
        client: &RestClient,
        start_epoch: Option<i64>,
        end_epoch: Option<i64>,
    ) -> Result<Vec<EpochInfo>> {
        let (last_events, state) = client
            .get_new_block_events_bcs(None, Some(1))
            .await?
            .into_parts();
        let mut start_seq_num = state.oldest_block_height;
        assert_eq!(last_events.len(), 1, "{:?}", last_events);
        let last_event = last_events.first().unwrap();
        let last_seq_num = last_event.sequence_number;

        let wanted_start_epoch = {
            let mut wanted_start_epoch = start_epoch.unwrap_or(2);
            if wanted_start_epoch < 0 {
                wanted_start_epoch = last_event.event.epoch() as i64 + wanted_start_epoch + 1;
            }

            let oldest_event = client
                .get_new_block_events_bcs(Some(start_seq_num), Some(1))
                .await?
                .into_inner()
                .into_iter()
                .next()
                .ok_or_else(|| anyhow!("No blocks at oldest_block_height {}", start_seq_num))?;
            let oldest_fetchable_epoch = std::cmp::max(oldest_event.event.epoch() + 1, 2);
            if oldest_fetchable_epoch > wanted_start_epoch as u64 {
                println!(
                    "Oldest full epoch that can be retreived is {} ",
                    oldest_fetchable_epoch
                );
                oldest_fetchable_epoch
            } else {
                wanted_start_epoch as u64
            }
        };
        let wanted_end_epoch = {
            let mut wanted_end_epoch = end_epoch.unwrap_or(i64::MAX);
            if wanted_end_epoch < 0 {
                wanted_end_epoch = last_event.event.epoch() as i64 + wanted_end_epoch + 1;
            }
            std::cmp::min(
                last_event.event.epoch() + 1,
                std::cmp::max(2, wanted_end_epoch) as u64,
            )
        };

        if wanted_start_epoch > 2 {
            let mut search_end = last_seq_num;

            // Stop when search is close enough, and we can then linearly
            // proceed from there.
            // Since we are ignoring results we are fetching during binary search
            // we want to stop when we are close.
            while start_seq_num + 20 < search_end {
                let mid = (start_seq_num + search_end) / 2;

                let mid_epoch = client
                    .get_new_block_events_bcs(Some(mid), Some(1))
                    .await?
                    .into_inner()
                    .first()
                    .unwrap()
                    .event
                    .epoch();

                if mid_epoch < wanted_start_epoch {
                    start_seq_num = mid;
                } else {
                    search_end = mid;
                }
            }
        }

        let batch: u16 = 100;
        let mut batch_index = 0;

        println!(
            "Fetching {} to {} sequence number, wanting epochs [{}, {}), last version: {} and epoch: {}",
            start_seq_num, last_seq_num, wanted_start_epoch, wanted_end_epoch, state.version, state.epoch,
        );
        let mut result: Vec<EpochInfo> = vec![];
        if wanted_start_epoch >= wanted_end_epoch {
            return Ok(result);
        }

        let mut validators: Vec<ValidatorInfo> = vec![];
        let mut current: Vec<VersionedNewBlockEvent> = vec![];
        let mut epoch = 0;

        let mut cursor = start_seq_num;
        loop {
            let events = client
                .get_new_block_events_bcs(Some(cursor), Some(batch))
                .await;

            if events.is_err() {
                println!(
                    "Failed to read new_block_events beyond {}, stopping. {:?}",
                    cursor,
                    events.unwrap_err()
                );
                assert!(!validators.is_empty());
                result.push(EpochInfo {
                    epoch,
                    blocks: current,
                    validators: validators.clone(),
                    partial: true,
                });
                return Ok(result);
            }

            for event in events.unwrap().into_inner() {
                if event.event.epoch() > epoch {
                    if epoch == 0 {
                        epoch = event.event.epoch();
                        current = vec![];
                    } else {
                        let last = current.last().cloned();
                        if let Some(last) = last {
                            let transactions = FetchMetadata::get_transactions(
                                client,
                                last.version,
                                event.version,
                            )
                            .await?;
                            assert_eq!(
                                transactions.first().unwrap().version().unwrap(),
                                last.version
                            );
                            for transaction in transactions {
                                if let Ok(new_validators) =
                                    FetchMetadata::get_validators_from_transaction(&transaction)
                                {
                                    if epoch >= wanted_start_epoch {
                                        assert!(!validators.is_empty());
                                        result.push(EpochInfo {
                                            epoch,
                                            blocks: current,
                                            validators: validators.clone(),
                                            partial: false,
                                        });
                                    }
                                    current = vec![];

                                    validators = new_validators;
                                    validators.sort_by_key(|v| v.validator_index);
                                    assert_eq!(epoch + 1, event.event.epoch());
                                    epoch = event.event.epoch();
                                    if epoch >= wanted_end_epoch {
                                        return Ok(result);
                                    }
                                    break;
                                }
                            }
                            assert!(
                                current.is_empty(),
                                "Couldn't find ValidatorSet change for transactions start={}, limit={} for epoch {}",
                                last.version,
                                event.version - last.version,
                                event.event.epoch(),
                            );
                        }
                    }
                }
                current.push(event);
            }

            cursor += u64::from(batch);
            batch_index += 1;
            if batch_index % 100 == 0 {
                println!(
                    "Fetched {} epochs (in epoch {} with {} blocks) from {} NewBlockEvents",
                    result.len(),
                    epoch,
                    current.len(),
                    cursor
                );
            }

            if cursor > last_seq_num {
                if !validators.is_empty() {
                    result.push(EpochInfo {
                        epoch,
                        blocks: current,
                        validators: validators.clone(),
                        partial: true,
                    });
                }
                return Ok(result);
            }
        }
    }
}
<commit_msg>Make fetching work when api can return less than limit<commit_after>// Copyright (c) Aptos
// SPDX-License-Identifier: Apache-2.0

use anyhow::{anyhow, Result};
use aptos_rest_client::{
    aptos_api_types::{IdentifierWrapper, MoveResource, WriteSetChange},
    Client as RestClient, Transaction, VersionedNewBlockEvent,
};
use aptos_types::account_address::AccountAddress;
use std::str::FromStr;

const MAX_FETCH_BATCH_SIZE: u16 = 1000;

#[derive(Eq, PartialEq, Clone, Copy, Debug)]
pub struct ValidatorInfo {
    pub address: AccountAddress,
    pub voting_power: u64,
    pub validator_index: u64,
}

pub struct EpochInfo {
    pub epoch: u64,
    pub blocks: Vec<VersionedNewBlockEvent>,
    pub validators: Vec<ValidatorInfo>,
    pub partial: bool,
}

pub struct FetchMetadata {}

impl FetchMetadata {
    fn get_validator_addresses(
        data: &MoveResource,
        field_name: &str,
    ) -> Result<Vec<ValidatorInfo>> {
        fn extract_validator_address(validator: &serde_json::Value) -> Result<ValidatorInfo> {
            Ok(ValidatorInfo {
                address: AccountAddress::from_hex_literal(
                    validator.get("addr").unwrap().as_str().unwrap(),
                )
                .map_err(|e| anyhow!("Cannot parse address {:?}", e))?,
                voting_power: validator
                    .get("voting_power")
                    .unwrap()
                    .as_str()
                    .unwrap()
                    .parse()
                    .map_err(|e| anyhow!("Cannot parse voting_power {:?}", e))?,
                validator_index: validator
                    .get("config")
                    .unwrap()
                    .get("validator_index")
                    .unwrap()
                    .as_str()
                    .unwrap()
                    .parse()
                    .map_err(|e| anyhow!("Cannot parse validator_index {:?}", e))?,
            })
        }

        let validators_json = data
            .data
            .0
            .get(&IdentifierWrapper::from_str(field_name).unwrap())
            .unwrap();
        if let serde_json::Value::Array(validators_array) = validators_json {
            let mut validators: Vec<ValidatorInfo> = vec![];
            for validator in validators_array {
                validators.push(extract_validator_address(validator)?);
            }
            Ok(validators)
        } else {
            Err(anyhow!("{} validators not in json", field_name))
        }
    }

    async fn get_transactions_in_range(
        client: &RestClient,
        start: u64,
        last: u64,
    ) -> Result<Vec<Transaction>> {
        let mut result = Vec::new();
        let mut cursor = start;
        while cursor < last {
            let limit = std::cmp::min(MAX_FETCH_BATCH_SIZE as u64, last - cursor) as u16;
            let mut current = client
                .get_transactions(Some(cursor), Some(limit as u16))
                .await?
                .into_inner();
            if current.is_empty() {
                return Err(anyhow!(
                    "No transactions returned with start={} and limit={}",
                    cursor,
                    limit
                ));
            }
            cursor += current.len() as u64;
            result.append(&mut current);
        }
        Ok(result)
    }

    fn get_validators_from_transaction(transaction: &Transaction) -> Result<Vec<ValidatorInfo>> {
        if let Ok(info) = transaction.transaction_info() {
            for change in &info.changes {
                if let WriteSetChange::WriteResource(resource) = change {
                    if resource.data.typ.name.0.as_str() == "ValidatorSet" {
                        // No pending at epoch change
                        assert_eq!(
                            Vec::<ValidatorInfo>::new(),
                            FetchMetadata::get_validator_addresses(
                                &resource.data,
                                "pending_inactive"
                            )?
                        );
                        assert_eq!(
                            Vec::<ValidatorInfo>::new(),
                            FetchMetadata::get_validator_addresses(
                                &resource.data,
                                "pending_active"
                            )?
                        );
                        return FetchMetadata::get_validator_addresses(
                            &resource.data,
                            "active_validators",
                        );
                    }
                }
            }
        }
        Err(anyhow!("Couldn't find ValidatorSet in the transaction"))
    }

    pub async fn fetch_new_block_events(
        client: &RestClient,
        start_epoch: Option<i64>,
        end_epoch: Option<i64>,
    ) -> Result<Vec<EpochInfo>> {
        let (last_events, state) = client
            .get_new_block_events_bcs(None, Some(1))
            .await?
            .into_parts();
        let mut start_seq_num = state.oldest_block_height;
        assert_eq!(last_events.len(), 1, "{:?}", last_events);
        let last_event = last_events.first().unwrap();
        let last_seq_num = last_event.sequence_number;

        let wanted_start_epoch = {
            let mut wanted_start_epoch = start_epoch.unwrap_or(2);
            if wanted_start_epoch < 0 {
                wanted_start_epoch = last_event.event.epoch() as i64 + wanted_start_epoch + 1;
            }

            let oldest_event = client
                .get_new_block_events_bcs(Some(start_seq_num), Some(1))
                .await?
                .into_inner()
                .into_iter()
                .next()
                .ok_or_else(|| anyhow!("No blocks at oldest_block_height {}", start_seq_num))?;
            let oldest_fetchable_epoch = std::cmp::max(oldest_event.event.epoch() + 1, 2);
            if oldest_fetchable_epoch > wanted_start_epoch as u64 {
                println!(
                    "Oldest full epoch that can be retreived is {} ",
                    oldest_fetchable_epoch
                );
                oldest_fetchable_epoch
            } else {
                wanted_start_epoch as u64
            }
        };
        let wanted_end_epoch = {
            let mut wanted_end_epoch = end_epoch.unwrap_or(i64::MAX);
            if wanted_end_epoch < 0 {
                wanted_end_epoch = last_event.event.epoch() as i64 + wanted_end_epoch + 1;
            }
            std::cmp::min(
                last_event.event.epoch() + 1,
                std::cmp::max(2, wanted_end_epoch) as u64,
            )
        };

        if wanted_start_epoch > 2 {
            let mut search_end = last_seq_num;

            // Stop when search is close enough, and we can then linearly
            // proceed from there.
            // Since we are ignoring results we are fetching during binary search
            // we want to stop when we are close.
            while start_seq_num + 20 < search_end {
                let mid = (start_seq_num + search_end) / 2;

                let mid_epoch = client
                    .get_new_block_events_bcs(Some(mid), Some(1))
                    .await?
                    .into_inner()
                    .first()
                    .unwrap()
                    .event
                    .epoch();

                if mid_epoch < wanted_start_epoch {
                    start_seq_num = mid;
                } else {
                    search_end = mid;
                }
            }
        }

        let mut batch_index = 0;

        println!(
            "Fetching {} to {} sequence number, wanting epochs [{}, {}), last version: {} and epoch: {}",
            start_seq_num, last_seq_num, wanted_start_epoch, wanted_end_epoch, state.version, state.epoch,
        );
        let mut result: Vec<EpochInfo> = vec![];
        if wanted_start_epoch >= wanted_end_epoch {
            return Ok(result);
        }

        let mut validators: Vec<ValidatorInfo> = vec![];
        let mut current: Vec<VersionedNewBlockEvent> = vec![];
        let mut epoch = 0;

        let mut cursor = start_seq_num;
        loop {
            let response = client
                .get_new_block_events_bcs(Some(cursor), Some(MAX_FETCH_BATCH_SIZE))
                .await;

            if response.is_err() {
                println!(
                    "Failed to read new_block_events beyond {}, stopping. {:?}",
                    cursor,
                    response.unwrap_err()
                );
                assert!(!validators.is_empty());
                result.push(EpochInfo {
                    epoch,
                    blocks: current,
                    validators: validators.clone(),
                    partial: true,
                });
                return Ok(result);
            }
            let events = response.unwrap().into_inner();

            if events.is_empty() {
                return Err(anyhow!(
                    "No transactions returned with start={} and limit={}",
                    cursor,
                    MAX_FETCH_BATCH_SIZE
                ));
            }

            cursor += events.len() as u64;
            batch_index += 1;

            for event in events {
                if event.event.epoch() > epoch {
                    if epoch == 0 {
                        epoch = event.event.epoch();
                        current = vec![];
                    } else {
                        let last = current.last().cloned();
                        if let Some(last) = last {
                            let transactions = FetchMetadata::get_transactions_in_range(
                                client,
                                last.version,
                                event.version,
                            )
                            .await?;
                            assert_eq!(
                                transactions.first().unwrap().version().unwrap(),
                                last.version
                            );
                            for transaction in transactions {
                                if let Ok(new_validators) =
                                    FetchMetadata::get_validators_from_transaction(&transaction)
                                {
                                    if epoch >= wanted_start_epoch {
                                        assert!(!validators.is_empty());
                                        result.push(EpochInfo {
                                            epoch,
                                            blocks: current,
                                            validators: validators.clone(),
                                            partial: false,
                                        });
                                    }
                                    current = vec![];

                                    validators = new_validators;
                                    validators.sort_by_key(|v| v.validator_index);
                                    assert_eq!(epoch + 1, event.event.epoch());
                                    epoch = event.event.epoch();
                                    if epoch >= wanted_end_epoch {
                                        return Ok(result);
                                    }
                                    break;
                                }
                            }
                            assert!(
                                current.is_empty(),
                                "Couldn't find ValidatorSet change for transactions start={}, limit={} for epoch {}",
                                last.version,
                                event.version - last.version,
                                event.event.epoch(),
                            );
                        }
                    }
                }
                current.push(event);
            }

            if batch_index % 100 == 0 {
                println!(
                    "Fetched {} epochs (in epoch {} with {} blocks) from {} NewBlockEvents",
                    result.len(),
                    epoch,
                    current.len(),
                    cursor
                );
            }

            if cursor > last_seq_num {
                if !validators.is_empty() {
                    result.push(EpochInfo {
                        epoch,
                        blocks: current,
                        validators: validators.clone(),
                        partial: true,
                    });
                }
                return Ok(result);
            }
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use crate::{Error as EngineError, IncludeCppEngine, RebuildDependencyRecorder};
use proc_macro2::TokenStream;
use quote::ToTokens;
use std::{fmt::Display, io::Read, path::PathBuf};
use std::{panic::UnwindSafe, path::Path, rc::Rc};
use syn::Item;

/// Errors which may occur when parsing a Rust source file to discover
/// and interpret include_cxx macros.
#[derive(Debug)]
pub enum ParseError {
    /// Unable to open the source file
    FileOpen(std::io::Error),
    /// The .rs file couldn't be read.
    FileRead(std::io::Error),
    /// The .rs file couldn't be parsed.
    Syntax(syn::Error),
    /// The include CPP macro could not be expanded into
    /// Rust bindings to C++, because of some problem during the conversion
    /// process. This could be anything from a C++ parsing error to some
    /// C++ feature that autocxx can't yet handle and isn't able to skip
    /// over. It could also cover errors in your syntax of the `include_cpp`
    /// macro or the directives inside.
    AutocxxCodegenError(EngineError),
}

impl Display for ParseError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            ParseError::FileOpen(err) => write!(f, "Unable to open file: {}", err)?,
            ParseError::FileRead(err) => write!(f, "Unable to read file: {}", err)?,
            ParseError::Syntax(err) => write!(f, "Syntax error parsing Rust file: {}", err)?,
            ParseError::AutocxxCodegenError(err) => {
                write!(f, "Unable to parse include_cpp! macro: {}", err)?
            }
        }
        Ok(())
    }
}

/// Parse a Rust file, and spot any include_cpp macros within it.
pub fn parse_file<P1: AsRef<Path>>(rs_file: P1) -> Result<ParsedFile, ParseError> {
    let mut source = String::new();
    let mut file = std::fs::File::open(rs_file).map_err(ParseError::FileOpen)?;
    file.read_to_string(&mut source)
        .map_err(ParseError::FileRead)?;
    proc_macro2::fallback::force();
    let source = syn::parse_file(&source).map_err(ParseError::Syntax)?;
    parse_file_contents(source)
}

fn parse_file_contents(source: syn::File) -> Result<ParsedFile, ParseError> {
    let mut results = Vec::new();
    for item in source.items {
        if let Item::Macro(ref mac) = item {
            if mac.mac.path.is_ident("include_cpp") {
                let include_cpp = crate::IncludeCppEngine::new_from_syn(mac.mac.clone())
                    .map_err(ParseError::AutocxxCodegenError)?;
                results.push(Segment::Autocxx(include_cpp));
                continue;
            }
        }
        results.push(Segment::Other(item));
    }
    Ok(ParsedFile(results))
}

/// A Rust file parsed by autocxx. May contain zero or more autocxx 'engines',
/// i.e. the `IncludeCpp` class, corresponding to zero or more include_cpp
/// macros within this file. Also contains `syn::Item` structures for all
/// the rest of the Rust code, such that it can be reconstituted if necessary.
pub struct ParsedFile(Vec<Segment>);

#[allow(clippy::large_enum_variant)]
enum Segment {
    Autocxx(IncludeCppEngine),
    Other(Item),
}

impl ParsedFile {
    /// Get all the autocxxes in this parsed file.
    pub fn get_autocxxes(&self) -> impl Iterator<Item = &IncludeCppEngine> {
        self.0.iter().filter_map(|s| match s {
            Segment::Autocxx(includecpp) => Some(includecpp),
            Segment::Other(_) => None,
        })
    }

    pub fn get_autocxxes_mut(&mut self) -> impl Iterator<Item = &mut IncludeCppEngine> {
        self.0.iter_mut().filter_map(|s| match s {
            Segment::Autocxx(includecpp) => Some(includecpp),
            Segment::Other(_) => None,
        })
    }

    pub fn resolve_all(
        &mut self,
        autocxx_inc: Vec<PathBuf>,
        extra_clang_args: &[&str],
        dep_recorder: Option<Box<dyn RebuildDependencyRecorder>>,
    ) -> Result<(), ParseError> {
        let inner_dep_recorder: Option<Rc<dyn RebuildDependencyRecorder>> =
            dep_recorder.map(Rc::from);
        for include_cpp in self.get_autocxxes_mut() {
            #[allow(clippy::manual_map)] // because of dyn shenanigans
            let dep_recorder: Option<Box<dyn RebuildDependencyRecorder>> = match &inner_dep_recorder
            {
                None => None,
                Some(inner_dep_recorder) => Some(Box::new(CompositeDepRecorder::new(
                    inner_dep_recorder.clone(),
                ))),
            };
            include_cpp
                .generate(autocxx_inc.clone(), extra_clang_args, dep_recorder)
                .map_err(ParseError::AutocxxCodegenError)?
        }
        Ok(())
    }
}

impl ToTokens for ParsedFile {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        for seg in &self.0 {
            match seg {
                Segment::Other(item) => item.to_tokens(tokens),
                Segment::Autocxx(autocxx) => {
                    let these_tokens = autocxx.generate_rs();
                    tokens.extend(these_tokens);
                }
            }
        }
    }
}

/// Shenanigans required to share the same RebuildDependencyRecorder
/// with all of the include_cpp instances in this one file.
#[derive(Debug, Clone)]
struct CompositeDepRecorder(Rc<dyn RebuildDependencyRecorder>);

impl CompositeDepRecorder {
    fn new(inner: Rc<dyn RebuildDependencyRecorder>) -> Self {
        CompositeDepRecorder(inner)
    }
}

impl UnwindSafe for CompositeDepRecorder {}

impl RebuildDependencyRecorder for CompositeDepRecorder {
    fn record_header_file_dependency(&self, filename: &str) {
        self.0.record_header_file_dependency(filename);
    }
}
<commit_msg>Handle autocxx::include_cpp! directly<commit_after>// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use crate::{Error as EngineError, IncludeCppEngine, RebuildDependencyRecorder};
use proc_macro2::TokenStream;
use quote::ToTokens;
use std::{fmt::Display, io::Read, path::PathBuf};
use std::{panic::UnwindSafe, path::Path, rc::Rc};
use syn::Item;

/// Errors which may occur when parsing a Rust source file to discover
/// and interpret include_cxx macros.
#[derive(Debug)]
pub enum ParseError {
    /// Unable to open the source file
    FileOpen(std::io::Error),
    /// The .rs file couldn't be read.
    FileRead(std::io::Error),
    /// The .rs file couldn't be parsed.
    Syntax(syn::Error),
    /// The include CPP macro could not be expanded into
    /// Rust bindings to C++, because of some problem during the conversion
    /// process. This could be anything from a C++ parsing error to some
    /// C++ feature that autocxx can't yet handle and isn't able to skip
    /// over. It could also cover errors in your syntax of the `include_cpp`
    /// macro or the directives inside.
    AutocxxCodegenError(EngineError),
}

impl Display for ParseError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            ParseError::FileOpen(err) => write!(f, "Unable to open file: {}", err)?,
            ParseError::FileRead(err) => write!(f, "Unable to read file: {}", err)?,
            ParseError::Syntax(err) => write!(f, "Syntax error parsing Rust file: {}", err)?,
            ParseError::AutocxxCodegenError(err) => {
                write!(f, "Unable to parse include_cpp! macro: {}", err)?
            }
        }
        Ok(())
    }
}

/// Parse a Rust file, and spot any include_cpp macros within it.
pub fn parse_file<P1: AsRef<Path>>(rs_file: P1) -> Result<ParsedFile, ParseError> {
    let mut source = String::new();
    let mut file = std::fs::File::open(rs_file).map_err(ParseError::FileOpen)?;
    file.read_to_string(&mut source)
        .map_err(ParseError::FileRead)?;
    proc_macro2::fallback::force();
    let source = syn::parse_file(&source).map_err(ParseError::Syntax)?;
    parse_file_contents(source)
}

fn parse_file_contents(source: syn::File) -> Result<ParsedFile, ParseError> {
    let mut results = Vec::new();
    for item in source.items {
        if let Item::Macro(ref mac) = item {
            if mac
                .mac
                .path
                .segments
                .last()
                .map(|s| s.ident.to_string() == "include_cpp")
                .unwrap_or(false)
            {
                let include_cpp = crate::IncludeCppEngine::new_from_syn(mac.mac.clone())
                    .map_err(ParseError::AutocxxCodegenError)?;
                results.push(Segment::Autocxx(include_cpp));
                continue;
            }
        }
        results.push(Segment::Other(item));
    }
    Ok(ParsedFile(results))
}

/// A Rust file parsed by autocxx. May contain zero or more autocxx 'engines',
/// i.e. the `IncludeCpp` class, corresponding to zero or more include_cpp
/// macros within this file. Also contains `syn::Item` structures for all
/// the rest of the Rust code, such that it can be reconstituted if necessary.
pub struct ParsedFile(Vec<Segment>);

#[allow(clippy::large_enum_variant)]
enum Segment {
    Autocxx(IncludeCppEngine),
    Other(Item),
}

impl ParsedFile {
    /// Get all the autocxxes in this parsed file.
    pub fn get_autocxxes(&self) -> impl Iterator<Item = &IncludeCppEngine> {
        self.0.iter().filter_map(|s| match s {
            Segment::Autocxx(includecpp) => Some(includecpp),
            Segment::Other(_) => None,
        })
    }

    pub fn get_autocxxes_mut(&mut self) -> impl Iterator<Item = &mut IncludeCppEngine> {
        self.0.iter_mut().filter_map(|s| match s {
            Segment::Autocxx(includecpp) => Some(includecpp),
            Segment::Other(_) => None,
        })
    }

    pub fn resolve_all(
        &mut self,
        autocxx_inc: Vec<PathBuf>,
        extra_clang_args: &[&str],
        dep_recorder: Option<Box<dyn RebuildDependencyRecorder>>,
    ) -> Result<(), ParseError> {
        let inner_dep_recorder: Option<Rc<dyn RebuildDependencyRecorder>> =
            dep_recorder.map(Rc::from);
        for include_cpp in self.get_autocxxes_mut() {
            #[allow(clippy::manual_map)] // because of dyn shenanigans
            let dep_recorder: Option<Box<dyn RebuildDependencyRecorder>> = match &inner_dep_recorder
            {
                None => None,
                Some(inner_dep_recorder) => Some(Box::new(CompositeDepRecorder::new(
                    inner_dep_recorder.clone(),
                ))),
            };
            include_cpp
                .generate(autocxx_inc.clone(), extra_clang_args, dep_recorder)
                .map_err(ParseError::AutocxxCodegenError)?
        }
        Ok(())
    }
}

impl ToTokens for ParsedFile {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        for seg in &self.0 {
            match seg {
                Segment::Other(item) => item.to_tokens(tokens),
                Segment::Autocxx(autocxx) => {
                    let these_tokens = autocxx.generate_rs();
                    tokens.extend(these_tokens);
                }
            }
        }
    }
}

/// Shenanigans required to share the same RebuildDependencyRecorder
/// with all of the include_cpp instances in this one file.
#[derive(Debug, Clone)]
struct CompositeDepRecorder(Rc<dyn RebuildDependencyRecorder>);

impl CompositeDepRecorder {
    fn new(inner: Rc<dyn RebuildDependencyRecorder>) -> Self {
        CompositeDepRecorder(inner)
    }
}

impl UnwindSafe for CompositeDepRecorder {}

impl RebuildDependencyRecorder for CompositeDepRecorder {
    fn record_header_file_dependency(&self, filename: &str) {
        self.0.record_header_file_dependency(filename);
    }
}
<|endoftext|>


<commit_before>// Problem statement for `strprintf` crate: provide a way to interpolate printf-style format
// strings using native Rust types. For example, it should be possible to format a string
// "%i %.2f %x" using values 42u32, 3.1415f64, and 255u8, and get a `std::string::String` "42 3.14
// ff".
//
// This is the same as strprintf module we already have in C++.
//
// The problem can be solved by wrapping `libc::snprintf`, which is what we do both here and in
// C++. However, our experience with C++ showed that we should constrain what types can be
// formatted. Otherwise, complex objects like `String` will be passed over FFI and lead to
// unexpected results (e.g. garbage strings).
//
// To achieve that, we provide a `Printfable` trait that's implemented only for types that our
// formatting macro accepts. Everything else will result in a compile-time error. See the docs in
// `trait` module for more on that.

extern crate libc;

pub mod specifiers_iterator;
pub mod traits;

use std::ffi::{CStr, CString};
use std::vec::Vec;
use traits::*;

/// Helper function to `fmt!`. **Use it only through that macro!**
///
/// Returns a formatted string, or the size of the buffer that's necessary to hold the formatted
/// string.
#[doc(hidden)]
pub fn fmt_arg_with_buffer_size<T>(
    format_cstring: &CStr,
    arg_c_repr_holder: &T,
    buf_size: usize,
) -> Result<String, usize>
where
    T: CReprHolder,
{
    let mut buffer = Vec::<u8>::with_capacity(buf_size);
    // Filling the vector with ones because CString::new() doesn't want any zeroes in there. The
    // last byte is left unused, so that CString::new() can put a terminating zero byte there
    // without triggering a re-allocation.
    buffer.resize(buf_size - 1, 1);
    unsafe {
        // It's safe to use this function because we initialized the buffer and we know there are
        // no zeroes there
        let buffer = CString::from_vec_unchecked(buffer);
        let buffer_ptr = buffer.into_raw();
        let bytes_written = libc::snprintf(
            buffer_ptr,
            buf_size as libc::size_t,
            format_cstring.as_ptr() as *const libc::c_char,
            arg_c_repr_holder.to_c_repr(),
        ) as usize;
        let buffer = CString::from_raw(buffer_ptr);
        if bytes_written >= buf_size {
            Err(bytes_written + 1)
        } else {
            buffer.into_string().map_err(|_| 0)
        }
    }
}

/// Helper function to `fmt!`. **Use it only through that macro!**
#[doc(hidden)]
pub fn fmt_arg<T>(format: &str, arg: T) -> Option<String>
where
    T: Printfable,
{
    // Returns None if `format` contains a null byte
    CString::new(format).ok().and_then(|local_format_cstring| {
        // Returns None if a holder couldn't be obtained - e.g. the value is a String that
        // contains a null byte
        arg.to_c_repr_holder().and_then(|arg_c_repr_holder| {
            match fmt_arg_with_buffer_size(&local_format_cstring, &arg_c_repr_holder, 1024) {
                Ok(formatted) => Some(formatted),
                Err(buf_size) => {
                    fmt_arg_with_buffer_size(&local_format_cstring, &arg_c_repr_holder, buf_size)
                        .ok()
                }
            }
        })
    })
}

/// A safe-ish wrapper around `libc::snprintf`.
///
/// It pairs each format specifier ("%i", "%.2f" etc.) with a value, and passes those to
/// `libc::snprinf`; the results are then concatenated.
///
/// If a pair couldn't be formatted, it's omitted from the output. This can happen if:
/// - a format string contains null bytes;
/// - the string value to be formatted contains null bytes;
/// - `libc::snprintf` failed to format things even when given large enough buffer to do so.
#[macro_export]
macro_rules! fmt {
    ( $format:expr ) => {
        {
            let format: &str = $format;
            String::from(format)
        }
    };

    ( $format:expr, $( $arg:expr ),+ ) => {
        {
            let format: &str = $format;

            let mut result = String::new();

            use $crate::specifiers_iterator::SpecifiersIterator;
            let mut specifiers = SpecifiersIterator::from(format);

            $(
                let local_format = specifiers.next().unwrap_or("");
                if let Some(formatted_string) = $crate::fmt_arg(local_format, $arg) {
                    result.push_str(&formatted_string);
                }
            )+

            result
        }
    };
}

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

    #[test]
    fn returns_first_argument_if_it_is_the_only_one() {
        let input = String::from("Hello, world!");
        assert_eq!(fmt!(&input), input);
    }

    #[test]
    fn replaces_printf_format_with_text_representation_of_an_argument() {
        assert_eq!(fmt!("%i", 42), "42");
        assert_eq!(fmt!("%i", -13), "-13");
        assert_eq!(fmt!("%.3f", 3.1416), "3.142");
        assert_eq!(fmt!("%i %i", 100500, -191), "100500 -191");
    }

    #[test]
    fn formats_i32() {
        assert_eq!(fmt!("%i", 42i32), "42");
        assert_eq!(fmt!("%i", std::i32::MIN), "-2147483648");
        assert_eq!(fmt!("%i", std::i32::MAX), "2147483647");
    }

    #[test]
    fn formats_u32() {
        assert_eq!(fmt!("%u", 42u32), "42");
        assert_eq!(fmt!("%u", 0u32), "0");
        assert_eq!(fmt!("%u", std::u32::MAX), "4294967295");
    }

    #[test]
    fn formats_i64() {
        assert_eq!(fmt!("%li", 42i64), "42");
        assert_eq!(fmt!("%li", std::i32::MIN as i64 - 1), "-2147483649");
        assert_eq!(fmt!("%li", std::i32::MAX as i64 + 1), "2147483648");

        assert_eq!(fmt!("%lli", 42i64), "42");
        assert_eq!(fmt!("%lli", std::i64::MIN), "-9223372036854775808");
        assert_eq!(fmt!("%lli", std::i64::MAX), "9223372036854775807");
    }

    #[test]
    fn formats_u64() {
        assert_eq!(fmt!("%lu", 42u64), "42");
        assert_eq!(fmt!("%lu", 0u64), "0");
        assert_eq!(fmt!("%lu", std::u64::MAX), "18446744073709551615");

        assert_eq!(fmt!("%llu", 42u64), "42");
        assert_eq!(fmt!("%llu", 0u64), "0");
        assert_eq!(fmt!("%llu", std::u64::MAX), "18446744073709551615");
    }

    #[test]
    fn formats_pointers() {
        let x = 42i64;

        let x_ptr = &x as *const i64;
        let x_ptr_formatted = fmt!("%p", x_ptr);

        let x_ptr_void = x_ptr as *const libc::c_void;
        let x_ptr_void_formatted = fmt!("%p", x_ptr_void);
        assert!(!x_ptr_formatted.is_empty());
        assert_eq!(x_ptr_formatted, x_ptr_void_formatted);
    }

    #[test]
    fn formats_all_null_pointers_the_same() {
        let i32_ptr_fmt = fmt!("%p", std::ptr::null::<i32>());
        let u32_ptr_fmt = fmt!("%p", std::ptr::null::<u32>());
        let i64_ptr_fmt = fmt!("%p", std::ptr::null::<i64>());
        let u64_ptr_fmt = fmt!("%p", std::ptr::null::<u64>());
        let f32_ptr_fmt = fmt!("%p", std::ptr::null::<f32>());
        let f64_ptr_fmt = fmt!("%p", std::ptr::null::<f64>());
        assert!(!i32_ptr_fmt.is_empty());
        assert_eq!(i32_ptr_fmt, u32_ptr_fmt);
        assert_eq!(u32_ptr_fmt, i64_ptr_fmt);
        assert_eq!(i64_ptr_fmt, u64_ptr_fmt);
        assert_eq!(u64_ptr_fmt, f32_ptr_fmt);
        assert_eq!(f32_ptr_fmt, f64_ptr_fmt);
    }

    #[test]
    fn formats_float() {
        let x = 42.0f32;
        assert_eq!(fmt!("%f", x), "42.000000");
        assert_eq!(fmt!("%.3f", x), "42.000");

        let y = 42e3f32;
        assert_eq!(fmt!("%e", y), "4.200000e+04");
        assert_eq!(fmt!("%.3e", y), "4.200e+04");

        assert_eq!(fmt!("%f", std::f32::INFINITY), "inf");
        assert_eq!(fmt!("%F", std::f32::INFINITY), "INF");

        assert_eq!(fmt!("%f", std::f32::NEG_INFINITY), "-inf");
        assert_eq!(fmt!("%F", std::f32::NEG_INFINITY), "-INF");

        assert_eq!(fmt!("%f", std::f32::NAN), "nan");
        assert_eq!(fmt!("%F", std::f32::NAN), "NAN");
    }

    #[test]
    fn formats_double() {
        let x = 42.0f64;
        assert_eq!(fmt!("%f", x), "42.000000");
        assert_eq!(fmt!("%.3f", x), "42.000");

        let y = 42e138f64;
        assert_eq!(fmt!("%e", y), "4.200000e+139");
        assert_eq!(fmt!("%.3e", y), "4.200e+139");

        assert_eq!(fmt!("%f", std::f64::INFINITY), "inf");
        assert_eq!(fmt!("%F", std::f64::INFINITY), "INF");

        assert_eq!(fmt!("%f", std::f64::NEG_INFINITY), "-inf");
        assert_eq!(fmt!("%F", std::f64::NEG_INFINITY), "-INF");

        assert_eq!(fmt!("%f", std::f64::NAN), "nan");
        assert_eq!(fmt!("%F", std::f64::NAN), "NAN");
    }

    #[test]
    fn formats_str_slice() {
        let input = "Hello, world!";
        assert_eq!(fmt!("%s", input), input);
    }

    #[test]
    fn formats_borrowed_string() {
        let input = String::from("Hello, world!");
        assert_eq!(fmt!("%s", &input), input);
    }

    #[test]
    fn formats_moved_string() {
        let input = String::from("Hello, world!");
        assert_eq!(fmt!("%s", input.clone()), input);
    }

    #[test]
    fn formats_2_megabyte_string() {
        let spacer = String::from(" ").repeat(1024 * 1024);
        let format = {
            let mut result = spacer.clone();
            result.push_str("%i");
            result.push_str(&spacer);
            result.push_str("%i");
            result
        };
        let expected = {
            let mut result = spacer.clone();
            result.push_str("42");
            result.push_str(&spacer);
            result.push_str("100500");
            result
        };
        assert_eq!(fmt!(&format, 42, 100500), expected);
    }
}
<commit_msg>fixup! Make fmt! work nicely with long strings<commit_after>// Problem statement for `strprintf` crate: provide a way to interpolate printf-style format
// strings using native Rust types. For example, it should be possible to format a string
// "%i %.2f %x" using values 42u32, 3.1415f64, and 255u8, and get a `std::string::String` "42 3.14
// ff".
//
// This is the same as strprintf module we already have in C++.
//
// The problem can be solved by wrapping `libc::snprintf`, which is what we do both here and in
// C++. However, our experience with C++ showed that we should constrain what types can be
// formatted. Otherwise, complex objects like `String` will be passed over FFI and lead to
// unexpected results (e.g. garbage strings).
//
// To achieve that, we provide a `Printfable` trait that's implemented only for types that our
// formatting macro accepts. Everything else will result in a compile-time error. See the docs in
// `trait` module for more on that.

extern crate libc;

pub mod specifiers_iterator;
pub mod traits;

use std::ffi::{CStr, CString};
use std::vec::Vec;
use traits::*;

/// Helper function to `fmt!`. **Use it only through that macro!**
///
/// Returns a formatted string, or the size of the buffer that's necessary to hold the formatted
/// string.
#[doc(hidden)]
pub fn fmt_arg_with_buffer_size<T>(
    format_cstring: &CStr,
    arg_c_repr_holder: &T,
    buf_size: usize,
) -> Result<String, usize>
where
    T: CReprHolder,
{
    let mut buffer = Vec::<u8>::with_capacity(buf_size);
    // CString appends a null byte and calls Vec::into_byte_slice(). That conversion will shed all
    // unused capacity. For this reason, we pre-fill the vector with dummy values, leaving one byte
    // free, so CString's null byte doesn't trigger a re-allocation.
    buffer.resize(buf_size - 1, 0);
    unsafe {
        // It's safe to use this function because we initialized the buffer and we know there are
        // no zeroes there
        let buffer = CString::from_vec_unchecked(buffer);
        let buffer_ptr = buffer.into_raw();
        let bytes_written = libc::snprintf(
            buffer_ptr,
            buf_size as libc::size_t,
            format_cstring.as_ptr() as *const libc::c_char,
            arg_c_repr_holder.to_c_repr(),
        ) as usize;
        let buffer = CString::from_raw(buffer_ptr);
        if bytes_written >= buf_size {
            Err(bytes_written + 1)
        } else {
            buffer.into_string().map_err(|_| 0)
        }
    }
}

/// Helper function to `fmt!`. **Use it only through that macro!**
#[doc(hidden)]
pub fn fmt_arg<T>(format: &str, arg: T) -> Option<String>
where
    T: Printfable,
{
    // Returns None if `format` contains a null byte
    CString::new(format).ok().and_then(|local_format_cstring| {
        // Returns None if a holder couldn't be obtained - e.g. the value is a String that
        // contains a null byte
        arg.to_c_repr_holder().and_then(|arg_c_repr_holder| {
            match fmt_arg_with_buffer_size(&local_format_cstring, &arg_c_repr_holder, 1024) {
                Ok(formatted) => Some(formatted),
                Err(buf_size) => {
                    fmt_arg_with_buffer_size(&local_format_cstring, &arg_c_repr_holder, buf_size)
                        .ok()
                }
            }
        })
    })
}

/// A safe-ish wrapper around `libc::snprintf`.
///
/// It pairs each format specifier ("%i", "%.2f" etc.) with a value, and passes those to
/// `libc::snprinf`; the results are then concatenated.
///
/// If a pair couldn't be formatted, it's omitted from the output. This can happen if:
/// - a format string contains null bytes;
/// - the string value to be formatted contains null bytes;
/// - `libc::snprintf` failed to format things even when given large enough buffer to do so.
#[macro_export]
macro_rules! fmt {
    ( $format:expr ) => {
        {
            let format: &str = $format;
            String::from(format)
        }
    };

    ( $format:expr, $( $arg:expr ),+ ) => {
        {
            let format: &str = $format;

            let mut result = String::new();

            use $crate::specifiers_iterator::SpecifiersIterator;
            let mut specifiers = SpecifiersIterator::from(format);

            $(
                let local_format = specifiers.next().unwrap_or("");
                if let Some(formatted_string) = $crate::fmt_arg(local_format, $arg) {
                    result.push_str(&formatted_string);
                }
            )+

            result
        }
    };
}

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

    #[test]
    fn returns_first_argument_if_it_is_the_only_one() {
        let input = String::from("Hello, world!");
        assert_eq!(fmt!(&input), input);
    }

    #[test]
    fn replaces_printf_format_with_text_representation_of_an_argument() {
        assert_eq!(fmt!("%i", 42), "42");
        assert_eq!(fmt!("%i", -13), "-13");
        assert_eq!(fmt!("%.3f", 3.1416), "3.142");
        assert_eq!(fmt!("%i %i", 100500, -191), "100500 -191");
    }

    #[test]
    fn formats_i32() {
        assert_eq!(fmt!("%i", 42i32), "42");
        assert_eq!(fmt!("%i", std::i32::MIN), "-2147483648");
        assert_eq!(fmt!("%i", std::i32::MAX), "2147483647");
    }

    #[test]
    fn formats_u32() {
        assert_eq!(fmt!("%u", 42u32), "42");
        assert_eq!(fmt!("%u", 0u32), "0");
        assert_eq!(fmt!("%u", std::u32::MAX), "4294967295");
    }

    #[test]
    fn formats_i64() {
        assert_eq!(fmt!("%li", 42i64), "42");
        assert_eq!(fmt!("%li", std::i32::MIN as i64 - 1), "-2147483649");
        assert_eq!(fmt!("%li", std::i32::MAX as i64 + 1), "2147483648");

        assert_eq!(fmt!("%lli", 42i64), "42");
        assert_eq!(fmt!("%lli", std::i64::MIN), "-9223372036854775808");
        assert_eq!(fmt!("%lli", std::i64::MAX), "9223372036854775807");
    }

    #[test]
    fn formats_u64() {
        assert_eq!(fmt!("%lu", 42u64), "42");
        assert_eq!(fmt!("%lu", 0u64), "0");
        assert_eq!(fmt!("%lu", std::u64::MAX), "18446744073709551615");

        assert_eq!(fmt!("%llu", 42u64), "42");
        assert_eq!(fmt!("%llu", 0u64), "0");
        assert_eq!(fmt!("%llu", std::u64::MAX), "18446744073709551615");
    }

    #[test]
    fn formats_pointers() {
        let x = 42i64;

        let x_ptr = &x as *const i64;
        let x_ptr_formatted = fmt!("%p", x_ptr);

        let x_ptr_void = x_ptr as *const libc::c_void;
        let x_ptr_void_formatted = fmt!("%p", x_ptr_void);
        assert!(!x_ptr_formatted.is_empty());
        assert_eq!(x_ptr_formatted, x_ptr_void_formatted);
    }

    #[test]
    fn formats_all_null_pointers_the_same() {
        let i32_ptr_fmt = fmt!("%p", std::ptr::null::<i32>());
        let u32_ptr_fmt = fmt!("%p", std::ptr::null::<u32>());
        let i64_ptr_fmt = fmt!("%p", std::ptr::null::<i64>());
        let u64_ptr_fmt = fmt!("%p", std::ptr::null::<u64>());
        let f32_ptr_fmt = fmt!("%p", std::ptr::null::<f32>());
        let f64_ptr_fmt = fmt!("%p", std::ptr::null::<f64>());
        assert!(!i32_ptr_fmt.is_empty());
        assert_eq!(i32_ptr_fmt, u32_ptr_fmt);
        assert_eq!(u32_ptr_fmt, i64_ptr_fmt);
        assert_eq!(i64_ptr_fmt, u64_ptr_fmt);
        assert_eq!(u64_ptr_fmt, f32_ptr_fmt);
        assert_eq!(f32_ptr_fmt, f64_ptr_fmt);
    }

    #[test]
    fn formats_float() {
        let x = 42.0f32;
        assert_eq!(fmt!("%f", x), "42.000000");
        assert_eq!(fmt!("%.3f", x), "42.000");

        let y = 42e3f32;
        assert_eq!(fmt!("%e", y), "4.200000e+04");
        assert_eq!(fmt!("%.3e", y), "4.200e+04");

        assert_eq!(fmt!("%f", std::f32::INFINITY), "inf");
        assert_eq!(fmt!("%F", std::f32::INFINITY), "INF");

        assert_eq!(fmt!("%f", std::f32::NEG_INFINITY), "-inf");
        assert_eq!(fmt!("%F", std::f32::NEG_INFINITY), "-INF");

        assert_eq!(fmt!("%f", std::f32::NAN), "nan");
        assert_eq!(fmt!("%F", std::f32::NAN), "NAN");
    }

    #[test]
    fn formats_double() {
        let x = 42.0f64;
        assert_eq!(fmt!("%f", x), "42.000000");
        assert_eq!(fmt!("%.3f", x), "42.000");

        let y = 42e138f64;
        assert_eq!(fmt!("%e", y), "4.200000e+139");
        assert_eq!(fmt!("%.3e", y), "4.200e+139");

        assert_eq!(fmt!("%f", std::f64::INFINITY), "inf");
        assert_eq!(fmt!("%F", std::f64::INFINITY), "INF");

        assert_eq!(fmt!("%f", std::f64::NEG_INFINITY), "-inf");
        assert_eq!(fmt!("%F", std::f64::NEG_INFINITY), "-INF");

        assert_eq!(fmt!("%f", std::f64::NAN), "nan");
        assert_eq!(fmt!("%F", std::f64::NAN), "NAN");
    }

    #[test]
    fn formats_str_slice() {
        let input = "Hello, world!";
        assert_eq!(fmt!("%s", input), input);
    }

    #[test]
    fn formats_borrowed_string() {
        let input = String::from("Hello, world!");
        assert_eq!(fmt!("%s", &input), input);
    }

    #[test]
    fn formats_moved_string() {
        let input = String::from("Hello, world!");
        assert_eq!(fmt!("%s", input.clone()), input);
    }

    #[test]
    fn formats_2_megabyte_string() {
        let spacer = String::from(" ").repeat(1024 * 1024);
        let format = {
            let mut result = spacer.clone();
            result.push_str("%i");
            result.push_str(&spacer);
            result.push_str("%i");
            result
        };
        let expected = {
            let mut result = spacer.clone();
            result.push_str("42");
            result.push_str(&spacer);
            result.push_str("100500");
            result
        };
        assert_eq!(fmt!(&format, 42, 100500), expected);
    }
}
<|endoftext|>


<commit_before>pub trait HexDisplay {
      /// Converts the value of `self` to a hex value, returning the owned
      ///     /// string.
  fn to_hex(&self, chunk_size: usize) -> String;
}

static CHARS: &'static[u8] = b"0123456789abcdef";

impl HexDisplay for [u8] {
  #[allow(unused_variables)]
  fn to_hex(&self, chunk_size: usize) -> String {
    let mut v = Vec::with_capacity(self.len() * 3);
    let mut i = 0;
    for chunk in self.chunks(chunk_size) {
      let s = format!("{:08x}", i);
      for &ch in s.as_bytes().iter() {
        v.push(ch);
      }
      v.push('\t' as u8);

      i = i + chunk_size;

      for &byte in chunk {
        v.push(CHARS[(byte >> 4) as usize]);
        v.push(CHARS[(byte & 0xf) as usize]);
        v.push(' ' as u8);
      }
      if chunk_size > chunk.len() {
        for j in 0..(chunk_size - chunk.len()) {
          v.push(' ' as u8);
          v.push(' ' as u8);
          v.push(' ' as u8);
        }
      }
      v.push('\t' as u8);

      for &byte in chunk {
        if (byte >=32 && byte <= 126) || byte >= 128 {
          v.push(byte);
        } else {
          v.push('.' as u8);
        }
      }
      v.push('\n' as u8);
    }

    unsafe {
      String::from_utf8_unchecked(v)
    }
  }
}

pub trait AsBytes {
  fn as_bytes(&self) -> &[u8];
}

impl<'a> AsBytes for &'a str {
  #[inline(always)]
  fn as_bytes(&self) -> &[u8] {
    str::as_bytes(self)
  }
}

impl AsBytes for str {
  #[inline(always)]
  fn as_bytes(&self) -> &[u8] {
    str::as_bytes(self)
  }
}

impl<'a> AsBytes for &'a [u8] {
  #[inline(always)]
  fn as_bytes(&self) -> &[u8] {
    *self
  }
}

impl AsBytes for [u8] {
  #[inline(always)]
  fn as_bytes(&self) -> &[u8] {
    self
  }
}
<commit_msg>Add more utility methods to develop parsers<commit_after>use std::raw::{self, Repr};

pub trait HexDisplay {
  fn offset(&self, second:&[u8]) -> usize;

  /// Converts the value of `self` to a hex value, returning the owned
  /// string.
  fn to_hex(&self, chunk_size: usize) -> String;

  fn to_hex_from(&self, chunk_size: usize, from: usize) -> String;
}

static CHARS: &'static[u8] = b"0123456789abcdef";

impl HexDisplay for [u8] {
  fn offset(&self, second:&[u8]) -> usize {
    let fst: raw::Slice<u8> = self.repr();
    let snd: raw::Slice<u8> = second.repr();

    unsafe {
      snd.data as usize -
        fst.data as usize
    }
  }

  #[allow(unused_variables)]
  fn to_hex(&self, chunk_size: usize) -> String {
    self.to_hex_from(chunk_size, 0)
  }

  #[allow(unused_variables)]
  fn to_hex_from(&self, chunk_size: usize, from: usize) -> String {
    let mut v = Vec::with_capacity(self.len() * 3);
    let mut i = from;
    for chunk in self.chunks(chunk_size) {
      let s = format!("{:08x}", i);
      for &ch in s.as_bytes().iter() {
        v.push(ch);
      }
      v.push('\t' as u8);

      i = i + chunk_size;

      for &byte in chunk {
        v.push(CHARS[(byte >> 4) as usize]);
        v.push(CHARS[(byte & 0xf) as usize]);
        v.push(' ' as u8);
      }
      if chunk_size > chunk.len() {
        for j in 0..(chunk_size - chunk.len()) {
          v.push(' ' as u8);
          v.push(' ' as u8);
          v.push(' ' as u8);
        }
      }
      v.push('\t' as u8);

      for &byte in chunk {
        if (byte >=32 && byte <= 126) || byte >= 128 {
          v.push(byte);
        } else {
          v.push('.' as u8);
        }
      }
      v.push('\n' as u8);
    }

    unsafe {
      String::from_utf8_unchecked(v)
    }
  }
}

pub trait AsBytes {
  fn as_bytes(&self) -> &[u8];
}

impl<'a> AsBytes for &'a str {
  #[inline(always)]
  fn as_bytes(&self) -> &[u8] {
    str::as_bytes(self)
  }
}

impl AsBytes for str {
  #[inline(always)]
  fn as_bytes(&self) -> &[u8] {
    str::as_bytes(self)
  }
}

impl<'a> AsBytes for &'a [u8] {
  #[inline(always)]
  fn as_bytes(&self) -> &[u8] {
    *self
  }
}

impl AsBytes for [u8] {
  #[inline(always)]
  fn as_bytes(&self) -> &[u8] {
    self
  }
}
<|endoftext|>


<commit_before>use ::*;
use relm::{Relm, RemoteRelm, Widget};
use gtk::prelude::*;
use gtk::{Window, WindowType, DrawingArea, Button};
use gtk::{FileChooserDialog};
use tokio_core::reactor::Interval;
use std::time::Duration;

use map::*;

#[derive(Clone)]
pub struct Model {
    map: Map,
}

#[derive(SimpleMsg)]
pub enum Msg {
    Save,
    Open,
    Tick,
    Quit,
}

#[derive(Clone)]
pub struct Win {
    hbox: gtk::Box,
    button_box: gtk::ButtonBox,
    open_button: Button,
    save_button: Button,
    area: DrawingArea,
    window: Window,
}

impl Widget for Win {
    type Root = Window;
    type Model = Model;
    type Msg = Msg;

    fn root(&self) -> &Self::Root {
        &self.window
    }

    fn model() -> Self::Model {
        Model {
            map: Map::blom(),
        }
    }

    fn subscriptions(relm: &Relm<Msg>) {
        let stream = Interval::new(Duration::from_millis(30), relm.handle()).unwrap();
        relm.connect_exec_ignore_err(stream, Msg::Tick);
    }

    fn update(&mut self, event: Msg, model: &mut Self::Model) {
        match event {
            Msg::Tick => {
                model.map.next_generation();
                use gdk::prelude::ContextExt;
                let cr = cairo::Context::create_from_window(&self.area.get_window().unwrap());
                cr.set_source_rgb(1., 1., 1.);
                cr.paint();
                cr.scale(2., 2.);
                cr.set_source_rgb(0., 0., 0.);
                for pos in &model.map.alive_cells {
                    cr.rectangle(pos.x as f64+100., pos.y as f64+100., 1., 1.);
                }
                cr.fill();
            },
            Msg::Save => {
                let dialog = FileChooserDialog::new(
                    Some("Save File"),
                    Some(&self.window),
                    gtk::FileChooserAction::Save);
                let cancel: i32 = gtk::ResponseType::Cancel.into();
                let accept: i32 = gtk::ResponseType::Accept.into();
                dialog.add_button("Cancel", cancel);
                dialog.add_button("Save", accept);
                if accept == dialog.run() {
                    if let Some(path) = dialog.get_filename() {
                        model.map.save(path).unwrap();
                    }
                }
                dialog.close();
            },
            Msg::Open => {
                let dialog = FileChooserDialog::new(
                    Some("Open File"),
                    Some(&self.window),
                    gtk::FileChooserAction::Open);
                let cancel: i32 = gtk::ResponseType::Cancel.into();
                let accept: i32 = gtk::ResponseType::Accept.into();
                dialog.add_button("Cancel", cancel);
                dialog.add_button("Open", accept);
                if accept == dialog.run() {
                    if let Some(path) = dialog.get_filename() {
                        model.map = Map::open(path).unwrap();
                    }
                }
                dialog.close();
            },
            Msg::Quit => gtk::main_quit(),
        }
    }

    fn view(relm: RemoteRelm<Msg>, _model: &Self::Model) -> Self {
        let window = Window::new(WindowType::Toplevel);
        let hbox = gtk::Box::new(gtk::Orientation::Horizontal, 0);
        let button_box = gtk::ButtonBox::new(gtk::Orientation::Vertical);
        let open_button = Button::new_with_label("Open");
        let save_button = Button::new_with_label("Save");
        let area = DrawingArea::new();
        area.set_size_request(500, 500);
        button_box.set_layout(gtk::ButtonBoxStyle::Start);

        button_box.pack_start(&open_button, false, false, 0);
        button_box.pack_start(&save_button, false, false, 0);
        hbox.pack_start(&area, false, false, 0);
        hbox.pack_start(&button_box, false, false, 0);
        window.add(&hbox);
        window.set_title("Game of Life");
        window.show_all();

        connect!(relm, window, connect_delete_event(_, _) (Some(Msg::Quit), Inhibit(false)));
        connect!(relm, save_button, connect_clicked(_), Msg::Save);
        connect!(relm, open_button, connect_clicked(_), Msg::Open);

        Win {
            hbox: hbox,
            button_box: button_box,
            open_button: open_button,
            save_button: save_button,
            area: area,
            window: window,
        }
    }
}
<commit_msg>Drafting<commit_after>use ::*;
use relm::{Relm, RemoteRelm, Widget};
use gtk::prelude::*;
use gtk::{Window, WindowType, DrawingArea, Button};
use gtk::{FileChooserDialog};
use tokio_core::reactor::Interval;
use std::time::Duration;

use map::*;

#[derive(Clone)]
pub struct Model {
    map: Map,
    size: Pos,
}

#[derive(SimpleMsg)]
pub enum Msg {
    Motion(((f64, f64), u32)),
    Save,
    Open,
    Tick,
    Quit,
}

#[derive(Clone)]
pub struct Win {
    hbox: gtk::Box,
    button_box: gtk::ButtonBox,
    open_button: Button,
    save_button: Button,
    area: DrawingArea,
    window: Window,
}

impl Widget for Win {
    type Root = Window;
    type Model = Model;
    type Msg = Msg;

    fn root(&self) -> &Self::Root {
        &self.window
    }

    fn model() -> Self::Model {
        Model {
            map: Map::blom(),
            size: pos(0, 0),
        }
    }

    fn subscriptions(relm: &Relm<Msg>) {
        let stream = Interval::new(Duration::from_millis(30), relm.handle()).unwrap();
        relm.connect_exec_ignore_err(stream, Msg::Tick);
    }

    fn update(&mut self, event: Msg, model: &mut Self::Model) {
        match event {
            Msg::Tick => {
                model.map.next_generation();
                use gdk::prelude::ContextExt;
                let cr = cairo::Context::create_from_window(&self.area.get_window().unwrap());
                cr.set_source_rgb(1., 1., 1.);
                cr.paint();
                cr.scale(2., 2.);
                cr.set_source_rgb(0., 0., 0.);
                for pos in &model.map.alive_cells {
                    cr.rectangle(pos.x as f64+100., pos.y as f64+100., 1., 1.);
                }
                cr.fill();
            },
            Msg::Save => {
                let dialog = FileChooserDialog::new(
                    Some("Save File"),
                    Some(&self.window),
                    gtk::FileChooserAction::Save);
                let cancel: i32 = gtk::ResponseType::Cancel.into();
                let accept: i32 = gtk::ResponseType::Accept.into();
                dialog.add_button("Cancel", cancel);
                dialog.add_button("Save", accept);
                if accept == dialog.run() {
                    if let Some(path) = dialog.get_filename() {
                        model.map.save(path).unwrap();
                    }
                }
                dialog.close();
            },
            Msg::Open => {
                let dialog = FileChooserDialog::new(
                    Some("Open File"),
                    Some(&self.window),
                    gtk::FileChooserAction::Open);
                let cancel: i32 = gtk::ResponseType::Cancel.into();
                let accept: i32 = gtk::ResponseType::Accept.into();
                dialog.add_button("Cancel", cancel);
                dialog.add_button("Open", accept);
                if accept == dialog.run() {
                    if let Some(path) = dialog.get_filename() {
                        model.map = Map::open(path).unwrap();
                    }
                }
                dialog.close();
            },
            Msg::Motion(((x, y), t)) => {
                println!("({}, {}), {}", x, y, t);
            },
            Msg::Quit => gtk::main_quit(),
        }
    }

    fn view(relm: RemoteRelm<Msg>, _model: &Self::Model) -> Self {
        let window = Window::new(WindowType::Toplevel);
        let hbox = gtk::Box::new(gtk::Orientation::Horizontal, 0);
        let button_box = gtk::ButtonBox::new(gtk::Orientation::Vertical);
        let open_button = Button::new_with_label("Open");
        let save_button = Button::new_with_label("Save");
        let area = DrawingArea::new();
        area.set_size_request(500, 500);
        area.set_events(area.get_events() | gdk::POINTER_MOTION_MASK.bits() as i32);
        area.set_events(area.get_events() | gdk::BUTTON_PRESS_MASK.bits() as i32);
        button_box.set_layout(gtk::ButtonBoxStyle::Start);

        button_box.pack_start(&open_button, false, false, 0);
        button_box.pack_start(&save_button, false, false, 0);
        hbox.pack_start(&area, false, false, 0);
        hbox.pack_start(&button_box, false, false, 0);
        window.add(&hbox);
        window.set_title("Game of Life");
        window.show_all();

        connect!(relm, window, connect_delete_event(_, _) (Some(Msg::Quit), Inhibit(false)));
        connect!(relm, area, connect_motion_notify_event(_, ev) (Some(Msg::Motion((ev.get_position(), 0))), Inhibit(false)));
        connect!(relm, save_button, connect_clicked(_), Msg::Save);
        connect!(relm, open_button, connect_clicked(_), Msg::Open);

        Win {
            hbox: hbox,
            button_box: button_box,
            open_button: open_button,
            save_button: save_button,
            area: area,
            window: window,
        }
    }
}
<|endoftext|>


<commit_before><commit_msg>Implement missing methods to make the AniList backend functional<commit_after><|endoftext|>


<commit_before><commit_msg>Implement the initial rebind proposal for the ArcBall.<commit_after><|endoftext|>


<commit_before><commit_msg>Make it even easier<commit_after><|endoftext|>