<commit_before>use std::net::TcpStream;
use std::collections::HashMap;
use std::io;
use std::io::{Read,Write};
use std::ops::Deref;
use libc;

use unix_socket::UnixStream;
use rustc_serialize::hex::ToHex;
use dbus_serialize::types::{Value,BasicValue};
use dbus_serialize::decoder::DBusDecoder;

use message;
use message::{Message,HeaderFieldName,MessageBuf};
use demarshal::{demarshal,DemarshalError};
use marshal::Marshal;

trait StreamSocket : Read + Write { }

impl StreamSocket for TcpStream { }
impl StreamSocket for UnixStream { }

enum Socket {
    Tcp(TcpStream),
    Uds(UnixStream)
}

pub struct Connection {
    sock: Socket,
    next_serial: u32,
    queue: Vec<Message>,
}

#[derive(Debug)]
pub enum Error {
    Disconnected,
    IOError(io::Error),
    DemarshalError(DemarshalError),
    BadData,
    AuthFailed,
}

impl From<io::Error> for Error {
    fn from(x: io::Error) -> Self {
        Error::IOError(x)
    }
}

impl From<DemarshalError> for Error {
    fn from(x: DemarshalError) -> Self {
        Error::DemarshalError(x)
    }
}

fn read_exactly(sock: &mut StreamSocket, buf: &mut Vec<u8>, len: usize) -> Result<(),Error> {
    buf.truncate(0);
    buf.reserve(len);
    if try!(sock.take(len as u64).read_to_end(buf)) != len {
        return Err(Error::Disconnected);
    }
    Ok(())
}

fn read_line(sock: &mut StreamSocket) -> Result<String,Error> {
    let mut line = "".to_string();
    let mut last = '\0';

    loop {
        let mut buf = vec![0];
        match sock.read(&mut buf) {
            Ok(x) if x > 0 => (),
            _ => return Err(Error::Disconnected)
        };
        let chr = buf[0] as char;
        line.push(chr);
        if chr == '\n' && last == '\r' {
            break;
        }
        last = chr;
    }
    Ok(line)
}

impl Connection {
    fn get_sock(&mut self) -> &mut StreamSocket {
        match self.sock {
            Socket::Tcp(ref mut x) => x,
            Socket::Uds(ref mut x) => x
        }
    }

    fn auth_anonymous(&mut self) -> Result<(),Error> {
        let sock = self.get_sock();

        // Authenticate to the daemon
        let buf = vec![0];
        try!(sock.write_all(&buf));

        try!(sock.write_all(b"AUTH ANONYMOUS 6c69626462757320312e382e3132\r\n"));

        // Read response
        let resp = try!(read_line(sock));
        if !resp.starts_with("OK ") {
            return Err(Error::AuthFailed);
        }

        // Ready for action
        try!(sock.write_all(b"BEGIN\r\n"));
        Ok(())
    }

    fn auth_external(&mut self) -> Result<(),Error> {
        let sock = self.get_sock();

        // Authenticate to the daemon
        let buf = vec![0];
        try!(sock.write_all(&buf));

        let uid = unsafe {
            libc::funcs::posix88::unistd::getuid()
        };
        let uid_str = uid.to_string();
        let uid_hex = uid_str.into_bytes().to_hex();
        let cmd = "AUTH EXTERNAL ".to_string() + &uid_hex + "\r\n";
        try!(sock.write_all(&cmd.into_bytes()));

        // Read response
        let resp = try!(read_line(sock));
        if !resp.starts_with("OK ") {
            return Err(Error::AuthFailed);
        }

        // Ready for action
        try!(sock.write_all(b"BEGIN\r\n"));
        Ok(())
    }

    fn say_hello(&mut self) -> Result<(),Error> {
        let mut msg = message::create_method_call("org.freedesktop.DBus",
                                                  "/org/freedesktop/DBus",
                                                  "org.freedesktop.DBus",
                                                  "Hello");
        try!(self.call_sync(&mut msg));
        // XXX: validate Hello reply
        Ok(())
    }

    pub fn connect_uds(addr: &str) -> Result<Connection,Error> {
        let sock = try!(UnixStream::connect(addr));
        let mut conn = Connection {
            sock: Socket::Uds(sock),
            queue: Vec::new(),
            next_serial: 1
        };

        try!(conn.auth_external());
        try!(conn.say_hello());
        Ok(conn)
    }

    pub fn connect_tcp(addr: &str) -> Result<Connection,Error> {
        let sock = try!(TcpStream::connect(addr));
        let mut conn = Connection {
            sock: Socket::Tcp(sock),
            queue: Vec::new(),
            next_serial: 1
        };

        try!(conn.auth_anonymous());
        try!(conn.say_hello());
        Ok(conn)
    }

    pub fn send(&mut self, mbuf: &mut MessageBuf) -> Result<u32, Error> {
        let mut msg = &mut mbuf.0;
        // A minimum header with no body is 16 bytes
        let mut len = msg.len() as u32;
        if len < 16 {
            return Err(Error::BadData);
        }

        // Get the current length from the message, which only include the length of the header.
        // That field should actually be the length of only the body, so update that now
        let old_len = message::get_length(msg);
        len -= old_len;
        let mut buf = Vec::new();
        len.dbus_encode(&mut buf);
        // Update the message with a correct serial number, as well
        let this_serial = self.next_serial;
        self.next_serial += 1;
        this_serial.dbus_encode(&mut buf);
        message::set_length(msg, &buf);

        let sock = self.get_sock();
        try!(sock.write_all(msg));
        Ok(this_serial)
    }

    pub fn call_sync(&mut self, mbuf: &mut MessageBuf) -> Result<Vec<Value>,Error> {
        // XXX: assert that this is a method call with reply
        let serial = try!(self.send(mbuf));
        // We need a local queue so that read_msg doesn't just give us
        // the same one over and over
        let mut queue = Vec::new();
        loop {
            let msg = try!(self.read_msg());
            match msg.headers.get(&(HeaderFieldName::ReplySerial as u8)) {
                Some(&Value::Variant(ref x)) => {
                    let reply_serial : u32 = DBusDecoder::decode(x.object.deref().clone()).unwrap();
                    if reply_serial == serial {
                        // Move our queued messages into the Connection's queue
                        for _ in 0..queue.len() {
                            self.queue.push(queue.remove(0));
                        }
                        return Ok(msg.body);
                    }
                }
                _ => ()
            };
            queue.push(msg);
        }
    }

    pub fn read_msg(&mut self) -> Result<Message,Error> {
        match self.queue.get(0) {
            Some(_) => return Ok(self.queue.remove(0)),
            _ => ()
        };
        let mut buf = Vec::new();
        let sock = self.get_sock();

        // Read and demarshal the fixed portion of the header
        try!(read_exactly(sock, &mut buf, 12));
        let mut offset = 0;
        let mut sig = "(yyyyuu)".to_string();
        let header = match try!(demarshal(&mut buf, &mut offset, &mut sig)) {
            Value::Struct(x) => x,
            x => panic!("Demarshal didn't return what we asked for: {:?}", x)
        };

        let mut v = header.objects;
        let mut msg : Message = Default::default();
        let endian : u8 = DBusDecoder::decode(v.remove(0)).unwrap();
        if endian == 'B' as u8 {
            msg.big_endian = true;
        }
        msg.message_type = message::MessageType(DBusDecoder::decode(v.remove(0)).unwrap());
        msg.flags = DBusDecoder::decode::<u8>(v.remove(0)).unwrap();
        msg.version = DBusDecoder::decode::<u8>(v.remove(0)).unwrap();
        let body_len = DBusDecoder::decode::<u32>(v.remove(0)).unwrap();
        msg.serial = DBusDecoder::decode::<u32>(v.remove(0)).unwrap();

        // Read array length
        try!(read_exactly(sock, &mut buf, 4));
        // demarshal consumes the buf, so save a copy for when we demarshal the entire array
        let mut buf_copy = buf.clone();
        offset = 12;
        sig = "u".to_string();
        let data = demarshal(&mut buf, &mut offset, &mut sig).ok().unwrap();
        let arr_len = DBusDecoder::decode::<u32>(data).unwrap() as usize;

        // Make buf_copy big enough for the entire array, and fill it
        buf_copy.reserve(arr_len);
        if try!(sock.take(arr_len as u64).read_to_end(&mut buf_copy)) != arr_len {
            return Err(Error::Disconnected);
        };

        offset = 12;
        sig = "a(yv)".to_string();
        let header_fields = match try!(demarshal(&mut buf_copy, &mut offset, &mut sig)) {
            Value::Array(x) => x,
            x => panic!("Demarshal didn't return what we asked for: {:?}", x)
        };

        msg.headers = HashMap::new();
        for i in header_fields.objects {
            let mut st = match i {
                Value::Struct(x) => x,
                x => panic!("Demarshal didn't return what we asked for: {:?}", x)
            };
            let val = st.objects.remove(1);
            let code = DBusDecoder::decode::<u8>(st.objects.remove(0)).unwrap();
            msg.headers.insert(code, val);
        }

        // Read the padding, if any
        let trailing_pad = 8 - (offset % 8);
        if trailing_pad % 8 != 0 {
            try!(read_exactly(sock, &mut buf, trailing_pad));
        }

        // Finally, read the entire body
        if body_len > 0 {
            let v = match msg.headers.get(&(HeaderFieldName::Signature as u8)) {
                Some(&Value::Variant(ref x)) => x,
                _ => return Err(Error::DemarshalError(DemarshalError::BadSignature))
            };

            let sigval = match v.object.deref() {
                &Value::BasicValue(BasicValue::Signature(ref x)) => x,
                _ => return Err(Error::DemarshalError(DemarshalError::BadSignature))
            };

            let mut body = Vec::new();
            try!(read_exactly(sock, &mut body, body_len as usize));

            let mut sig = "(".to_string() + &sigval.0 + ")";
            offset = 0;
            let objs = match try!(demarshal(&mut body, &mut offset, &mut sig)) {
                Value::Struct(x) => x.objects,
                x => panic!("Didn't get a struct: {:?}", x)
            };
            for x in objs {
                msg.body.push(x);
            }
        }

        Ok(msg)
    }
}

#[test]
fn test_connect () {
    let mut conn = Connection::connect_uds("/var/run/dbus/system_bus_socket").unwrap();
    let mut msg = message::create_method_call("org.freedesktop.DBus", "/org/freedesktop/DBus",
                                          "org.freedesktop.DBus", "ListNames");
    let resp = conn.call_sync(&mut msg).unwrap();
    println!("ListNames: {:?}", resp);
}

#[cfg(tcp)]
#[test]
fn test_tcp () {
    let mut conn = Connection::connect_tcp("localhost:12345").unwrap();
    let mut msg = message::create_method_call("org.freedesktop.DBus", "/org/freedesktop/DBus",
                                          "org.freedesktop.DBus", "ListNames");
    conn.send(&mut msg).ok();
    let msg = conn.read_msg().unwrap();
    println!("{:?}", msg.body);
    //loop {
    //    conn.read_msg().unwrap();
    //}
}
<commit_msg>connection.rs: avoid a copy<commit_after>use std::net::TcpStream;
use std::collections::HashMap;
use std::io;
use std::io::{Read,Write};
use std::ops::Deref;
use libc;

use unix_socket::UnixStream;
use rustc_serialize::hex::ToHex;
use dbus_serialize::types::{Value,BasicValue};
use dbus_serialize::decoder::DBusDecoder;

use message;
use message::{Message,HeaderFieldName,MessageBuf};
use demarshal::{demarshal,DemarshalError};
use marshal::Marshal;

trait StreamSocket : Read + Write { }

impl StreamSocket for TcpStream { }
impl StreamSocket for UnixStream { }

enum Socket {
    Tcp(TcpStream),
    Uds(UnixStream)
}

pub struct Connection {
    sock: Socket,
    next_serial: u32,
    queue: Vec<Message>,
}

#[derive(Debug)]
pub enum Error {
    Disconnected,
    IOError(io::Error),
    DemarshalError(DemarshalError),
    BadData,
    AuthFailed,
}

impl From<io::Error> for Error {
    fn from(x: io::Error) -> Self {
        Error::IOError(x)
    }
}

impl From<DemarshalError> for Error {
    fn from(x: DemarshalError) -> Self {
        Error::DemarshalError(x)
    }
}

fn read_exactly(sock: &mut StreamSocket, buf: &mut Vec<u8>, len: usize) -> Result<(),Error> {
    buf.truncate(0);
    buf.reserve(len);
    if try!(sock.take(len as u64).read_to_end(buf)) != len {
        return Err(Error::Disconnected);
    }
    Ok(())
}

fn read_line(sock: &mut StreamSocket) -> Result<String,Error> {
    let mut line = "".to_string();
    let mut last = '\0';

    loop {
        let mut buf = vec![0];
        match sock.read(&mut buf) {
            Ok(x) if x > 0 => (),
            _ => return Err(Error::Disconnected)
        };
        let chr = buf[0] as char;
        line.push(chr);
        if chr == '\n' && last == '\r' {
            break;
        }
        last = chr;
    }
    Ok(line)
}

impl Connection {
    fn get_sock(&mut self) -> &mut StreamSocket {
        match self.sock {
            Socket::Tcp(ref mut x) => x,
            Socket::Uds(ref mut x) => x
        }
    }

    fn auth_anonymous(&mut self) -> Result<(),Error> {
        let sock = self.get_sock();

        // Authenticate to the daemon
        let buf = vec![0];
        try!(sock.write_all(&buf));

        try!(sock.write_all(b"AUTH ANONYMOUS 6c69626462757320312e382e3132\r\n"));

        // Read response
        let resp = try!(read_line(sock));
        if !resp.starts_with("OK ") {
            return Err(Error::AuthFailed);
        }

        // Ready for action
        try!(sock.write_all(b"BEGIN\r\n"));
        Ok(())
    }

    fn auth_external(&mut self) -> Result<(),Error> {
        let sock = self.get_sock();

        // Authenticate to the daemon
        let buf = vec![0];
        try!(sock.write_all(&buf));

        let uid = unsafe {
            libc::funcs::posix88::unistd::getuid()
        };
        let uid_str = uid.to_string();
        let uid_hex = uid_str.into_bytes().to_hex();
        let cmd = "AUTH EXTERNAL ".to_string() + &uid_hex + "\r\n";
        try!(sock.write_all(&cmd.into_bytes()));

        // Read response
        let resp = try!(read_line(sock));
        if !resp.starts_with("OK ") {
            return Err(Error::AuthFailed);
        }

        // Ready for action
        try!(sock.write_all(b"BEGIN\r\n"));
        Ok(())
    }

    fn say_hello(&mut self) -> Result<(),Error> {
        let mut msg = message::create_method_call("org.freedesktop.DBus",
                                                  "/org/freedesktop/DBus",
                                                  "org.freedesktop.DBus",
                                                  "Hello");
        try!(self.call_sync(&mut msg));
        // XXX: validate Hello reply
        Ok(())
    }

    pub fn connect_uds(addr: &str) -> Result<Connection,Error> {
        let sock = try!(UnixStream::connect(addr));
        let mut conn = Connection {
            sock: Socket::Uds(sock),
            queue: Vec::new(),
            next_serial: 1
        };

        try!(conn.auth_external());
        try!(conn.say_hello());
        Ok(conn)
    }

    pub fn connect_tcp(addr: &str) -> Result<Connection,Error> {
        let sock = try!(TcpStream::connect(addr));
        let mut conn = Connection {
            sock: Socket::Tcp(sock),
            queue: Vec::new(),
            next_serial: 1
        };

        try!(conn.auth_anonymous());
        try!(conn.say_hello());
        Ok(conn)
    }

    pub fn send(&mut self, mbuf: &mut MessageBuf) -> Result<u32, Error> {
        let mut msg = &mut mbuf.0;
        // A minimum header with no body is 16 bytes
        let mut len = msg.len() as u32;
        if len < 16 {
            return Err(Error::BadData);
        }

        // Get the current length from the message, which only include the length of the header.
        // That field should actually be the length of only the body, so update that now
        let old_len = message::get_length(msg);
        len -= old_len;
        let mut buf = Vec::new();
        len.dbus_encode(&mut buf);
        // Update the message with a correct serial number, as well
        let this_serial = self.next_serial;
        self.next_serial += 1;
        this_serial.dbus_encode(&mut buf);
        message::set_length(msg, &buf);

        let sock = self.get_sock();
        try!(sock.write_all(msg));
        Ok(this_serial)
    }

    pub fn call_sync(&mut self, mbuf: &mut MessageBuf) -> Result<Vec<Value>,Error> {
        // XXX: assert that this is a method call with reply
        let serial = try!(self.send(mbuf));
        // We need a local queue so that read_msg doesn't just give us
        // the same one over and over
        let mut queue = Vec::new();
        loop {
            let mut msg = try!(self.read_msg());
            match msg.headers.remove(&(HeaderFieldName::ReplySerial as u8)) {
                Some(Value::Variant(x)) => {
                    let obj : Value = *x.object;
                    let reply_serial : u32 = DBusDecoder::decode(obj).unwrap();
                    if reply_serial == serial {
                        // Move our queued messages into the Connection's queue
                        for _ in 0..queue.len() {
                            self.queue.push(queue.remove(0));
                        }
                        return Ok(msg.body);
                    }
                }
                _ => ()
            };
            queue.push(msg);
        }
    }

    pub fn read_msg(&mut self) -> Result<Message,Error> {
        match self.queue.get(0) {
            Some(_) => return Ok(self.queue.remove(0)),
            _ => ()
        };
        let mut buf = Vec::new();
        let sock = self.get_sock();

        // Read and demarshal the fixed portion of the header
        try!(read_exactly(sock, &mut buf, 12));
        let mut offset = 0;
        let mut sig = "(yyyyuu)".to_string();
        let header = match try!(demarshal(&mut buf, &mut offset, &mut sig)) {
            Value::Struct(x) => x,
            x => panic!("Demarshal didn't return what we asked for: {:?}", x)
        };

        let mut v = header.objects;
        let mut msg : Message = Default::default();
        let endian : u8 = DBusDecoder::decode(v.remove(0)).unwrap();
        if endian == 'B' as u8 {
            msg.big_endian = true;
        }
        msg.message_type = message::MessageType(DBusDecoder::decode(v.remove(0)).unwrap());
        msg.flags = DBusDecoder::decode::<u8>(v.remove(0)).unwrap();
        msg.version = DBusDecoder::decode::<u8>(v.remove(0)).unwrap();
        let body_len = DBusDecoder::decode::<u32>(v.remove(0)).unwrap();
        msg.serial = DBusDecoder::decode::<u32>(v.remove(0)).unwrap();

        // Read array length
        try!(read_exactly(sock, &mut buf, 4));
        // demarshal consumes the buf, so save a copy for when we demarshal the entire array
        let mut buf_copy = buf.clone();
        offset = 12;
        sig = "u".to_string();
        let data = demarshal(&mut buf, &mut offset, &mut sig).ok().unwrap();
        let arr_len = DBusDecoder::decode::<u32>(data).unwrap() as usize;

        // Make buf_copy big enough for the entire array, and fill it
        buf_copy.reserve(arr_len);
        if try!(sock.take(arr_len as u64).read_to_end(&mut buf_copy)) != arr_len {
            return Err(Error::Disconnected);
        };

        offset = 12;
        sig = "a(yv)".to_string();
        let header_fields = match try!(demarshal(&mut buf_copy, &mut offset, &mut sig)) {
            Value::Array(x) => x,
            x => panic!("Demarshal didn't return what we asked for: {:?}", x)
        };

        msg.headers = HashMap::new();
        for i in header_fields.objects {
            let mut st = match i {
                Value::Struct(x) => x,
                x => panic!("Demarshal didn't return what we asked for: {:?}", x)
            };
            let val = st.objects.remove(1);
            let code = DBusDecoder::decode::<u8>(st.objects.remove(0)).unwrap();
            msg.headers.insert(code, val);
        }

        // Read the padding, if any
        let trailing_pad = 8 - (offset % 8);
        if trailing_pad % 8 != 0 {
            try!(read_exactly(sock, &mut buf, trailing_pad));
        }

        // Finally, read the entire body
        if body_len > 0 {
            let v = match msg.headers.get(&(HeaderFieldName::Signature as u8)) {
                Some(&Value::Variant(ref x)) => x,
                _ => return Err(Error::DemarshalError(DemarshalError::BadSignature))
            };

            let sigval = match v.object.deref() {
                &Value::BasicValue(BasicValue::Signature(ref x)) => x,
                _ => return Err(Error::DemarshalError(DemarshalError::BadSignature))
            };

            let mut body = Vec::new();
            try!(read_exactly(sock, &mut body, body_len as usize));

            let mut sig = "(".to_string() + &sigval.0 + ")";
            offset = 0;
            let objs = match try!(demarshal(&mut body, &mut offset, &mut sig)) {
                Value::Struct(x) => x.objects,
                x => panic!("Didn't get a struct: {:?}", x)
            };
            for x in objs {
                msg.body.push(x);
            }
        }

        Ok(msg)
    }
}

#[test]
fn test_connect () {
    let mut conn = Connection::connect_uds("/var/run/dbus/system_bus_socket").unwrap();
    let mut msg = message::create_method_call("org.freedesktop.DBus", "/org/freedesktop/DBus",
                                          "org.freedesktop.DBus", "ListNames");
    let resp = conn.call_sync(&mut msg).unwrap();
    println!("ListNames: {:?}", resp);
}

#[cfg(tcp)]
#[test]
fn test_tcp () {
    let mut conn = Connection::connect_tcp("localhost:12345").unwrap();
    let mut msg = message::create_method_call("org.freedesktop.DBus", "/org/freedesktop/DBus",
                                          "org.freedesktop.DBus", "ListNames");
    conn.send(&mut msg).ok();
    let msg = conn.read_msg().unwrap();
    println!("{:?}", msg.body);
    //loop {
    //    conn.read_msg().unwrap();
    //}
}
<|endoftext|>


<commit_before>use std::fs::File;
use std::io::prelude::*;
use std::io::BufReader;

pub fn obsecurity_1(file: &str) -> usize {
    parse_rooms(file)
}

pub fn obsecurity_2(file: &str) -> usize {
    parse_rooms(file)
}

fn parse_rooms(file: &str) -> usize {
    let input = File::open(file).expect("File open fail.");
    let reader = BufReader::new(input);

    let lines: Vec<String> = reader.lines()
        .filter_map(Result::ok)
        .collect();

    for line in lines {
        let words = line.split("-").collect::<Vec<&str>>();
        println!("{:?}", words);
    }
    9
}

struct Room {
    words: Vec<String>,
    id: usize,
    checksum: String,
}
<commit_msg>Progress splitting up into chars.<commit_after>use std::fs::File;
use std::io::prelude::*;
use std::io::BufReader;
use std::collections::BTreeMap;

pub fn obsecurity_1(file: &str) -> usize {
    parse_rooms(file)
}

pub fn obsecurity_2(file: &str) -> usize {
    parse_rooms(file)
}

fn parse_rooms(file: &str) -> usize {
    let input = File::open(file).expect("File open fail.");
    let reader = BufReader::new(input);

    let lines: Vec<String> = reader.lines()
        .filter_map(Result::ok)
        .collect();

    let mut sum = 0;
    for line in lines {
        let mut words = line.split("-").collect::<Vec<&str>>();

        if let Some(id_checksum) = words.pop() {
            if let Some(i) = id_checksum.find("[") {
                let (id, checksum) = id_checksum.split_at(i);
                let checksum = checksum.trim_matches(|c| c == '[' || c == ']')
                    .split("")
                    .flat_map(str::chars)
                    .collect::<Vec<char>>();
                println!("{} {:?}", id, checksum);
                let letters = words.iter().flat_map(|s| s.chars()).collect::<Vec<char>>();
                println!("{:?}", letters);
                let mut count = BTreeMap::new();
                for c in letters {
                    *count.entry(c).or_insert(0) += 1;
                }
                if let Ok(id) = id.parse::<usize>() {
                    sum += id;
                }
            }
        };
    }
    9
}
<|endoftext|>


<commit_before>pub mod search;

use std::thread;
use std::cmp::min;
use std::sync::Arc;
use std::sync::mpsc::{channel, Sender, Receiver};
use std::time::SystemTime;
use std::num::Wrapping;
use basetypes::*;
use uci::{UciEngine, UciEngineFactory, EngineReply, OptionName, OptionDescription};
use position::Position;
use chess_move::*;
use tt::*;

// use rand;
// use rand::distributions::{Sample, Range};
// let mut rng = rand::thread_rng();
// let mut between = Range::new(0, 10);
// let x = between.sample(&mut rng);

const VERSION: &'static str = "0.1";
const MAX_DEPTH: u8 = 126;

/// Implements `UciEngine` trait.
pub struct Engine {
    position: Position,
    pondering_is_allowed: bool,
    is_thinking: bool,
    is_pondering: bool,
    curr_depth: u8,
    replies: Vec<EngineReply>,
    tt: Arc<TranspositionTable>,
    commands: Sender<search::Command>,
    reports: Receiver<search::Report>,
    search_id: usize,
    thinking_since: SystemTime,
    searched_nodes: NodeCount,
    searched_time: u64, // milliseconds
    stop_when: TimeManagement,
}

impl Engine {
    /// Creates a new instance.
    ///
    /// `tt_size_mb` is the preferred size of the transposition
    /// table in Mbytes.
    pub fn new(tt_size_mb: usize) -> Engine {
        let mut tt = TranspositionTable::new();
        tt.resize(tt_size_mb);
        let tt = Arc::new(tt);
        let (commands_tx, commands_rx) = channel();
        let (reports_tx, reports_rx) = channel();
        let tt2 = tt.clone();
        thread::spawn(move || {
            search::run_deepening(tt2, commands_rx, reports_tx);
        });

        Engine {
            position: Position::from_fen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w QKqk - 0 \
                                          1")
                          .ok()
                          .unwrap(),
            pondering_is_allowed: false,
            is_thinking: false,
            is_pondering: false,
            curr_depth: 0,
            replies: vec![],
            tt: tt,
            commands: commands_tx,
            reports: reports_rx,
            search_id: 0,
            thinking_since: SystemTime::now(),
            searched_nodes: 0,
            searched_time: 0,
            stop_when: TimeManagement::Infinite,
        }
    }
}

impl UciEngine for Engine {
    fn set_option(&mut self, name: &str, value: &str) {
        match name {
            // Tells the engine that it will be allowed to ponder.
            // This option is needed because the engine might change
            // its time management algorithm when pondering is
            // allowed.
            "Ponder" => {
                self.pondering_is_allowed = value == "true";
            }

            // An invalid option. Notice that we do not support
            // re-sizing of the transposition table once the engine
            // had started.
            _ => (),
        }
    }

    fn new_game(&mut self) {
        // Clearing the transposition table would not change anything,
        // so we do nothing.
    }

    fn position(&mut self, fen: &str, moves: &mut Iterator<Item = &str>) {
        if !self.is_thinking {
            if let Ok(p) = Position::from_history(fen, moves) {
                self.position = p;
                self.tt.new_search();
            }
        }
    }

    #[allow(unused_variables)]
    fn go(&mut self,
          searchmoves: Option<Vec<String>>,
          ponder: bool,
          wtime: Option<u64>,
          btime: Option<u64>,
          winc: Option<u64>,
          binc: Option<u64>,
          movestogo: Option<u64>,
          depth: Option<u64>,
          nodes: Option<u64>,
          mate: Option<u64>,
          movetime: Option<u64>,
          infinite: bool) {
        if !self.is_thinking {
            // Note: We ignore the "searchmoves" parameter.

            self.is_thinking = true;
            self.is_pondering = ponder;
            self.search_id = (Wrapping(self.search_id) + Wrapping(1)).0;
            self.thinking_since = SystemTime::now();
            self.searched_nodes = 0;
            self.searched_time = 0;
            self.curr_depth = 0;

            // Figure out when we should stop thinking.
            let (time, inc) = if self.position.board().to_move() == WHITE {
                (wtime, winc.unwrap_or(0))
            } else {
                (btime, binc.unwrap_or(0))
            };
            self.stop_when = if infinite {
                TimeManagement::Infinite
            } else if movetime.is_some() {
                TimeManagement::MoveTime(movetime.unwrap())
            } else if nodes.is_some() {
                TimeManagement::Nodes(nodes.unwrap())
            } else if depth.is_some() {
                TimeManagement::Depth(min(depth.unwrap(), MAX_DEPTH as u64) as u8)
            } else {
                // TODO: Robert Hyatt proposes this:
                //
                // nMoves =  min( numberOfMovesOutOfBook, 10 )
                // factor = 2 - nMoves / 10
                // target = timeLeft / numberOfMovesUntilNextTimeControl
                // time   = factor * target
                let time = time.unwrap_or(0);
                let movestogo = movestogo.unwrap_or(40);
                let movetime = (time + inc * movestogo) / movestogo;
                TimeManagement::MoveTimeHint(min(movetime, time / 2))
            };

            // Start a search with the maximum possible depth.
            self.commands
                .send(search::Command::Search {
                    search_id: self.search_id,
                    position: self.position.clone(),
                    depth: MAX_DEPTH,
                    lower_bound: -20000,
                    upper_bound: 20000,
                })
                .unwrap();
        }
    }

    fn ponder_hit(&mut self) {
        if self.is_thinking && self.is_pondering {
            self.is_pondering = false;
        }
    }

    fn stop(&mut self) {
        if self.is_thinking {
            self.commands.send(search::Command::Stop).unwrap();
            let best_move = self.get_best_move();
            self.replies.push(EngineReply::BestMove {
                best_move: best_move,
                ponder_move: None,
            });
            self.is_thinking = false;
        }
    }

    fn is_thinking(&self) -> bool {
        self.is_thinking
    }

    fn get_reply(&mut self) -> Option<EngineReply> {
        // TODO: implement panic mode for MoveTimeHint management.
        if self.is_thinking && !self.is_pondering {
            if match self.stop_when {
                TimeManagement::MoveTime(t) => self.searched_time > t,
                TimeManagement::MoveTimeHint(t) => self.searched_time > t,
                TimeManagement::Nodes(n) => self.searched_nodes > n,
                TimeManagement::Depth(d) => self.curr_depth > d,
                TimeManagement::Infinite => false,
            } {
                self.stop();
            }
        }

        // Empty the reports queue.
        while let Ok(report) = self.reports.try_recv() {
            if self.is_thinking {
                match report {
                    search::Report::Progress { search_id, searched_nodes, depth }
                        if search_id == self.search_id => {

                        let thinking_duration = self.thinking_since.elapsed().unwrap();
                        self.searched_nodes = searched_nodes;
                        self.searched_time = 1000 * thinking_duration.as_secs() +
                                             (thinking_duration.subsec_nanos() / 1000000) as u64;
                        if self.curr_depth < depth {
                            // We are done with the old depth, so we
                            // report the current primary variation.
                            self.report_pv();
                            self.curr_depth = depth;
                        }
                    }
                    search::Report::Done { search_id, .. } if search_id == self.search_id => {
                        if !self.is_pondering {
                            if let TimeManagement::Infinite = self.stop_when {
                                ()
                            } else {
                                self.stop()
                            }
                        }
                    }
                    _ => (),
                }
            }
        }

        if self.replies.len() > 0 {
            Some(self.replies.remove(0))
        } else {
            None
        }
    }
}

impl Engine {
    fn report_pv(&mut self) {
        let mut prev_move = None;
        let mut p = self.position.clone();
        let mut pv = Vec::new();
        let mut value = -20000;
        let mut bound = BOUND_LOWER;

        // Extract the primary variation, the value, and the bound
        // from the transposition table.
        while let Some(entry) = self.tt.probe(p.hash()) {
            if pv.len() < self.curr_depth as usize && entry.bound() != BOUND_NONE {
                if let Some(m) = prev_move {
                    pv.push(m);
                }
                if pv.len() & 1 == 0 {
                    value = entry.value();
                    bound = entry.bound();
                } else {
                    // In this case the value stored in `entry` is
                    // from other side's perspective.
                    value = -entry.value();
                    bound = match entry.bound() {
                        BOUND_UPPER => BOUND_LOWER,
                        BOUND_LOWER => BOUND_UPPER,
                        x => x,
                    };
                }
                if bound == BOUND_EXACT {
                    if let Some(m) = p.try_move_digest(entry.move16()) {
                        if p.do_move(m) && !p.is_repeated() {
                            prev_move = Some(m);
                            continue;
                        }
                    }
                }
            }
            break;
        }

        // Send the extracted info to the GUI.
        let score_suffix = match bound {
            BOUND_EXACT => "",
            BOUND_UPPER => " upperbound",
            BOUND_LOWER => " lowerbound",
            _ => panic!("unexpected bound type"),
        };
        let nps = if self.searched_time == 0 {
            0
        } else {
            1000 * self.searched_nodes / self.searched_time
        };
        let mut pv_string = String::new();
        for m in pv {
            pv_string.push(' ');
            pv_string.push_str(&m.notation());
        }
        self.replies.push(EngineReply::Info(vec![
            ("depth".to_string(), format!("{}", self.curr_depth)),
            ("score".to_string(), format!("cp {}{}", value, score_suffix)),
            ("time".to_string(), format!("{}", self.searched_time)),
            ("nodes".to_string(), format!("{}", self.searched_nodes)),
            ("nps".to_string(), format!("{}", nps)),
            ("pv".to_string(), pv_string),
        ]));
    }

    fn get_best_move(&mut self) -> String {
        let mut m = match self.tt.probe(self.position.hash()) {
            Some(entry) => entry.move16(),
            None => 0,
        };
        if m == 0 {
            // Pick the first legal move.
            let mut first_legal_move = Move::invalid();
            let mut v = MoveStack::new();
            self.position.generate_moves(&mut v);
            while let Some(x) = v.pop() {
                if self.position.do_move(x) {
                    self.position.undo_move();
                    first_legal_move = x;
                    break;
                }
            }
            m = first_legal_move.digest();
        }
        if m != 0 {
            let move_type = move_type(m);
            let orig_square = orig_square(m);
            let dest_square = dest_square(m);
            let promoted_piece = match aux_data(m) {
                0 => "q",
                1 => "r",
                2 => "b",
                3 => "n",
                _ => panic!("invalid promoted piece code"),
            };
            format!("{}{}{}",
                    notation(orig_square),
                    notation(dest_square),
                    if move_type == MOVE_PROMOTION {
                        promoted_piece
                    } else {
                        ""
                    })

        } else {
            "0000".to_string()
        }
    }
}

/// Implements `UciEngineFactory` trait.
pub struct EngineFactory;

impl UciEngineFactory<Engine> for EngineFactory {
    fn name(&self) -> String {
        format!("Alcibiades {}", VERSION)
    }

    fn author(&self) -> String {
        "Evgeni Pandurski".to_string()
    }

    fn options(&self) -> Vec<(OptionName, OptionDescription)> {
        vec![
            // TODO: Calculate a sane limit for the hash size.
            ("Hash".to_string(), OptionDescription::Spin { min: 1, max: 2048, default: 16 }),
            ("Ponder".to_string(), OptionDescription::Check { default: false }),
        ]
    }

    fn create(&self, hash_size_mb: Option<usize>) -> Engine {
        Engine::new(hash_size_mb.unwrap_or(16))
    }
}

enum TimeManagement {
    MoveTime(u64),
    MoveTimeHint(u64),
    Nodes(NodeCount),
    Depth(u8),
    Infinite,
}
<commit_msg>beautification<commit_after>pub mod search;

use std::thread;
use std::cmp::min;
use std::sync::Arc;
use std::sync::mpsc::{channel, Sender, Receiver};
use std::time::SystemTime;
use std::num::Wrapping;
use basetypes::*;
use uci::{UciEngine, UciEngineFactory, EngineReply, OptionName, OptionDescription};
use position::Position;
use chess_move::*;
use tt::*;

// use rand;
// use rand::distributions::{Sample, Range};
// let mut rng = rand::thread_rng();
// let mut between = Range::new(0, 10);
// let x = between.sample(&mut rng);

const VERSION: &'static str = "0.1";
const MAX_DEPTH: u8 = 126;

/// Implements `UciEngine` trait.
pub struct Engine {
    position: Position,
    pondering_is_allowed: bool,
    is_thinking: bool,
    is_pondering: bool,
    curr_depth: u8,
    replies: Vec<EngineReply>,
    tt: Arc<TranspositionTable>,
    commands: Sender<search::Command>,
    reports: Receiver<search::Report>,
    search_id: usize,
    thinking_since: SystemTime,
    searched_nodes: NodeCount,
    searched_time: u64, // milliseconds
    stop_when: TimeManagement,
}

impl Engine {
    /// Creates a new instance.
    ///
    /// `tt_size_mb` is the preferred size of the transposition
    /// table in Mbytes.
    pub fn new(tt_size_mb: usize) -> Engine {
        let mut tt = TranspositionTable::new();
        tt.resize(tt_size_mb);
        let tt = Arc::new(tt);
        let (commands_tx, commands_rx) = channel();
        let (reports_tx, reports_rx) = channel();
        let tt2 = tt.clone();
        thread::spawn(move || {
            search::run_deepening(tt2, commands_rx, reports_tx);
        });

        Engine {
            position: Position::from_fen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w QKqk - 0 \
                                          1")
                          .ok()
                          .unwrap(),
            pondering_is_allowed: false,
            is_thinking: false,
            is_pondering: false,
            curr_depth: 0,
            replies: vec![],
            tt: tt,
            commands: commands_tx,
            reports: reports_rx,
            search_id: 0,
            thinking_since: SystemTime::now(),
            searched_nodes: 0,
            searched_time: 0,
            stop_when: TimeManagement::Infinite,
        }
    }
}

impl UciEngine for Engine {
    fn set_option(&mut self, name: &str, value: &str) {
        match name {
            // Tells the engine that it will be allowed to ponder.
            // This option is needed because the engine might change
            // its time management algorithm when pondering is
            // allowed.
            "Ponder" => {
                self.pondering_is_allowed = value == "true";
            }

            // An invalid option. Notice that we do not support
            // re-sizing of the transposition table once the engine
            // had started.
            _ => (),
        }
    }

    fn new_game(&mut self) {
        // Clearing the transposition table would not change anything,
        // so we do nothing.
    }

    fn position(&mut self, fen: &str, moves: &mut Iterator<Item = &str>) {
        if !self.is_thinking {
            if let Ok(p) = Position::from_history(fen, moves) {
                self.position = p;
                self.tt.new_search();
            }
        }
    }

    #[allow(unused_variables)]
    fn go(&mut self,
          searchmoves: Option<Vec<String>>,
          ponder: bool,
          wtime: Option<u64>,
          btime: Option<u64>,
          winc: Option<u64>,
          binc: Option<u64>,
          movestogo: Option<u64>,
          depth: Option<u64>,
          nodes: Option<u64>,
          mate: Option<u64>,
          movetime: Option<u64>,
          infinite: bool) {
        if !self.is_thinking {
            // Note: We ignore the "searchmoves" parameter.

            self.is_thinking = true;
            self.is_pondering = ponder;
            self.search_id = (Wrapping(self.search_id) + Wrapping(1)).0;
            self.thinking_since = SystemTime::now();
            self.searched_nodes = 0;
            self.searched_time = 0;
            self.curr_depth = 0;

            // Figure out when we should stop thinking.
            let (time, inc) = if self.position.board().to_move() == WHITE {
                (wtime, winc.unwrap_or(0))
            } else {
                (btime, binc.unwrap_or(0))
            };
            self.stop_when = if infinite {
                TimeManagement::Infinite
            } else if movetime.is_some() {
                TimeManagement::MoveTime(movetime.unwrap())
            } else if nodes.is_some() {
                TimeManagement::Nodes(nodes.unwrap())
            } else if depth.is_some() {
                TimeManagement::Depth(min(depth.unwrap(), MAX_DEPTH as u64) as u8)
            } else {
                // TODO: Robert Hyatt proposes this:
                //
                // nMoves =  min( numberOfMovesOutOfBook, 10 )
                // factor = 2 - nMoves / 10
                // target = timeLeft / numberOfMovesUntilNextTimeControl
                // time   = factor * target
                let time = time.unwrap_or(0);
                let movestogo = movestogo.unwrap_or(40);
                let movetime = (time + inc * movestogo) / movestogo;
                TimeManagement::MoveTimeHint(min(movetime, time / 2))
            };

            // Start a search with the maximum possible depth.
            self.commands
                .send(search::Command::Search {
                    search_id: self.search_id,
                    position: self.position.clone(),
                    depth: MAX_DEPTH,
                    lower_bound: -20000,
                    upper_bound: 20000,
                })
                .unwrap();
        }
    }

    fn ponder_hit(&mut self) {
        if self.is_thinking && self.is_pondering {
            self.is_pondering = false;
        }
    }

    fn stop(&mut self) {
        if self.is_thinking {
            self.commands.send(search::Command::Stop).unwrap();
            let best_move = self.get_best_move();
            self.replies.push(EngineReply::BestMove {
                best_move: best_move,
                ponder_move: None,
            });
            self.is_thinking = false;
        }
    }

    fn is_thinking(&self) -> bool {
        self.is_thinking
    }

    fn get_reply(&mut self) -> Option<EngineReply> {
        // TODO: implement panic mode for MoveTimeHint management.
        if self.is_thinking && !self.is_pondering {
            if match self.stop_when {
                TimeManagement::MoveTime(t) => self.searched_time > t,
                TimeManagement::MoveTimeHint(t) => self.searched_time > t,
                TimeManagement::Nodes(n) => self.searched_nodes > n,
                TimeManagement::Depth(d) => self.curr_depth > d,
                TimeManagement::Infinite => false,
            } {
                self.stop();
            }
        }

        // Empty the reports queue.
        while let Ok(report) = self.reports.try_recv() {
            if self.is_thinking {
                match report {
                    search::Report::Progress { search_id, searched_nodes, depth }
                        if search_id == self.search_id => {

                        let thinking_duration = self.thinking_since.elapsed().unwrap();
                        self.searched_nodes = searched_nodes;
                        self.searched_time = 1000 * thinking_duration.as_secs() +
                                             (thinking_duration.subsec_nanos() / 1000000) as u64;
                        if self.curr_depth < depth {
                            // We are done with the old depth, so we
                            // report the current primary variation.
                            self.report_pv();
                            self.curr_depth = depth;
                        }
                    }
                    search::Report::Done { search_id, .. } if search_id == self.search_id => {
                        if !self.is_pondering {
                            if let TimeManagement::Infinite = self.stop_when {
                                ()
                            } else {
                                self.stop()
                            }
                        }
                    }
                    _ => (),
                }
            }
        }

        if self.replies.len() > 0 {
            Some(self.replies.remove(0))
        } else {
            None
        }
    }
}

impl Engine {
    // A helper method. It extracts the primary variation (PV) from
    // the transposition table (TT) and sends it to the GUI.
    fn report_pv(&mut self) {
        let mut prev_move = None;
        let mut p = self.position.clone();

        // Extract the PV, the value, and the bound from the TT.
        let mut pv = Vec::new();
        let mut value = -20000;
        let mut bound = BOUND_LOWER;
        while let Some(entry) = self.tt.probe(p.hash()) {
            if pv.len() < self.curr_depth as usize && entry.bound() != BOUND_NONE {
                if let Some(m) = prev_move {
                    pv.push(m);
                }
                if pv.len() & 1 == 0 {
                    value = entry.value();
                    bound = entry.bound();
                } else {
                    // In this case the value stored in `entry` is
                    // from other side's perspective.
                    value = -entry.value();
                    bound = match entry.bound() {
                        BOUND_UPPER => BOUND_LOWER,
                        BOUND_LOWER => BOUND_UPPER,
                        x => x,
                    };
                }
                if bound == BOUND_EXACT {
                    if let Some(m) = p.try_move_digest(entry.move16()) {
                        if p.do_move(m) && !p.is_repeated() {
                            // Try to extend the PV.
                            prev_move = Some(m);
                            continue;
                        }
                    }
                }
            }
            break;
        }

        // Send the extracted info to the GUI.
        let value_suffix = match bound {
            BOUND_EXACT => "",
            BOUND_UPPER => " upperbound",
            BOUND_LOWER => " lowerbound",
            _ => panic!("unexpected bound type"),
        };
        let nps = if self.searched_time == 0 {
            0
        } else {
            1000 * self.searched_nodes / self.searched_time
        };
        let mut pv_string = String::new();
        for m in pv {
            pv_string.push(' ');
            pv_string.push_str(&m.notation());
        }
        self.replies.push(EngineReply::Info(vec![
            ("depth".to_string(), format!("{}", self.curr_depth)),
            ("score".to_string(), format!("cp {}{}", value, value_suffix)),
            ("time".to_string(), format!("{}", self.searched_time)),
            ("nodes".to_string(), format!("{}", self.searched_nodes)),
            ("nps".to_string(), format!("{}", nps)),
            ("pv".to_string(), pv_string),
        ]));
    }

    fn get_best_move(&mut self) -> String {
        let mut m = match self.tt.probe(self.position.hash()) {
            Some(entry) => entry.move16(),
            None => 0,
        };
        if m == 0 {
            // Pick the first legal move.
            let mut first_legal_move = Move::invalid();
            let mut v = MoveStack::new();
            self.position.generate_moves(&mut v);
            while let Some(x) = v.pop() {
                if self.position.do_move(x) {
                    self.position.undo_move();
                    first_legal_move = x;
                    break;
                }
            }
            m = first_legal_move.digest();
        }
        if m != 0 {
            let move_type = move_type(m);
            let orig_square = orig_square(m);
            let dest_square = dest_square(m);
            let promoted_piece = match aux_data(m) {
                0 => "q",
                1 => "r",
                2 => "b",
                3 => "n",
                _ => panic!("invalid promoted piece code"),
            };
            format!("{}{}{}",
                    notation(orig_square),
                    notation(dest_square),
                    if move_type == MOVE_PROMOTION {
                        promoted_piece
                    } else {
                        ""
                    })

        } else {
            "0000".to_string()
        }
    }
}

/// Implements `UciEngineFactory` trait.
pub struct EngineFactory;

impl UciEngineFactory<Engine> for EngineFactory {
    fn name(&self) -> String {
        format!("Alcibiades {}", VERSION)
    }

    fn author(&self) -> String {
        "Evgeni Pandurski".to_string()
    }

    fn options(&self) -> Vec<(OptionName, OptionDescription)> {
        vec![
            // TODO: Calculate a sane limit for the hash size.
            ("Hash".to_string(), OptionDescription::Spin { min: 1, max: 2048, default: 16 }),
            ("Ponder".to_string(), OptionDescription::Check { default: false }),
        ]
    }

    fn create(&self, hash_size_mb: Option<usize>) -> Engine {
        Engine::new(hash_size_mb.unwrap_or(16))
    }
}

enum TimeManagement {
    MoveTime(u64),
    MoveTimeHint(u64),
    Nodes(NodeCount),
    Depth(u8),
    Infinite,
}
<|endoftext|>


<commit_before>/*
    This file is a part of cargo-esr.

    Copyright (C) 2017 Mohammad AlSaleh <CE.Mohammad.AlSaleh at gmail.com>
    https://github.com/rust-alt/cargo-esr

    This Source Code Form is subject to the terms of the Mozilla Public
    License, v. 2.0. If a copy of the MPL was not distributed with this
    file, You can obtain one at <http://mozilla.org/MPL/2.0/>.
*/

use pipeliner::Pipeline;

use esr_from::{EsrFrom, DefEsrFrom};
use esr_util;
use esr_errors::Result;

#[derive(Deserialize, Debug)]
struct PullRequestInfo {
    merged_at: Option<String>,
    number: usize,
}

#[derive(Deserialize, Debug)]
struct IssueInfo {
    closed_at: Option<String>,
    number: usize,
}

#[derive(Deserialize, Debug)]
struct ContributorInfo {
    contributions: usize,
}

// =================

type RepoPullRequests = Vec<PullRequestInfo>;

impl EsrFrom for RepoPullRequests {
    fn url_from_id(id: &str) -> String {
        let url = String::from("https://api.github.com/repos/:id/pulls?state=all&per_page=100");
        url.replace(":id", id)
    }
}

type RepoClosedIssues = Vec<IssueInfo>;

impl EsrFrom for RepoClosedIssues {
    fn url_from_id(id: &str) -> String {
        let url = String::from("https://api.github.com/repos/:id/issues?\
                               state=closed&per_page=1");
        url.replace(":id", id)
    }
}

type RepoContributors = Vec<ContributorInfo>;

impl EsrFrom for RepoContributors {
    fn url_from_id(id: &str) -> String {
        let url = String::from("https://api.github.com/repos/:id/contributors?per_page=100");
        url.replace(":id", id)
    }
}

#[derive(Deserialize, Debug)]
struct RepoGeneralInfo {
    subscribers_count: usize,
    created_at: String,
    // Same as created_at if the repo is empty
    pushed_at: String,
}

impl EsrFrom for RepoGeneralInfo {
    fn url_from_id(id: &str) -> String {
        let url = String::from("https://api.github.com/repos/:id");
        url.replace(":id", id)
    }
}

// =================

pub struct RepoInfo {
    general_info: RepoGeneralInfo,
    last_100_closed_issues: RepoClosedIssues,
    last_100_pull_requests: RepoPullRequests,
    top_100_contributors: RepoContributors,
}

impl RepoInfo {
    pub fn from_id(_: &str) -> Result<Self> {
        Err("Unimplemented: use  from_id_with_token()")?
    }

    pub fn from_id_with_token(id: &str, token: &str) -> Result<Self> {
        let urls = vec![
            RepoGeneralInfo::url_from_id_and_token(id, token),
            RepoClosedIssues::url_from_id_and_token(id, token),
            RepoPullRequests::url_from_id_and_token(id, token),
            RepoContributors::url_from_id_and_token(id, token),
        ];

        // `.with_threads()` does not guarantee order. So, we use `.enumerate()` as an indexer
        let bytes = urls
            .into_iter()
            .enumerate()
            .with_threads(4)
            .map(|(idx, url)| DefEsrFrom::bytes_from_url(&url).map(|bytes| (idx, bytes)))
            .collect::<Result<Vec<_>>>()?;

        let &(_, ref bytes_general) = bytes.iter().find(|&&(idx,_)| idx == 0).ok_or("impossible")?;
        let &(_, ref bytes_closed_issues) = bytes.iter().find(|&&(idx,_)| idx == 1).ok_or("impossible")?;
        let &(_, ref bytes_pulls) = bytes.iter().find(|&&(idx,_)| idx == 2).ok_or("impossible")?;
        let &(_, ref bytes_contributors) = bytes.iter().find(|&&(idx,_)| idx == 3).ok_or("impossible")?;

        Ok(Self {
            general_info: RepoGeneralInfo::from_bytes(bytes_general)?,
            last_100_closed_issues: RepoClosedIssues::from_bytes(bytes_closed_issues)?,
            last_100_pull_requests: RepoPullRequests::from_bytes(bytes_pulls)?,
            top_100_contributors: RepoContributors::from_bytes(bytes_contributors)?,
        })
    }
}

pub struct RepoScoreInfo {
    subscribers: f64,
    contributors_up_to_100: usize,
    commits_from_upto_100_contributors: f64,
    secondary_contribution_pct: usize,
    tertiary_contribution_pct: usize,
    merged_pull_requests_in_last_100: usize,
    months_since_last_pr_merged: f64,
    months_since_last_issue_closed: f64,
    push_span_in_months: f64,
    months_since_last_push: f64,
}

impl RepoScoreInfo {
    fn from_repo_info(repo_info: &RepoInfo) -> Result<Self> {
        let general_info = &repo_info.general_info;

        // Days active, months since last push
        let push_span_in_months =esr_util::span_in_months(&general_info.created_at,
                                                          &general_info.pushed_at)?;
        let months_since_last_push = esr_util::age_in_months(&general_info.pushed_at)?;

        // Get subscribers count
        let subscribers = general_info.subscribers_count as f64;

        // Get contributor count, commit count using contributors info.
        if repo_info.top_100_contributors.is_empty() {
            Err("Empty contributors list")?;
        }

        let contributors_up_to_100 = repo_info.top_100_contributors.len();

        let commits_from_upto_100_contributors = repo_info.top_100_contributors
            .iter()
            .map(|contributor| contributor.contributions)
            .sum::<usize>() as f64;

        // Secondary and Tertiary contribution pct
        let commits = commits_from_upto_100_contributors;
        let top_committer_contrib = repo_info.top_100_contributors[0].contributions as f64 / commits;
        let second_committer_contrib = repo_info.top_100_contributors.get(1)
            .map(|c| c.contributions as f64 / commits)
            .unwrap_or(0_f64);

        let secondary_contribution_pct = ((1.0 - top_committer_contrib) * 100.0).ceil() as usize;
        let tertiary_contribution_pct = ((1.0 - top_committer_contrib - second_committer_contrib) * 100.0).ceil() as usize;

        // merged pull requests in last 100, months since last merged
        let merged_pull_requests_in_last_100 = repo_info.last_100_pull_requests
            .iter()
            .filter(|pr| pr.merged_at.is_some())
            .count();

        let last_pr_merged_opt = repo_info.last_100_pull_requests
            .iter()
            .filter(|pr| pr.merged_at.is_some())
            .nth(0);

        let months_since_last_pr_merged = match last_pr_merged_opt {
            Some(pr) => esr_util::age_in_months(pr.merged_at.as_ref().ok_or("Impossible")?)?,
            None => esr_util::age_in_months(&general_info.created_at)?,
        };

        // months since last closed
        let last_issue_closed_opt = repo_info.last_100_closed_issues.get(0);

        let months_since_last_issue_closed = match last_issue_closed_opt {
            Some(issue) => esr_util::age_in_months(issue.closed_at.as_ref().ok_or("Impossible")?)?,
            None => esr_util::age_in_months(&general_info.created_at)?,
        };

        // Done
        Ok(Self {
            subscribers,
            contributors_up_to_100,
            commits_from_upto_100_contributors,
            secondary_contribution_pct,
            tertiary_contribution_pct,
            push_span_in_months,
            merged_pull_requests_in_last_100,
            months_since_last_pr_merged,
            months_since_last_issue_closed,
            months_since_last_push,
        })
    }

    fn mk_score(&self) -> (Vec<(String, String, String)>, f64, f64) {
        let mut positive_score = 0.0;
        let mut negative_score = 0.0;
        let mut table = Vec::with_capacity(9);

        // +ve
        score_add!(table, positive_score, self.subscribers.powf(0.5), 8.0);
        score_add!(table, positive_score, self.contributors_up_to_100, 3.0);
        score_add!(table,
                   positive_score,
                   self.commits_from_upto_100_contributors.powf(0.5),
                   2.0);

        // We only take secondary/tertiary contribution into account if the repo has >= 50 commits
        if self.commits_from_upto_100_contributors >= 50 {
            score_add!(table, positive_score, self.secondary_contribution_pct, 2.0);
            score_add!(table, positive_score, self.tertiary_contribution_pct, 3.0);
        }

        score_add!(table,
                   positive_score,
                   self.push_span_in_months.powf(0.5),
                   5.0);
        score_add!(table,
                   positive_score,
                   self.merged_pull_requests_in_last_100,
                   2.0);

        // -ve
        score_add!(table,
                   negative_score,
                   self.months_since_last_pr_merged.powf(1.5),
                   -1.0);
        score_add!(table,
                   negative_score,
                   self.months_since_last_issue_closed.powf(1.5),
                   -1.0);
        score_add!(table,
                   negative_score,
                   self.months_since_last_push.powf(1.5),
                   -4.0);

        (table, positive_score, negative_score)
    }
}

pub struct RepoInfoWithScore {
    repo_info: RepoInfo,
    repo_score_info: RepoScoreInfo,
    score_positive: f64,
    score_negative: f64,
    score_table: Vec<(String, String, String)>,
}

impl RepoInfoWithScore {
    pub fn from_id_with_token(id: &str, token: &str) -> Result<Self> {
        let repo_info = RepoInfo::from_id_with_token(id, token)?;
        let repo_score_info = RepoScoreInfo::from_repo_info(&repo_info)?;
        let (score_table, score_positive, score_negative) = repo_score_info.mk_score();

        Ok(Self {
            repo_info,
            repo_score_info,
            score_positive,
            score_negative,
            score_table,
        })
    }

    pub fn get_info(&self) -> &RepoInfo {
        &self.repo_info
    }

    pub fn get_score_info(&self) -> &RepoScoreInfo {
        &self.repo_score_info
    }

    pub fn get_score_tuple(&self) -> (f64, f64) {
        (self.score_positive, self.score_negative)
    }

    pub fn get_score_table(&self) -> &[(String, String, String)] {
        &self.score_table
    }
}
<commit_msg>esr_github: Raise contributions from some important factors<commit_after>/*
    This file is a part of cargo-esr.

    Copyright (C) 2017 Mohammad AlSaleh <CE.Mohammad.AlSaleh at gmail.com>
    https://github.com/rust-alt/cargo-esr

    This Source Code Form is subject to the terms of the Mozilla Public
    License, v. 2.0. If a copy of the MPL was not distributed with this
    file, You can obtain one at <http://mozilla.org/MPL/2.0/>.
*/

use pipeliner::Pipeline;

use esr_from::{EsrFrom, DefEsrFrom};
use esr_util;
use esr_errors::Result;

#[derive(Deserialize, Debug)]
struct PullRequestInfo {
    merged_at: Option<String>,
    number: usize,
}

#[derive(Deserialize, Debug)]
struct IssueInfo {
    closed_at: Option<String>,
    number: usize,
}

#[derive(Deserialize, Debug)]
struct ContributorInfo {
    contributions: usize,
}

// =================

type RepoPullRequests = Vec<PullRequestInfo>;

impl EsrFrom for RepoPullRequests {
    fn url_from_id(id: &str) -> String {
        let url = String::from("https://api.github.com/repos/:id/pulls?state=all&per_page=100");
        url.replace(":id", id)
    }
}

type RepoClosedIssues = Vec<IssueInfo>;

impl EsrFrom for RepoClosedIssues {
    fn url_from_id(id: &str) -> String {
        let url = String::from("https://api.github.com/repos/:id/issues?\
                               state=closed&per_page=1");
        url.replace(":id", id)
    }
}

type RepoContributors = Vec<ContributorInfo>;

impl EsrFrom for RepoContributors {
    fn url_from_id(id: &str) -> String {
        let url = String::from("https://api.github.com/repos/:id/contributors?per_page=100");
        url.replace(":id", id)
    }
}

#[derive(Deserialize, Debug)]
struct RepoGeneralInfo {
    subscribers_count: usize,
    created_at: String,
    // Same as created_at if the repo is empty
    pushed_at: String,
}

impl EsrFrom for RepoGeneralInfo {
    fn url_from_id(id: &str) -> String {
        let url = String::from("https://api.github.com/repos/:id");
        url.replace(":id", id)
    }
}

// =================

pub struct RepoInfo {
    general_info: RepoGeneralInfo,
    last_100_closed_issues: RepoClosedIssues,
    last_100_pull_requests: RepoPullRequests,
    top_100_contributors: RepoContributors,
}

impl RepoInfo {
    pub fn from_id(_: &str) -> Result<Self> {
        Err("Unimplemented: use  from_id_with_token()")?
    }

    pub fn from_id_with_token(id: &str, token: &str) -> Result<Self> {
        let urls = vec![
            RepoGeneralInfo::url_from_id_and_token(id, token),
            RepoClosedIssues::url_from_id_and_token(id, token),
            RepoPullRequests::url_from_id_and_token(id, token),
            RepoContributors::url_from_id_and_token(id, token),
        ];

        // `.with_threads()` does not guarantee order. So, we use `.enumerate()` as an indexer
        let bytes = urls
            .into_iter()
            .enumerate()
            .with_threads(4)
            .map(|(idx, url)| DefEsrFrom::bytes_from_url(&url).map(|bytes| (idx, bytes)))
            .collect::<Result<Vec<_>>>()?;

        let &(_, ref bytes_general) = bytes.iter().find(|&&(idx,_)| idx == 0).ok_or("impossible")?;
        let &(_, ref bytes_closed_issues) = bytes.iter().find(|&&(idx,_)| idx == 1).ok_or("impossible")?;
        let &(_, ref bytes_pulls) = bytes.iter().find(|&&(idx,_)| idx == 2).ok_or("impossible")?;
        let &(_, ref bytes_contributors) = bytes.iter().find(|&&(idx,_)| idx == 3).ok_or("impossible")?;

        Ok(Self {
            general_info: RepoGeneralInfo::from_bytes(bytes_general)?,
            last_100_closed_issues: RepoClosedIssues::from_bytes(bytes_closed_issues)?,
            last_100_pull_requests: RepoPullRequests::from_bytes(bytes_pulls)?,
            top_100_contributors: RepoContributors::from_bytes(bytes_contributors)?,
        })
    }
}

pub struct RepoScoreInfo {
    subscribers: f64,
    contributors_up_to_100: usize,
    commits_from_upto_100_contributors: f64,
    secondary_contribution_pct: usize,
    tertiary_contribution_pct: usize,
    merged_pull_requests_in_last_100: usize,
    months_since_last_pr_merged: f64,
    months_since_last_issue_closed: f64,
    push_span_in_months: f64,
    months_since_last_push: f64,
}

impl RepoScoreInfo {
    fn from_repo_info(repo_info: &RepoInfo) -> Result<Self> {
        let general_info = &repo_info.general_info;

        // Days active, months since last push
        let push_span_in_months =esr_util::span_in_months(&general_info.created_at,
                                                          &general_info.pushed_at)?;
        let months_since_last_push = esr_util::age_in_months(&general_info.pushed_at)?;

        // Get subscribers count
        let subscribers = general_info.subscribers_count as f64;

        // Get contributor count, commit count using contributors info.
        if repo_info.top_100_contributors.is_empty() {
            Err("Empty contributors list")?;
        }

        let contributors_up_to_100 = repo_info.top_100_contributors.len();

        let commits_from_upto_100_contributors = repo_info.top_100_contributors
            .iter()
            .map(|contributor| contributor.contributions)
            .sum::<usize>() as f64;

        // Secondary and Tertiary contribution pct
        let commits = commits_from_upto_100_contributors;
        let top_committer_contrib = repo_info.top_100_contributors[0].contributions as f64 / commits;
        let second_committer_contrib = repo_info.top_100_contributors.get(1)
            .map(|c| c.contributions as f64 / commits)
            .unwrap_or(0_f64);

        let secondary_contribution_pct = ((1.0 - top_committer_contrib) * 100.0).ceil() as usize;
        let tertiary_contribution_pct = ((1.0 - top_committer_contrib - second_committer_contrib) * 100.0).ceil() as usize;

        // merged pull requests in last 100, months since last merged
        let merged_pull_requests_in_last_100 = repo_info.last_100_pull_requests
            .iter()
            .filter(|pr| pr.merged_at.is_some())
            .count();

        let last_pr_merged_opt = repo_info.last_100_pull_requests
            .iter()
            .filter(|pr| pr.merged_at.is_some())
            .nth(0);

        let months_since_last_pr_merged = match last_pr_merged_opt {
            Some(pr) => esr_util::age_in_months(pr.merged_at.as_ref().ok_or("Impossible")?)?,
            None => esr_util::age_in_months(&general_info.created_at)?,
        };

        // months since last closed
        let last_issue_closed_opt = repo_info.last_100_closed_issues.get(0);

        let months_since_last_issue_closed = match last_issue_closed_opt {
            Some(issue) => esr_util::age_in_months(issue.closed_at.as_ref().ok_or("Impossible")?)?,
            None => esr_util::age_in_months(&general_info.created_at)?,
        };

        // Done
        Ok(Self {
            subscribers,
            contributors_up_to_100,
            commits_from_upto_100_contributors,
            secondary_contribution_pct,
            tertiary_contribution_pct,
            push_span_in_months,
            merged_pull_requests_in_last_100,
            months_since_last_pr_merged,
            months_since_last_issue_closed,
            months_since_last_push,
        })
    }

    fn mk_score(&self) -> (Vec<(String, String, String)>, f64, f64) {
        let mut positive_score = 0.0;
        let mut negative_score = 0.0;
        let mut table = Vec::with_capacity(9);

        // +ve
        score_add!(table, positive_score, self.subscribers.powf(0.5), 8.0);
        score_add!(table, positive_score, self.contributors_up_to_100, 3.0);
        score_add!(table,
                   positive_score,
                   self.commits_from_upto_100_contributors.powf(0.5),
                   2.0);

        // We only take secondary/tertiary contribution into account if the repo has >= 50 commits
        if self.commits_from_upto_100_contributors >= 50.0 {
            score_add!(table, positive_score, self.secondary_contribution_pct, 2.5);
            score_add!(table, positive_score, self.tertiary_contribution_pct, 5.0);
        }

        score_add!(table,
                   positive_score,
                   self.push_span_in_months.powf(0.5),
                   5.0);
        score_add!(table,
                   positive_score,
                   self.merged_pull_requests_in_last_100,
                   2.5);

        // -ve
        score_add!(table,
                   negative_score,
                   self.months_since_last_pr_merged.powf(1.5),
                   -1.0);
        score_add!(table,
                   negative_score,
                   self.months_since_last_issue_closed.powf(1.5),
                   -1.0);
        score_add!(table,
                   negative_score,
                   self.months_since_last_push.powf(1.5),
                   -4.0);

        (table, positive_score, negative_score)
    }
}

pub struct RepoInfoWithScore {
    repo_info: RepoInfo,
    repo_score_info: RepoScoreInfo,
    score_positive: f64,
    score_negative: f64,
    score_table: Vec<(String, String, String)>,
}

impl RepoInfoWithScore {
    pub fn from_id_with_token(id: &str, token: &str) -> Result<Self> {
        let repo_info = RepoInfo::from_id_with_token(id, token)?;
        let repo_score_info = RepoScoreInfo::from_repo_info(&repo_info)?;
        let (score_table, score_positive, score_negative) = repo_score_info.mk_score();

        Ok(Self {
            repo_info,
            repo_score_info,
            score_positive,
            score_negative,
            score_table,
        })
    }

    pub fn get_info(&self) -> &RepoInfo {
        &self.repo_info
    }

    pub fn get_score_info(&self) -> &RepoScoreInfo {
        &self.repo_score_info
    }

    pub fn get_score_tuple(&self) -> (f64, f64) {
        (self.score_positive, self.score_negative)
    }

    pub fn get_score_table(&self) -> &[(String, String, String)] {
        &self.score_table
    }
}
<|endoftext|>


<commit_before>use std::convert::TryFrom;
use std::marker::PhantomData;
use stack::Stack;
use num::Float;

// Add  (+)     pop 2, push 1
// Sub  (-)     pop 2, push 1
// Mul  (*)     pop 2, push 1
// Div  (/)     pop 2, push 1
// Neg  (neg)   pop 1, push 1
// Sqrt (sqrt)  pop 1, push 1
// Pow  (pow)   pop 1, push 1
// Log2 (log2)  pop 1, push 1
// Exp  (exp)   pop 1, push 1
// Swap (swap)  pop 2, push 2
// Zero (zero)  pop 0, push 1
// One  (zero)  pop 0, push 1

pub trait Operate<T> {
    type Err;
    fn operate(stack: &mut Stack<T>) -> Result<(), Self::Err>;
}

// pub trait FromOperate<T> {
//     type Err;
//     fn operate(stack: &mut Stack<T>) -> Result<(), Self::Err>;
// }

#[derive(Debug)]
pub enum Arithm<T, O: Operate<T>> {
    Operand(T),
    Operator(O),
}

pub struct Expression<T, O: Operate<T>>(Vec<Arithm<T, O>>);

impl<'a, T, O: Operate<T>> TryFrom<&'a str> for Expression<T, O> {
    type Err = (); // TODO change this
    fn try_from(expr: &'a str) -> Result<Self, Self::Err> {
        // let mut arithm = Vec::new();

        for token in expr.split_whitespace() {
            // arithm.push(Arithm::Operand(0))
        }

        Ok(Expression(Vec::new())) // TODO change this
    }
}

// TODO move this elsewhere !!!
#[derive(Debug)]
pub enum FloatOperator<T: Float> {
    Add(PhantomData<T>),
    Sub(PhantomData<T>),
    Mul(PhantomData<T>),
    Div(PhantomData<T>),
}

impl<T: Float> Operate<T> for FloatOperator<T> {
    type Err = ();
    fn operate(stack: &mut Stack<T>) -> Result<(), Self::Err> {
        Ok(())
    }
}
<commit_msg>expression: Add basic expression convertion<commit_after>use std::convert::{TryFrom, TryInto};
use std::str::FromStr;
use std::marker::PhantomData;
use stack::Stack;
use num::Float;

// Add  (+)     pop 2, push 1
// Sub  (-)     pop 2, push 1
// Mul  (*)     pop 2, push 1
// Div  (/)     pop 2, push 1
// Neg  (neg)   pop 1, push 1
// Sqrt (sqrt)  pop 1, push 1
// Pow  (pow)   pop 1, push 1
// Log2 (log2)  pop 1, push 1
// Exp  (exp)   pop 1, push 1
// Swap (swap)  pop 2, push 2
// Zero (zero)  pop 0, push 1
// One  (zero)  pop 0, push 1

pub trait Operate<T> {
    type Err;
    fn operate(self, stack: &mut Stack<T>) -> Result<(), Self::Err>;
}

// pub trait FromOperate<T> {
//     type Err;
//     fn operate(stack: &mut Stack<T>) -> Result<(), Self::Err>;
// }

#[derive(Debug)]
pub enum Arithm<T, O: Operate<T>> {
    Operand(T),
    Operator(O),
}

#[derive(Debug)]
pub struct Expression<T, O: Operate<T>>(Vec<Arithm<T, O>>);

impl<'a, T, O> TryFrom<&'a str> for Expression<T, O>
               where T: FromStr,
                     O: Operate<T> + TryFrom<&'a str> {
    type Err = (); // TODO change this
    fn try_from(expr: &'a str) -> Result<Self, Self::Err> {
        let mut expression = Vec::new();
        for token in expr.split_whitespace() {
            // TODO make this more rusty !
            let arithm: Arithm<T, O> = match token.parse() {
                Ok(operand) => Arithm::Operand(operand),
                Err(operand_err) => match token.try_into() {
                    Ok(operator) => Arithm::Operator(operator),
                    Err(operator_err) => return Err(()), // TODO change this
                },
            };
            expression.push(arithm);
        }
        Ok(Expression(expression)) // TODO change this
    }
}

// TODO move this elsewhere !!!
#[derive(Debug)]
pub enum FloatOperator<T: Float> {
    Add(PhantomData<T>),
    Sub(PhantomData<T>),
    Mul(PhantomData<T>),
    Div(PhantomData<T>),
}

impl<T: Float> Operate<T> for FloatOperator<T> {
    type Err = ();
    fn operate(self, stack: &mut Stack<T>) -> Result<(), Self::Err> {
        let (a, b) = (stack.pop().unwrap(), stack.pop().unwrap()); // TODO don't unwrap !!!
        match self {
            FloatOperator::Add(_) => Ok(stack.push(a + b)),
            FloatOperator::Sub(_) => Ok(stack.push(b - a)),
            FloatOperator::Mul(_) => Ok(stack.push(a * b)),
            FloatOperator::Div(_) => Ok(stack.push(b / a)),
        }
    }
}

impl<'a, T: Float> TryFrom<&'a str> for FloatOperator<T> {
    type Err = (); // TODO change this
    fn try_from(expr: &'a str) -> Result<Self, Self::Err> {
        match expr {
            "+" => Ok(FloatOperator::Add(PhantomData::default())),
            "-" => Ok(FloatOperator::Sub(PhantomData::default())),
            "*" => Ok(FloatOperator::Mul(PhantomData::default())),
            "/" => Ok(FloatOperator::Div(PhantomData::default())),
            _ => Err(())
        }
    }
}

#[cfg(test)]
mod tests {
    use std::convert::TryInto;
    use expression::{Expression, FloatOperator};

    #[test]
    fn simple_expression() {
        let expr: Expression<f32, FloatOperator<_>> = "3 4 +".try_into().unwrap();
    }
}
<|endoftext|>


<commit_before>/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2015 Andres Vahter (andres.vahter@gmail.com)
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

use libc::{c_void, c_int, c_char, c_double, c_ulong, c_uint};

#[repr(C)]
#[allow(dead_code)]
#[allow(non_camel_case_types)]
#[derive(Debug, Clone)]
pub enum op_stat_t {
    OP_STAT_UNKNOWN = 0,
    OP_STAT_OPERATIONAL,    // Operational           [+]
    OP_STAT_NONOP,          // Nonoperational        [-]
    OP_STAT_PARTIAL,        // Partially operational [P]
    OP_STAT_STDBY,          // Backup/Standby        [B]
    OP_STAT_SPARE,          // Spare                 [S]
    OP_STAT_EXTENDED,       // Extended Mission      [X]
}

#[repr(C)]
#[allow(dead_code)]
#[allow(non_camel_case_types)]
#[derive(Debug, Clone)]
pub enum orbit_type_t {
    ORBIT_TYPE_UNKNOWN = 0,
    ORBIT_TYPE_LEO,         // Low Earth orbit, up to 1200 km
    ORBIT_TYPE_ICO,         // Intermediate Circular Orbit, up to 1400 km
    ORBIT_TYPE_GEO,         // Geostationary
    ORBIT_TYPE_GSO,         // Geosynchronuous
    ORBIT_TYPE_MOLNIYA,
    ORBIT_TYPE_TUNDRA,
    ORBIT_TYPE_POLAR,
    ORBIT_TYPE_SUNSYNC,
    ORBIT_TYPE_DECAYED
}

#[repr(C)]
#[allow(dead_code, raw_pointer_derive)]
#[derive(Debug, Clone)]
pub struct qth_t {
    pub name:       *const c_char,  // Name, eg. callsign
    pub loc:        *const c_char,  // Location, eg City, Country
    pub desc:       *const c_char,  // Short description
    pub lat:        c_double,       // Latitude in dec. deg. North
    pub lon:        c_double,       // Longitude in dec. deg. East
    pub alt:        c_int,          // Altitude above sea level in meters
    pub qra:        *const c_char,  // QRA locator
    pub wx:         *const c_char,  // Weather station code (4 chars)
}

#[repr(C)]
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct tle_t {
    pub epoch:      c_double,       // Epoch Time in NORAD TLE format YYDDD.FFFFFFFF
    pub epoch_year: c_uint,         // Epoch: year
    pub epoch_day:  c_uint,         // Epoch: day of year
    pub epoch_fod:  c_double,       // Epoch: Fraction of day
    pub xndt2o:     c_double,       // 1. time derivative of mean motion
    pub xndd6o:     c_double,       // 2. time derivative of mean motion
    pub bstar:      c_double,       // Bstar drag coefficient
    pub xincl:      c_double,       // Inclination
    pub xnodeo:     c_double,       // R.A.A.N.
    pub eo:         c_double,       // Eccentricity
    pub omegao:     c_double,       // argument of perigee
    pub xmo:        c_double,       // mean anomaly
    pub xno:        c_double,       // mean motion

    pub catnr:      c_int,          // Catalogue Number
    pub elset:      c_int,          // Element Set number
    pub revnum:     c_int,          // Revolution Number at epoch

    pub sat_name:   [c_char; 25],   // Satellite name string
    pub idesg:      [c_char; 9],    // International Designator
    pub status:     op_stat_t,      // Operational status

    // values needed for squint calculations
    pub xincl1:     c_double,
    pub xnodeo1:    c_double,
    pub omegao1:    c_double,
}

#[repr(C)]
#[derive(Default, Debug, Clone)]
#[allow(dead_code)]
pub struct vector_t {
    pub x:          c_double,   // X component
    pub y:          c_double,   // Y component
    pub z:          c_double,   // Z component
    pub w:          c_double,   // Magnitude
}

#[repr(C)]
#[allow(dead_code)]
#[derive(Default, Debug, Clone)]
/// Common arguments between deep-space functions
pub struct deep_arg_t {
    // Used by dpinit part of Deep()
    pub eosq:       c_double,
    pub sinio:      c_double,
    pub cosio:      c_double,
    pub betao:      c_double,
    pub aodp:       c_double,
    pub theta2:     c_double,
    pub sing:       c_double,
    pub cosg:       c_double,
    pub betao2:     c_double,
    pub xmdot:      c_double,
    pub omgdot:     c_double,
    pub xnodot:     c_double,
    pub xnodp:      c_double,

    // Used by dpsec and dpper parts of Deep()
    pub xll:        c_double,
    pub omgadf:     c_double,
    pub xnode:      c_double,
    pub em:         c_double,
    pub xinc:       c_double,
    pub xn:         c_double,
    pub t:          c_double,

    // Used by thetg and Deep()
    pub ds50:       c_double,
}

#[repr(C)]
#[allow(dead_code)]
#[derive(Default, Debug, Clone)]
/// static data for SGP4 and SDP4
pub struct sgpsdp_static_t {
    pub aodp:       c_double,
    pub aycof:      c_double,
    pub c1:         c_double,
    pub c4:         c_double,
    pub c5:         c_double,
    pub cosio:      c_double,
    pub d2:         c_double,
    pub d3:         c_double,
    pub d4:         c_double,
    pub delmo:      c_double,
    pub omgcof:     c_double,

    pub eta:        c_double,
    pub omgdot:     c_double,
    pub sinio:      c_double,
    pub xnodp:      c_double,
    pub sinmo:      c_double,
    pub t2cof:      c_double,
    pub t3cof:      c_double,
    pub t4cof:      c_double,
    pub t5cof:      c_double,

    pub x1mth2:     c_double,
    pub x3thm1:     c_double,
    pub x7thm1:     c_double,
    pub xmcof:      c_double,
    pub xmdot:      c_double,
    pub xnodcf:     c_double,
    pub xnodot:     c_double,
    pub xlcof:      c_double,
}

#[repr(C)]
#[allow(dead_code)]
#[derive(Default, Debug, Clone)]
/// static data for DEEP
pub struct deep_static_t {
    pub thgr:       c_double,
    pub xnq:        c_double,
    pub xqncl:      c_double,
    pub omegaq:     c_double,
    pub zmol:       c_double,
    pub zmos:       c_double,
    pub savtsn:     c_double,
    pub ee2:        c_double,
    pub e3:         c_double,
    pub xi2:        c_double,

    pub xl2:        c_double,
    pub xl3:        c_double,
    pub xl4:        c_double,
    pub xgh2:       c_double,
    pub xgh3:       c_double,
    pub xgh4:       c_double,
    pub xh2:        c_double,
    pub xh3:        c_double,
    pub sse:        c_double,
    pub ssi:        c_double,
    pub ssg:        c_double,
    pub xi3:        c_double,

    pub se2:        c_double,
    pub si2:        c_double,
    pub sl2:        c_double,
    pub sgh2:       c_double,
    pub sh2:        c_double,
    pub se3:        c_double,
    pub si3:        c_double,
    pub sl3:        c_double,
    pub sgh3:       c_double,
    pub sh3:        c_double,
    pub sl4:        c_double,
    pub sgh4:       c_double,

    pub ssl:        c_double,
    pub ssh:        c_double,
    pub d3210:      c_double,
    pub d3222:      c_double,
    pub d4410:      c_double,
    pub d4422:      c_double,
    pub d5220:      c_double,
    pub d5232:      c_double,
    pub d5421:      c_double,

    pub d5433:      c_double,
    pub del1:       c_double,
    pub del2:       c_double,
    pub del3:       c_double,
    pub fasx2:      c_double,
    pub fasx4:      c_double,
    pub fasx6:      c_double,
    pub xlamo:      c_double,
    pub xfact:      c_double,

    pub xni:        c_double,
    pub atime:      c_double,
    pub stepp:      c_double,
    pub stepn:      c_double,
    pub step2:      c_double,
    pub preep:      c_double,
    pub pl:         c_double,
    pub sghs:       c_double,
    pub xli:        c_double,

    pub d2201:      c_double,
    pub d2211:      c_double,
    pub sghl:       c_double,
    pub sh1:        c_double,
    pub pinc:       c_double,
    pub pe:         c_double,
    pub shs:        c_double,
    pub zsingl:     c_double,
    pub zcosgl:     c_double,

    pub zsinhl:     c_double,
    pub zcoshl:     c_double,
    pub zsinil:     c_double,
    pub zcosil:     c_double,

}

#[repr(C)]
#[allow(raw_pointer_derive)]
#[derive(Debug, Clone)]
pub struct sat_t {
    pub name:       *const c_char,
    pub nickname:   *const c_char,
    pub website:    *const c_char,
    pub tle:        tle_t,          // Keplerian elements
    pub flags:      c_int,          // Flags for algo ctrl
    pub sgps:       sgpsdp_static_t,
    pub dps:        deep_static_t,
    pub deep_arg:   deep_arg_t,
    pub pos:        vector_t,       // Raw position and range
    pub vel:        vector_t,       // Raw velocity

    pub jul_epoch:  c_double,
    pub jul_utc:    c_double,
    pub tsince:     c_double,
    pub aos:        c_double,       // Next AOS
    pub los:        c_double,       // Next LO

    pub az:         c_double,       // Azimuth [deg]
    pub el:         c_double,       // Elevation [deg]
    pub range:      c_double,       // Range [km]
    pub range_rate: c_double,       // Range Rate [km/sec]
    pub ra:         c_double,       // Right Ascension [deg]
    pub dec:        c_double,       // Declination [deg]
    pub ssplat:     c_double,       // SSP latitude [deg]
    pub ssplon:     c_double,       // SSP longitude [deg]
    pub alt:        c_double,       // altitude [km]
    pub velo:       c_double,       // velocity [km/s]
    pub ma:         c_double,       // mean anomaly
    pub footprint:  c_double,       // footprint
    pub phase:      c_double,       // orbit phase
    pub meanmo:     c_double,       // mean motion kept in rev/day
    pub orbit:      c_ulong,        // orbit number
    pub otype:      orbit_type_t    // orbit type
}

#[link(name = "gpredict")]
extern {
    pub fn get_current_daynum() -> c_double;
    pub fn predict_calc(sat: *mut sat_t, qth: *mut qth_t, t: c_double) -> c_void;
    pub fn find_aos(sat: *mut sat_t, qth: *mut qth_t, start: c_double, maxdt: c_double) -> c_double;
    pub fn find_los(sat: *mut sat_t, qth: *mut qth_t, start: c_double, maxdt: c_double) -> c_double;

    pub fn Get_Next_Tle_Set(line: *const c_char, tle: *mut tle_t) -> c_int;
    pub fn select_ephemeris(sat: *mut sat_t) -> c_void;
    pub fn gtk_sat_data_init_sat(sat: *mut sat_t, sat: *mut qth_t) -> c_void;
}
<commit_msg>remove allow raw_pointer_derive (not needed with rust 1.6)<commit_after>/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2015 Andres Vahter (andres.vahter@gmail.com)
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

use libc::{c_void, c_int, c_char, c_double, c_ulong, c_uint};

#[repr(C)]
#[allow(dead_code)]
#[allow(non_camel_case_types)]
#[derive(Debug, Clone)]
pub enum op_stat_t {
    OP_STAT_UNKNOWN = 0,
    OP_STAT_OPERATIONAL,    // Operational           [+]
    OP_STAT_NONOP,          // Nonoperational        [-]
    OP_STAT_PARTIAL,        // Partially operational [P]
    OP_STAT_STDBY,          // Backup/Standby        [B]
    OP_STAT_SPARE,          // Spare                 [S]
    OP_STAT_EXTENDED,       // Extended Mission      [X]
}

#[repr(C)]
#[allow(dead_code)]
#[allow(non_camel_case_types)]
#[derive(Debug, Clone)]
pub enum orbit_type_t {
    ORBIT_TYPE_UNKNOWN = 0,
    ORBIT_TYPE_LEO,         // Low Earth orbit, up to 1200 km
    ORBIT_TYPE_ICO,         // Intermediate Circular Orbit, up to 1400 km
    ORBIT_TYPE_GEO,         // Geostationary
    ORBIT_TYPE_GSO,         // Geosynchronuous
    ORBIT_TYPE_MOLNIYA,
    ORBIT_TYPE_TUNDRA,
    ORBIT_TYPE_POLAR,
    ORBIT_TYPE_SUNSYNC,
    ORBIT_TYPE_DECAYED
}

#[repr(C)]
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct qth_t {
    pub name:       *const c_char,  // Name, eg. callsign
    pub loc:        *const c_char,  // Location, eg City, Country
    pub desc:       *const c_char,  // Short description
    pub lat:        c_double,       // Latitude in dec. deg. North
    pub lon:        c_double,       // Longitude in dec. deg. East
    pub alt:        c_int,          // Altitude above sea level in meters
    pub qra:        *const c_char,  // QRA locator
    pub wx:         *const c_char,  // Weather station code (4 chars)
}

#[repr(C)]
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct tle_t {
    pub epoch:      c_double,       // Epoch Time in NORAD TLE format YYDDD.FFFFFFFF
    pub epoch_year: c_uint,         // Epoch: year
    pub epoch_day:  c_uint,         // Epoch: day of year
    pub epoch_fod:  c_double,       // Epoch: Fraction of day
    pub xndt2o:     c_double,       // 1. time derivative of mean motion
    pub xndd6o:     c_double,       // 2. time derivative of mean motion
    pub bstar:      c_double,       // Bstar drag coefficient
    pub xincl:      c_double,       // Inclination
    pub xnodeo:     c_double,       // R.A.A.N.
    pub eo:         c_double,       // Eccentricity
    pub omegao:     c_double,       // argument of perigee
    pub xmo:        c_double,       // mean anomaly
    pub xno:        c_double,       // mean motion

    pub catnr:      c_int,          // Catalogue Number
    pub elset:      c_int,          // Element Set number
    pub revnum:     c_int,          // Revolution Number at epoch

    pub sat_name:   [c_char; 25],   // Satellite name string
    pub idesg:      [c_char; 9],    // International Designator
    pub status:     op_stat_t,      // Operational status

    // values needed for squint calculations
    pub xincl1:     c_double,
    pub xnodeo1:    c_double,
    pub omegao1:    c_double,
}

#[repr(C)]
#[derive(Default, Debug, Clone)]
#[allow(dead_code)]
pub struct vector_t {
    pub x:          c_double,   // X component
    pub y:          c_double,   // Y component
    pub z:          c_double,   // Z component
    pub w:          c_double,   // Magnitude
}

#[repr(C)]
#[allow(dead_code)]
#[derive(Default, Debug, Clone)]
/// Common arguments between deep-space functions
pub struct deep_arg_t {
    // Used by dpinit part of Deep()
    pub eosq:       c_double,
    pub sinio:      c_double,
    pub cosio:      c_double,
    pub betao:      c_double,
    pub aodp:       c_double,
    pub theta2:     c_double,
    pub sing:       c_double,
    pub cosg:       c_double,
    pub betao2:     c_double,
    pub xmdot:      c_double,
    pub omgdot:     c_double,
    pub xnodot:     c_double,
    pub xnodp:      c_double,

    // Used by dpsec and dpper parts of Deep()
    pub xll:        c_double,
    pub omgadf:     c_double,
    pub xnode:      c_double,
    pub em:         c_double,
    pub xinc:       c_double,
    pub xn:         c_double,
    pub t:          c_double,

    // Used by thetg and Deep()
    pub ds50:       c_double,
}

#[repr(C)]
#[allow(dead_code)]
#[derive(Default, Debug, Clone)]
/// static data for SGP4 and SDP4
pub struct sgpsdp_static_t {
    pub aodp:       c_double,
    pub aycof:      c_double,
    pub c1:         c_double,
    pub c4:         c_double,
    pub c5:         c_double,
    pub cosio:      c_double,
    pub d2:         c_double,
    pub d3:         c_double,
    pub d4:         c_double,
    pub delmo:      c_double,
    pub omgcof:     c_double,

    pub eta:        c_double,
    pub omgdot:     c_double,
    pub sinio:      c_double,
    pub xnodp:      c_double,
    pub sinmo:      c_double,
    pub t2cof:      c_double,
    pub t3cof:      c_double,
    pub t4cof:      c_double,
    pub t5cof:      c_double,

    pub x1mth2:     c_double,
    pub x3thm1:     c_double,
    pub x7thm1:     c_double,
    pub xmcof:      c_double,
    pub xmdot:      c_double,
    pub xnodcf:     c_double,
    pub xnodot:     c_double,
    pub xlcof:      c_double,
}

#[repr(C)]
#[allow(dead_code)]
#[derive(Default, Debug, Clone)]
/// static data for DEEP
pub struct deep_static_t {
    pub thgr:       c_double,
    pub xnq:        c_double,
    pub xqncl:      c_double,
    pub omegaq:     c_double,
    pub zmol:       c_double,
    pub zmos:       c_double,
    pub savtsn:     c_double,
    pub ee2:        c_double,
    pub e3:         c_double,
    pub xi2:        c_double,

    pub xl2:        c_double,
    pub xl3:        c_double,
    pub xl4:        c_double,
    pub xgh2:       c_double,
    pub xgh3:       c_double,
    pub xgh4:       c_double,
    pub xh2:        c_double,
    pub xh3:        c_double,
    pub sse:        c_double,
    pub ssi:        c_double,
    pub ssg:        c_double,
    pub xi3:        c_double,

    pub se2:        c_double,
    pub si2:        c_double,
    pub sl2:        c_double,
    pub sgh2:       c_double,
    pub sh2:        c_double,
    pub se3:        c_double,
    pub si3:        c_double,
    pub sl3:        c_double,
    pub sgh3:       c_double,
    pub sh3:        c_double,
    pub sl4:        c_double,
    pub sgh4:       c_double,

    pub ssl:        c_double,
    pub ssh:        c_double,
    pub d3210:      c_double,
    pub d3222:      c_double,
    pub d4410:      c_double,
    pub d4422:      c_double,
    pub d5220:      c_double,
    pub d5232:      c_double,
    pub d5421:      c_double,

    pub d5433:      c_double,
    pub del1:       c_double,
    pub del2:       c_double,
    pub del3:       c_double,
    pub fasx2:      c_double,
    pub fasx4:      c_double,
    pub fasx6:      c_double,
    pub xlamo:      c_double,
    pub xfact:      c_double,

    pub xni:        c_double,
    pub atime:      c_double,
    pub stepp:      c_double,
    pub stepn:      c_double,
    pub step2:      c_double,
    pub preep:      c_double,
    pub pl:         c_double,
    pub sghs:       c_double,
    pub xli:        c_double,

    pub d2201:      c_double,
    pub d2211:      c_double,
    pub sghl:       c_double,
    pub sh1:        c_double,
    pub pinc:       c_double,
    pub pe:         c_double,
    pub shs:        c_double,
    pub zsingl:     c_double,
    pub zcosgl:     c_double,

    pub zsinhl:     c_double,
    pub zcoshl:     c_double,
    pub zsinil:     c_double,
    pub zcosil:     c_double,

}

#[repr(C)]
#[derive(Debug, Clone)]
pub struct sat_t {
    pub name:       *const c_char,
    pub nickname:   *const c_char,
    pub website:    *const c_char,
    pub tle:        tle_t,          // Keplerian elements
    pub flags:      c_int,          // Flags for algo ctrl
    pub sgps:       sgpsdp_static_t,
    pub dps:        deep_static_t,
    pub deep_arg:   deep_arg_t,
    pub pos:        vector_t,       // Raw position and range
    pub vel:        vector_t,       // Raw velocity

    pub jul_epoch:  c_double,
    pub jul_utc:    c_double,
    pub tsince:     c_double,
    pub aos:        c_double,       // Next AOS
    pub los:        c_double,       // Next LO

    pub az:         c_double,       // Azimuth [deg]
    pub el:         c_double,       // Elevation [deg]
    pub range:      c_double,       // Range [km]
    pub range_rate: c_double,       // Range Rate [km/sec]
    pub ra:         c_double,       // Right Ascension [deg]
    pub dec:        c_double,       // Declination [deg]
    pub ssplat:     c_double,       // SSP latitude [deg]
    pub ssplon:     c_double,       // SSP longitude [deg]
    pub alt:        c_double,       // altitude [km]
    pub velo:       c_double,       // velocity [km/s]
    pub ma:         c_double,       // mean anomaly
    pub footprint:  c_double,       // footprint
    pub phase:      c_double,       // orbit phase
    pub meanmo:     c_double,       // mean motion kept in rev/day
    pub orbit:      c_ulong,        // orbit number
    pub otype:      orbit_type_t    // orbit type
}

#[link(name = "gpredict")]
extern {
    pub fn get_current_daynum() -> c_double;
    pub fn predict_calc(sat: *mut sat_t, qth: *mut qth_t, t: c_double) -> c_void;
    pub fn find_aos(sat: *mut sat_t, qth: *mut qth_t, start: c_double, maxdt: c_double) -> c_double;
    pub fn find_los(sat: *mut sat_t, qth: *mut qth_t, start: c_double, maxdt: c_double) -> c_double;

    pub fn Get_Next_Tle_Set(line: *const c_char, tle: *mut tle_t) -> c_int;
    pub fn select_ephemeris(sat: *mut sat_t) -> c_void;
    pub fn gtk_sat_data_init_sat(sat: *mut sat_t, sat: *mut qth_t) -> c_void;
}
<|endoftext|>