<commit_before>use crate::frame::Ax25Frame;
use crate::kiss;
use crate::linux;
use std::str::FromStr;
use std::sync::{Arc, Mutex};
use thiserror::Error;
use std::sync::mpsc::{Sender, Receiver, channel};
use std::collections::VecDeque;
use std::thread;

/// Errors that can occur when interacting with a `Tnc`.
#[derive(Debug, Error)]
pub enum TncError {
    #[error("Unable to connect to TNC: {}", source)]
    OpenTnc { source: std::io::Error },
    #[error("Interface with specified callsign '{}' does not exist", callsign)]
    InterfaceNotFound { callsign: String },
    #[error("Unable to send frame: {}", source)]
    SendFrame { source: std::io::Error },
    #[error("Unable to receive frame: {}", source)]
    ReceiveFrame { source: std::io::Error },
    #[error("Unable to make configuration change: {}", source)]
    ConfigFailed { source: std::io::Error },
}

/// Errors that can occur when parsing a `TncAddress` from a string.
#[derive(Debug, Error, PartialEq)]
pub enum ParseError {
    #[error("TNC address '{}' is invalid - it should begin with 'tnc:'", string)]
    NoTncPrefix { string: String },
    #[error("Unknown TNC type {}", tnc_type)]
    UnknownType { tnc_type: String },
    #[error(
        "TNC type '{}' expects {} parameters to follow but there are {}",
        tnc_type,
        expected,
        actual
    )]
    WrongParameterCount {
        tnc_type: String,
        expected: usize,
        actual: usize,
    },
    #[error("Supplied port '{}' should be a number from 0 to 65535", input)]
    InvalidPort {
        input: String,
        source: std::num::ParseIntError,
    },
}

/// Configuration details for a TCP KISS TNC. This structure can be created directly
/// or indirectly by parsing a string into a `TncAddress`.
#[derive(PartialEq, Debug)]
pub struct TcpKissConfig {
    /// Hostname or IP address of the computer with the TNC
    pub host: String,
    /// Port number
    pub port: u16,
}

/// Configuration details for a TNC attached as a Linux network interface using
/// `kissattach`. This structure can be created directly or indirectly by parsing
/// a string into a `TncAddress`.
#[derive(PartialEq, Debug)]
pub struct LinuxIfConfig {
    /// The hardware address associated with the interface, e.g. "VK7NTK-2"
    pub callsign: String,
}

#[derive(PartialEq, Debug)]
pub(crate) enum ConnectConfig {
    TcpKiss(TcpKissConfig),
    LinuxIf(LinuxIfConfig),
}

/// A parsed TNC address that can be used to open a `Tnc`.
#[derive(PartialEq, Debug)]
pub struct TncAddress {
    pub(crate) config: ConnectConfig,
}

impl TncAddress {
    /// Programmatically create a `TncAddress` pointing to a Linux network interface.
    pub fn new_linuxif(linuxif: LinuxIfConfig) -> Self {
        TncAddress {
            config: ConnectConfig::LinuxIf(linuxif),
        }
    }

    /// Porgrammatically create a `TncAddress` pointing to a KISS TCP service.
    pub fn new_tcpkiss(tcpkiss: TcpKissConfig) -> Self {
        TncAddress {
            config: ConnectConfig::TcpKiss(tcpkiss),
        }
    }
}

impl FromStr for TncAddress {
    type Err = ParseError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        if !s.starts_with("tnc:") {
            return Err(ParseError::NoTncPrefix {
                string: s.to_string(),
            });
        }
        let components: Vec<&str> = s.split(':').collect();
        let len = components.len();
        Ok(match components[1] {
            "tcpkiss" => {
                if len != 4 {
                    return Err(ParseError::WrongParameterCount {
                        tnc_type: components[1].to_string(),
                        expected: 2usize,
                        actual: len - 2,
                    });
                }
                TncAddress {
                    config: ConnectConfig::TcpKiss(TcpKissConfig {
                        host: components[2].to_string(),
                        port: components[3].parse().map_err(|e| ParseError::InvalidPort {
                            input: components[3].to_string(),
                            source: e,
                        })?,
                    }),
                }
            }
            "linuxif" => {
                if len != 3 {
                    return Err(ParseError::WrongParameterCount {
                        tnc_type: components[1].to_string(),
                        expected: 1usize,
                        actual: len - 2,
                    });
                }
                TncAddress {
                    config: ConnectConfig::LinuxIf(LinuxIfConfig {
                        callsign: components[2].to_string(),
                    }),
                }
            }
            unknown => {
                return Err(ParseError::UnknownType {
                    tnc_type: unknown.to_string(),
                })
            }
        })
    }
}

trait TncImpl: Send + Sync {
    fn send_frame(&self, frame: &Ax25Frame) -> Result<(), TncError>;
    fn receive_frame(&self) -> Result<Ax25Frame, TncError>;
    fn clone(&self) -> Box<dyn TncImpl>;
}

/// A local or remote TNC attached to a radio, which can send and receive frames.
pub struct Tnc {
    imp: Box<dyn TncImpl>,
    senders: Arc<Mutex<Vec<Sender<Ax25Frame>>>>,
    buffer: Arc<Mutex<VecDeque<Ax25Frame>>>,
}

impl Tnc {
    /// Attempt to obtain a `Tnc` connection using the provided address.
    pub fn open(address: &TncAddress) -> Result<Self, TncError> {
        let imp: Box<dyn TncImpl> = match &address.config {
            ConnectConfig::TcpKiss(config) => Box::new(TcpKissTnc::open(&config)?),
            ConnectConfig::LinuxIf(config) => Box::new(LinuxIfTnc::open(&config)?),
        };
        Ok(Tnc::new(imp))
    }

    fn new(imp: Box<dyn TncImpl>) -> Self {
        let tnc = Tnc {
            imp,
            senders: Arc::new(Mutex::new(Vec::new())),
            buffer: Arc::new(Mutex::new(VecDeque::new())),
        };
        {
            let tnc = tnc.clone();
            // TODO how to allow this thread to exit?
            thread::spawn(move || {
                loop {
                    // TODO what to do if this fails?
                    if let Ok(x) = tnc.imp.receive_frame() {
                        tnc.buffer.lock().unwrap().push_back(x.clone());
                        tnc.senders.lock().unwrap().retain(|s| {
                            let x = (&x).clone();
                            // If there's an error, remove sender from vec
                            s.send(x).is_ok()
                        });
                    }
                }
            });
        }
        tnc
    }

    /// Transmit a frame on the radio. Transmission is not guaranteed even if a
    /// `Ok` result is returned.
    pub fn send_frame(&self, frame: &Ax25Frame) -> Result<(), TncError> {
        self.imp.send_frame(frame)
    }

    /// Block to receive a frame from the radio. If you want to do this on
    /// a separate thread from sending, clone the `Tnc`.
    pub fn receive_frame(&self) -> Result<Ax25Frame, TncError> {
        self.imp.receive_frame()
    }

    /// Create a new `Receiver<Ax25Frame>`
    /// This will receive a copy of all incoming frames.
    pub fn incoming(&mut self) -> Receiver<Ax25Frame> {
        let (sender, receiver) = channel();
        self.senders.lock().unwrap().push(sender);
        receiver
    }
}

impl Clone for Tnc {
    fn clone(&self) -> Self {
        Tnc {
            imp: self.imp.clone(),
            senders: self.senders.clone(),
            buffer: self.buffer.clone()
        }
    }
}

struct LinuxIfTnc {
    socket: Arc<linux::Ax25RawSocket>,
    ifindex: i32,
}

impl LinuxIfTnc {
    fn open(config: &LinuxIfConfig) -> Result<Self, TncError> {
        let socket = linux::Ax25RawSocket::new().map_err(|e| TncError::OpenTnc { source: e })?;
        let ifindex = match socket
            .list_ax25_interfaces()
            .map_err(|e| TncError::OpenTnc { source: e })?
            .iter()
            .find(|nd| nd.name.to_uppercase() == config.callsign.to_uppercase())
        {
            Some(nd) => nd.ifindex,
            None => {
                return Err(TncError::InterfaceNotFound {
                    callsign: config.callsign.clone(),
                })
            }
        };
        Ok(Self {
            socket: Arc::new(socket),
            ifindex,
        })
    }
}

impl TncImpl for LinuxIfTnc {
    fn send_frame(&self, frame: &Ax25Frame) -> Result<(), TncError> {
        self.socket
            .send_frame(&frame.to_bytes(), self.ifindex)
            .map_err(|e| TncError::SendFrame { source: e })
    }

    fn receive_frame(&self) -> Result<Ax25Frame, TncError> {
        loop {
            let bytes = self
                .socket
                .receive_frame(self.ifindex)
                .map_err(|e| TncError::ReceiveFrame { source: e })?;
            if let Ok(parsed) = Ax25Frame::from_bytes(&bytes) {
                return Ok(parsed);
            }
        }
    }

    fn clone(&self) -> Box<dyn TncImpl> {
        Box::new(LinuxIfTnc {
            socket: self.socket.clone(),
            ifindex: self.ifindex,
        })
    }
}

struct TcpKissTnc {
    iface: Arc<kiss::TcpKissInterface>,
}

impl TcpKissTnc {
    fn open(config: &TcpKissConfig) -> Result<Self, TncError> {
        Ok(Self {
            iface: Arc::new(
                kiss::TcpKissInterface::new(format!("{}:{}", config.host, config.port))
                    .map_err(|e| TncError::OpenTnc { source: e })?,
            ),
        })
    }
}

impl TncImpl for TcpKissTnc {
    fn send_frame(&self, frame: &Ax25Frame) -> Result<(), TncError> {
        self.iface
            .send_frame(&frame.to_bytes())
            .map_err(|e| TncError::SendFrame { source: e })
    }

    fn receive_frame(&self) -> Result<Ax25Frame, TncError> {
        loop {
            let bytes = self
                .iface
                .receive_frame()
                .map_err(|e| TncError::ReceiveFrame { source: e })?;
            if let Ok(parsed) = Ax25Frame::from_bytes(&bytes) {
                return Ok(parsed);
            }
        }
    }

    fn clone(&self) -> Box<dyn TncImpl> {
        Box::new(TcpKissTnc {
            iface: self.iface.clone(),
        })
    }
}

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

    #[test]
    fn parse_tnc_addresses() {
        assert_eq!(
            "tnc:tcpkiss:192.168.0.1:8001".parse::<TncAddress>(),
            Ok(TncAddress {
                config: ConnectConfig::TcpKiss(TcpKissConfig {
                    host: "192.168.0.1".to_string(),
                    port: 8001_u16,
                })
            })
        );
        assert_eq!(
            "tnc:linuxif:VK7NTK-2".parse::<TncAddress>(),
            Ok(TncAddress {
                config: ConnectConfig::LinuxIf(LinuxIfConfig {
                    callsign: "VK7NTK-2".to_string(),
                })
            })
        );
        assert!(match "fish".parse::<TncAddress>() {
            Err(ParseError::NoTncPrefix { .. }) => true,
            _ => false,
        });
        assert!(match "tnc:".parse::<TncAddress>() {
            Err(ParseError::UnknownType { tnc_type }) => tnc_type == "",
            _ => false,
        });
        assert!(match "tnc:fish".parse::<TncAddress>() {
            Err(ParseError::UnknownType { tnc_type }) => tnc_type == "fish",
            _ => false,
        });
        assert!(match "tnc:tcpkiss".parse::<TncAddress>() {
            Err(ParseError::WrongParameterCount {
                tnc_type,
                expected,
                actual,
            }) => {
                tnc_type == "tcpkiss" && expected == 2 && actual == 0
            }
            _ => false,
        });
        assert!(match "tnc:tcpkiss:".parse::<TncAddress>() {
            Err(ParseError::WrongParameterCount {
                tnc_type,
                expected,
                actual,
            }) => {
                tnc_type == "tcpkiss" && expected == 2 && actual == 1
            }
            _ => false,
        });
        assert!(match "tnc:tcpkiss:a:b:c".parse::<TncAddress>() {
            Err(ParseError::WrongParameterCount {
                tnc_type,
                expected,
                actual,
            }) => {
                tnc_type == "tcpkiss" && expected == 2 && actual == 3
            }
            _ => false,
        });
        assert!(match "tnc:tcpkiss:192.168.0.1".parse::<TncAddress>() {
            Err(ParseError::WrongParameterCount {
                tnc_type,
                expected,
                actual,
            }) => {
                tnc_type == "tcpkiss" && expected == 2 && actual == 1
            }
            _ => false,
        });
        assert!(
            match "tnc:tcpkiss:192.168.0.1:hello".parse::<TncAddress>() {
                Err(ParseError::InvalidPort { input, .. }) => input == "hello",
                _ => false,
            }
        );
    }
}
<commit_msg>Fix formatting<commit_after>use crate::frame::Ax25Frame;
use crate::kiss;
use crate::linux;
use std::collections::VecDeque;
use std::str::FromStr;
use std::sync::mpsc::{channel, Receiver, Sender};
use std::sync::{Arc, Mutex};
use std::thread;
use thiserror::Error;

/// Errors that can occur when interacting with a `Tnc`.
#[derive(Debug, Error)]
pub enum TncError {
    #[error("Unable to connect to TNC: {}", source)]
    OpenTnc { source: std::io::Error },
    #[error("Interface with specified callsign '{}' does not exist", callsign)]
    InterfaceNotFound { callsign: String },
    #[error("Unable to send frame: {}", source)]
    SendFrame { source: std::io::Error },
    #[error("Unable to receive frame: {}", source)]
    ReceiveFrame { source: std::io::Error },
    #[error("Unable to make configuration change: {}", source)]
    ConfigFailed { source: std::io::Error },
}

/// Errors that can occur when parsing a `TncAddress` from a string.
#[derive(Debug, Error, PartialEq)]
pub enum ParseError {
    #[error("TNC address '{}' is invalid - it should begin with 'tnc:'", string)]
    NoTncPrefix { string: String },
    #[error("Unknown TNC type {}", tnc_type)]
    UnknownType { tnc_type: String },
    #[error(
        "TNC type '{}' expects {} parameters to follow but there are {}",
        tnc_type,
        expected,
        actual
    )]
    WrongParameterCount {
        tnc_type: String,
        expected: usize,
        actual: usize,
    },
    #[error("Supplied port '{}' should be a number from 0 to 65535", input)]
    InvalidPort {
        input: String,
        source: std::num::ParseIntError,
    },
}

/// Configuration details for a TCP KISS TNC. This structure can be created directly
/// or indirectly by parsing a string into a `TncAddress`.
#[derive(PartialEq, Debug)]
pub struct TcpKissConfig {
    /// Hostname or IP address of the computer with the TNC
    pub host: String,
    /// Port number
    pub port: u16,
}

/// Configuration details for a TNC attached as a Linux network interface using
/// `kissattach`. This structure can be created directly or indirectly by parsing
/// a string into a `TncAddress`.
#[derive(PartialEq, Debug)]
pub struct LinuxIfConfig {
    /// The hardware address associated with the interface, e.g. "VK7NTK-2"
    pub callsign: String,
}

#[derive(PartialEq, Debug)]
pub(crate) enum ConnectConfig {
    TcpKiss(TcpKissConfig),
    LinuxIf(LinuxIfConfig),
}

/// A parsed TNC address that can be used to open a `Tnc`.
#[derive(PartialEq, Debug)]
pub struct TncAddress {
    pub(crate) config: ConnectConfig,
}

impl TncAddress {
    /// Programmatically create a `TncAddress` pointing to a Linux network interface.
    pub fn new_linuxif(linuxif: LinuxIfConfig) -> Self {
        TncAddress {
            config: ConnectConfig::LinuxIf(linuxif),
        }
    }

    /// Porgrammatically create a `TncAddress` pointing to a KISS TCP service.
    pub fn new_tcpkiss(tcpkiss: TcpKissConfig) -> Self {
        TncAddress {
            config: ConnectConfig::TcpKiss(tcpkiss),
        }
    }
}

impl FromStr for TncAddress {
    type Err = ParseError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        if !s.starts_with("tnc:") {
            return Err(ParseError::NoTncPrefix {
                string: s.to_string(),
            });
        }
        let components: Vec<&str> = s.split(':').collect();
        let len = components.len();
        Ok(match components[1] {
            "tcpkiss" => {
                if len != 4 {
                    return Err(ParseError::WrongParameterCount {
                        tnc_type: components[1].to_string(),
                        expected: 2usize,
                        actual: len - 2,
                    });
                }
                TncAddress {
                    config: ConnectConfig::TcpKiss(TcpKissConfig {
                        host: components[2].to_string(),
                        port: components[3].parse().map_err(|e| ParseError::InvalidPort {
                            input: components[3].to_string(),
                            source: e,
                        })?,
                    }),
                }
            }
            "linuxif" => {
                if len != 3 {
                    return Err(ParseError::WrongParameterCount {
                        tnc_type: components[1].to_string(),
                        expected: 1usize,
                        actual: len - 2,
                    });
                }
                TncAddress {
                    config: ConnectConfig::LinuxIf(LinuxIfConfig {
                        callsign: components[2].to_string(),
                    }),
                }
            }
            unknown => {
                return Err(ParseError::UnknownType {
                    tnc_type: unknown.to_string(),
                })
            }
        })
    }
}

trait TncImpl: Send + Sync {
    fn send_frame(&self, frame: &Ax25Frame) -> Result<(), TncError>;
    fn receive_frame(&self) -> Result<Ax25Frame, TncError>;
    fn clone(&self) -> Box<dyn TncImpl>;
}

/// A local or remote TNC attached to a radio, which can send and receive frames.
pub struct Tnc {
    imp: Box<dyn TncImpl>,
    senders: Arc<Mutex<Vec<Sender<Ax25Frame>>>>,
    buffer: Arc<Mutex<VecDeque<Ax25Frame>>>,
}

impl Tnc {
    /// Attempt to obtain a `Tnc` connection using the provided address.
    pub fn open(address: &TncAddress) -> Result<Self, TncError> {
        let imp: Box<dyn TncImpl> = match &address.config {
            ConnectConfig::TcpKiss(config) => Box::new(TcpKissTnc::open(&config)?),
            ConnectConfig::LinuxIf(config) => Box::new(LinuxIfTnc::open(&config)?),
        };
        Ok(Tnc::new(imp))
    }

    fn new(imp: Box<dyn TncImpl>) -> Self {
        let tnc = Tnc {
            imp,
            senders: Arc::new(Mutex::new(Vec::new())),
            buffer: Arc::new(Mutex::new(VecDeque::new())),
        };
        {
            let tnc = tnc.clone();
            // TODO how to allow this thread to exit?
            thread::spawn(move || {
                loop {
                    // TODO what to do if this fails?
                    if let Ok(x) = tnc.imp.receive_frame() {
                        tnc.buffer.lock().unwrap().push_back(x.clone());
                        tnc.senders.lock().unwrap().retain(|s| {
                            let x = (&x).clone();
                            // If there's an error, remove sender from vec
                            s.send(x).is_ok()
                        });
                    }
                }
            });
        }
        tnc
    }

    /// Transmit a frame on the radio. Transmission is not guaranteed even if a
    /// `Ok` result is returned.
    pub fn send_frame(&self, frame: &Ax25Frame) -> Result<(), TncError> {
        self.imp.send_frame(frame)
    }

    /// Block to receive a frame from the radio. If you want to do this on
    /// a separate thread from sending, clone the `Tnc`.
    pub fn receive_frame(&self) -> Result<Ax25Frame, TncError> {
        self.imp.receive_frame()
    }

    /// Create a new `Receiver<Ax25Frame>`
    /// This will receive a copy of all incoming frames.
    pub fn incoming(&mut self) -> Receiver<Ax25Frame> {
        let (sender, receiver) = channel();
        self.senders.lock().unwrap().push(sender);
        receiver
    }
}

impl Clone for Tnc {
    fn clone(&self) -> Self {
        Tnc {
            imp: self.imp.clone(),
            senders: self.senders.clone(),
            buffer: self.buffer.clone(),
        }
    }
}

struct LinuxIfTnc {
    socket: Arc<linux::Ax25RawSocket>,
    ifindex: i32,
}

impl LinuxIfTnc {
    fn open(config: &LinuxIfConfig) -> Result<Self, TncError> {
        let socket = linux::Ax25RawSocket::new().map_err(|e| TncError::OpenTnc { source: e })?;
        let ifindex = match socket
            .list_ax25_interfaces()
            .map_err(|e| TncError::OpenTnc { source: e })?
            .iter()
            .find(|nd| nd.name.to_uppercase() == config.callsign.to_uppercase())
        {
            Some(nd) => nd.ifindex,
            None => {
                return Err(TncError::InterfaceNotFound {
                    callsign: config.callsign.clone(),
                })
            }
        };
        Ok(Self {
            socket: Arc::new(socket),
            ifindex,
        })
    }
}

impl TncImpl for LinuxIfTnc {
    fn send_frame(&self, frame: &Ax25Frame) -> Result<(), TncError> {
        self.socket
            .send_frame(&frame.to_bytes(), self.ifindex)
            .map_err(|e| TncError::SendFrame { source: e })
    }

    fn receive_frame(&self) -> Result<Ax25Frame, TncError> {
        loop {
            let bytes = self
                .socket
                .receive_frame(self.ifindex)
                .map_err(|e| TncError::ReceiveFrame { source: e })?;
            if let Ok(parsed) = Ax25Frame::from_bytes(&bytes) {
                return Ok(parsed);
            }
        }
    }

    fn clone(&self) -> Box<dyn TncImpl> {
        Box::new(LinuxIfTnc {
            socket: self.socket.clone(),
            ifindex: self.ifindex,
        })
    }
}

struct TcpKissTnc {
    iface: Arc<kiss::TcpKissInterface>,
}

impl TcpKissTnc {
    fn open(config: &TcpKissConfig) -> Result<Self, TncError> {
        Ok(Self {
            iface: Arc::new(
                kiss::TcpKissInterface::new(format!("{}:{}", config.host, config.port))
                    .map_err(|e| TncError::OpenTnc { source: e })?,
            ),
        })
    }
}

impl TncImpl for TcpKissTnc {
    fn send_frame(&self, frame: &Ax25Frame) -> Result<(), TncError> {
        self.iface
            .send_frame(&frame.to_bytes())
            .map_err(|e| TncError::SendFrame { source: e })
    }

    fn receive_frame(&self) -> Result<Ax25Frame, TncError> {
        loop {
            let bytes = self
                .iface
                .receive_frame()
                .map_err(|e| TncError::ReceiveFrame { source: e })?;
            if let Ok(parsed) = Ax25Frame::from_bytes(&bytes) {
                return Ok(parsed);
            }
        }
    }

    fn clone(&self) -> Box<dyn TncImpl> {
        Box::new(TcpKissTnc {
            iface: self.iface.clone(),
        })
    }
}

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

    #[test]
    fn parse_tnc_addresses() {
        assert_eq!(
            "tnc:tcpkiss:192.168.0.1:8001".parse::<TncAddress>(),
            Ok(TncAddress {
                config: ConnectConfig::TcpKiss(TcpKissConfig {
                    host: "192.168.0.1".to_string(),
                    port: 8001_u16,
                })
            })
        );
        assert_eq!(
            "tnc:linuxif:VK7NTK-2".parse::<TncAddress>(),
            Ok(TncAddress {
                config: ConnectConfig::LinuxIf(LinuxIfConfig {
                    callsign: "VK7NTK-2".to_string(),
                })
            })
        );
        assert!(match "fish".parse::<TncAddress>() {
            Err(ParseError::NoTncPrefix { .. }) => true,
            _ => false,
        });
        assert!(match "tnc:".parse::<TncAddress>() {
            Err(ParseError::UnknownType { tnc_type }) => tnc_type == "",
            _ => false,
        });
        assert!(match "tnc:fish".parse::<TncAddress>() {
            Err(ParseError::UnknownType { tnc_type }) => tnc_type == "fish",
            _ => false,
        });
        assert!(match "tnc:tcpkiss".parse::<TncAddress>() {
            Err(ParseError::WrongParameterCount {
                tnc_type,
                expected,
                actual,
            }) => {
                tnc_type == "tcpkiss" && expected == 2 && actual == 0
            }
            _ => false,
        });
        assert!(match "tnc:tcpkiss:".parse::<TncAddress>() {
            Err(ParseError::WrongParameterCount {
                tnc_type,
                expected,
                actual,
            }) => {
                tnc_type == "tcpkiss" && expected == 2 && actual == 1
            }
            _ => false,
        });
        assert!(match "tnc:tcpkiss:a:b:c".parse::<TncAddress>() {
            Err(ParseError::WrongParameterCount {
                tnc_type,
                expected,
                actual,
            }) => {
                tnc_type == "tcpkiss" && expected == 2 && actual == 3
            }
            _ => false,
        });
        assert!(match "tnc:tcpkiss:192.168.0.1".parse::<TncAddress>() {
            Err(ParseError::WrongParameterCount {
                tnc_type,
                expected,
                actual,
            }) => {
                tnc_type == "tcpkiss" && expected == 2 && actual == 1
            }
            _ => false,
        });
        assert!(
            match "tnc:tcpkiss:192.168.0.1:hello".parse::<TncAddress>() {
                Err(ParseError::InvalidPort { input, .. }) => input == "hello",
                _ => false,
            }
        );
    }
}
<|endoftext|>


<commit_before>use crate::hb;
use std;
use std::os::raw::c_void;
use std::ptr::NonNull;

use std::marker::PhantomData;
use std::path::Path;

use crate::blob::Blob;
use crate::common::{HarfbuzzObject, Owned, Shared, Tag};

/// A wrapper around `hb_face_t`.
///
/// An excerpt from harfbuzz documentation:
/// > Font face is objects represent a single face in a font family. More
/// > exactly, a font face represents a single face in a binary font file. Font
/// > faces are typically built from a binary blob and a face index. Font faces
/// > are used to create fonts.
#[derive(Debug)]
pub struct Face<'a> {
    raw: NonNull<hb::hb_face_t>,
    marker: PhantomData<&'a [u8]>,
}

impl<'a> Face<'a> {
    /// Create a new `Face` from the data.
    ///
    /// If `data` is not a valid font then this function returns the empty face.
    pub fn new<T: Into<Shared<Blob<'a>>>>(data: T, index: u32) -> Owned<Face<'a>> {
        let blob = data.into();
        let hb_face = unsafe { hb::hb_face_create(Shared::into_raw(blob), index) };
        unsafe { Owned::from_raw(hb_face) }
    }

    /// Returns a "null" face.
    pub fn empty() -> Owned<Face<'static>> {
        let hb_face = unsafe { hb::hb_face_get_empty() };
        unsafe { Owned::from_raw(hb_face) }
    }

    /// Create a new face from the contents of the file at `path`.
    ///
    /// This function reads the contents of the file at `path` into memory,
    /// creates a `Blob` and then calls `Face::new`.
    ///
    /// See also the discussion in `Blob::from_file`.
    pub fn from_file<P: AsRef<Path>>(path: P, index: u32) -> std::io::Result<Owned<Face<'static>>> {
        let blob = Blob::from_file(path)?;
        Ok(Face::new(blob, index))
    }

    /// Create a face from the bytes of a given slice and an index specifying
    /// which font to read from an OpenType font collection.
    pub fn from_bytes<'b>(bytes: &'b [u8], index: u32) -> Owned<Face<'b>> {
        let blob = Blob::with_bytes(bytes);
        Face::new(blob, index)
    }

    /// Create a new face from a closure that returns a raw
    /// [`Blob`](struct.Blob.html) of table data.
    pub fn from_table_func<'b, F>(func: F) -> Owned<Face<'b>>
    where
        F: 'b + Send + Sync + FnMut(Tag) -> Option<Shared<Blob<'b>>>,
    {
        extern "C" fn destroy_box<U>(ptr: *mut c_void) {
            unsafe { Box::from_raw(ptr as *mut U) };
        }
        extern "C" fn table_func<'b, F>(
            _: *mut hb::hb_face_t,
            tag: hb::hb_tag_t,
            user_data: *mut c_void,
        ) -> *mut hb::hb_blob_t
        where
            F: FnMut(Tag) -> Option<Shared<Blob<'b>>>,
        {
            let tag = Tag(tag);
            let closure = unsafe { &mut *(user_data as *mut F) };
            let blob = closure(tag);
            match blob {
                Some(blob) => Shared::into_raw(blob),
                None => std::ptr::null_mut(),
            }
        }
        let boxed_closure = Box::new(func);
        unsafe {
            let face = hb::hb_face_create_for_tables(
                Some(table_func::<'b, F>),
                Box::into_raw(boxed_closure) as *mut _,
                Some(destroy_box::<F>),
            );
            Owned::from_raw(face)
        }
    }

    pub fn face_data(&self) -> Shared<Blob<'a>> {
        unsafe {
            let raw_blob = hb::hb_face_reference_blob(self.as_raw());
            Shared::from_raw_owned(raw_blob)
        }
    }

    /// Returns the slice of bytes for the table named `tag` or None if there is
    /// no table with `tag`.
    pub fn table_with_tag(&self, tag: Tag) -> Option<Shared<Blob<'a>>> {
        unsafe {
            let raw_blob = hb::hb_face_reference_table(self.as_raw(), tag.0);
            if raw_blob.is_null() {
                None
            } else {
                let blob: Shared<Blob<'_>> = Shared::from_raw_owned(raw_blob);
                if blob.is_empty() {
                    None
                } else {
                    Some(blob)
                }
            }
        }
    }

    pub fn index(&self) -> u32 {
        unsafe { hb::hb_face_get_index(self.as_raw()) }
    }

    pub fn set_upem(&mut self, upem: u32) {
        unsafe { hb::hb_face_set_upem(self.as_raw(), upem) };
    }

    pub fn upem(&self) -> u32 {
        unsafe { hb::hb_face_get_upem(self.as_raw()) }
    }

    pub fn set_glyph_count(&mut self, count: u32) {
        unsafe { hb::hb_face_set_glyph_count(self.as_raw(), count) };
    }

    /// Returns the number of glyphs contained in the face.
    pub fn glyph_count(&self) -> u32 {
        unsafe { hb::hb_face_get_glyph_count(self.as_raw()) }
    }
}

unsafe impl<'a> HarfbuzzObject for Face<'a> {
    type Raw = hb::hb_face_t;

    unsafe fn from_raw(raw: *const hb::hb_face_t) -> Self {
        Face {
            raw: NonNull::new_unchecked(raw as *mut _),
            marker: PhantomData,
        }
    }

    fn as_raw(&self) -> *mut Self::Raw {
        self.raw.as_ptr()
    }

    unsafe fn reference(&self) {
        hb::hb_face_reference(self.as_raw());
    }

    unsafe fn dereference(&self) {
        hb::hb_face_destroy(self.as_raw());
    }
}

unsafe impl<'a> Send for Face<'a> {}
unsafe impl<'a> Sync for Face<'a> {}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::common::Tag;

    #[test]
    fn test_face_from_table_func() {
        let face = Face::from_table_func(|table_tag| {
            let content = format!("{}-table", table_tag);
            Some(content.into_bytes().into())
        });

        let maxp_table = face.table_with_tag(Tag::new('m', 'a', 'x', 'p')).unwrap();
        assert_eq!(maxp_table.as_ref(), b"maxp-table");

        let maxp_table = face.table_with_tag(Tag::new('h', 'h', 'e', 'a')).unwrap();
        assert_eq!(&maxp_table.as_ref(), b"hhea-table");
    }
}
<commit_msg>Fix memory Leak on face creation<commit_after>use crate::hb;
use std;
use std::os::raw::c_void;
use std::ptr::NonNull;

use std::marker::PhantomData;
use std::path::Path;

use crate::blob::Blob;
use crate::common::{HarfbuzzObject, Owned, Shared, Tag};

/// A wrapper around `hb_face_t`.
///
/// An excerpt from harfbuzz documentation:
/// > Font face is objects represent a single face in a font family. More
/// > exactly, a font face represents a single face in a binary font file. Font
/// > faces are typically built from a binary blob and a face index. Font faces
/// > are used to create fonts.
#[derive(Debug)]
pub struct Face<'a> {
    raw: NonNull<hb::hb_face_t>,
    marker: PhantomData<&'a [u8]>,
}

impl<'a> Face<'a> {
    /// Create a new `Face` from the data.
    ///
    /// If `data` is not a valid font then this function returns the empty face.
    pub fn new<T: Into<Shared<Blob<'a>>>>(data: T, index: u32) -> Owned<Face<'a>> {
        let blob = data.into();
        let hb_face = unsafe { hb::hb_face_create(blob.as_raw(), index) };
        unsafe { Owned::from_raw(hb_face) }
    }

    /// Returns a "null" face.
    pub fn empty() -> Owned<Face<'static>> {
        let hb_face = unsafe { hb::hb_face_get_empty() };
        unsafe { Owned::from_raw(hb_face) }
    }

    /// Create a new face from the contents of the file at `path`.
    ///
    /// This function reads the contents of the file at `path` into memory,
    /// creates a `Blob` and then calls `Face::new`.
    ///
    /// See also the discussion in `Blob::from_file`.
    pub fn from_file<P: AsRef<Path>>(path: P, index: u32) -> std::io::Result<Owned<Face<'static>>> {
        let blob = Blob::from_file(path)?;
        Ok(Face::new(blob, index))
    }

    /// Create a face from the bytes of a given slice and an index specifying
    /// which font to read from an OpenType font collection.
    pub fn from_bytes<'b>(bytes: &'b [u8], index: u32) -> Owned<Face<'b>> {
        let blob = Blob::with_bytes(bytes);
        Face::new(blob, index)
    }

    /// Create a new face from a closure that returns a raw
    /// [`Blob`](struct.Blob.html) of table data.
    pub fn from_table_func<'b, F>(func: F) -> Owned<Face<'b>>
    where
        F: 'b + Send + Sync + FnMut(Tag) -> Option<Shared<Blob<'b>>>,
    {
        extern "C" fn destroy_box<U>(ptr: *mut c_void) {
            unsafe { Box::from_raw(ptr as *mut U) };
        }
        extern "C" fn table_func<'b, F>(
            _: *mut hb::hb_face_t,
            tag: hb::hb_tag_t,
            user_data: *mut c_void,
        ) -> *mut hb::hb_blob_t
        where
            F: FnMut(Tag) -> Option<Shared<Blob<'b>>>,
        {
            let tag = Tag(tag);
            let closure = unsafe { &mut *(user_data as *mut F) };
            let blob = closure(tag);
            match blob {
                Some(blob) => Shared::into_raw(blob),
                None => std::ptr::null_mut(),
            }
        }
        let boxed_closure = Box::new(func);
        unsafe {
            let face = hb::hb_face_create_for_tables(
                Some(table_func::<'b, F>),
                Box::into_raw(boxed_closure) as *mut _,
                Some(destroy_box::<F>),
            );
            Owned::from_raw(face)
        }
    }

    pub fn face_data(&self) -> Shared<Blob<'a>> {
        unsafe {
            let raw_blob = hb::hb_face_reference_blob(self.as_raw());
            Shared::from_raw_owned(raw_blob)
        }
    }

    /// Returns the slice of bytes for the table named `tag` or None if there is
    /// no table with `tag`.
    pub fn table_with_tag(&self, tag: Tag) -> Option<Shared<Blob<'a>>> {
        unsafe {
            let raw_blob = hb::hb_face_reference_table(self.as_raw(), tag.0);
            if raw_blob.is_null() {
                None
            } else {
                let blob: Shared<Blob<'_>> = Shared::from_raw_owned(raw_blob);
                if blob.is_empty() {
                    None
                } else {
                    Some(blob)
                }
            }
        }
    }

    pub fn index(&self) -> u32 {
        unsafe { hb::hb_face_get_index(self.as_raw()) }
    }

    pub fn set_upem(&mut self, upem: u32) {
        unsafe { hb::hb_face_set_upem(self.as_raw(), upem) };
    }

    pub fn upem(&self) -> u32 {
        unsafe { hb::hb_face_get_upem(self.as_raw()) }
    }

    pub fn set_glyph_count(&mut self, count: u32) {
        unsafe { hb::hb_face_set_glyph_count(self.as_raw(), count) };
    }

    /// Returns the number of glyphs contained in the face.
    pub fn glyph_count(&self) -> u32 {
        unsafe { hb::hb_face_get_glyph_count(self.as_raw()) }
    }
}

unsafe impl<'a> HarfbuzzObject for Face<'a> {
    type Raw = hb::hb_face_t;

    unsafe fn from_raw(raw: *const hb::hb_face_t) -> Self {
        Face {
            raw: NonNull::new_unchecked(raw as *mut _),
            marker: PhantomData,
        }
    }

    fn as_raw(&self) -> *mut Self::Raw {
        self.raw.as_ptr()
    }

    unsafe fn reference(&self) {
        hb::hb_face_reference(self.as_raw());
    }

    unsafe fn dereference(&self) {
        hb::hb_face_destroy(self.as_raw());
    }
}

unsafe impl<'a> Send for Face<'a> {}
unsafe impl<'a> Sync for Face<'a> {}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::common::Tag;

    #[test]
    fn test_face_from_table_func() {
        let face = Face::from_table_func(|table_tag| {
            let content = format!("{}-table", table_tag);
            Some(content.into_bytes().into())
        });

        let maxp_table = face.table_with_tag(Tag::new('m', 'a', 'x', 'p')).unwrap();
        assert_eq!(maxp_table.as_ref(), b"maxp-table");

        let maxp_table = face.table_with_tag(Tag::new('h', 'h', 'e', 'a')).unwrap();
        assert_eq!(&maxp_table.as_ref(), b"hhea-table");
    }
}
<|endoftext|>


<commit_before>use std::io;
use std::fmt;
use std::error;
use std::vec::Vec;
use byteorder;
use byteorder::{BigEndian,ReadBytesExt,WriteBytesExt};

const PADDING : usize = 4;

#[derive(Debug)]
pub enum Error {
	Io(byteorder::Error),
	InvalidValue,
	InvalidType
}

impl From<byteorder::Error> for Error {
	fn from(err: byteorder::Error) -> Error { Error::Io(err) }
}

impl From<Error> for byteorder::Error {
	fn from(err: Error) -> byteorder::Error {
		match err {
			Error::Io(err) => err,
			Error::InvalidValue => byteorder::Error::Io(io::Error::new(io::ErrorKind::Other, "Invalid value")),
			Error::InvalidType => byteorder::Error::Io(io::Error::new(io::ErrorKind::Other, "Invalid type")),
		}
	}
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
	        match *self {
				Error::InvalidValue => write!(f, "Invalid value."),
				Error::InvalidType => write!(f, "Invalid type."),
				Error::Io(ref err) => err.fmt(f),
			}
	}
}

impl error::Error for Error {
    fn description(&self) -> &str {
        match *self {
            Error::InvalidValue => "Invalid value.",
            Error::InvalidType => "Invalid type.",
            Error::Io(ref err) => error::Error::description(err),
        }
    }

    fn cause(&self) -> Option<&error::Error> {
        match *self {
            Error::InvalidValue => None,
            Error::InvalidType => None,
            Error::Io(ref err) => err.cause(),
        }
    }
}

pub struct XdrReader<'a> {
	reader : io::Cursor<&'a [u8]>
}

pub struct XdrWriter {
	writer : io::Cursor<Vec<u8>>
}

impl XdrWriter {
	pub fn new() -> XdrWriter {
		let v : Vec<u8>= Vec::new();
		XdrWriter{ writer: io::Cursor::new(v)}
	}

	pub fn into_buffer(self) -> Vec<u8> {
		self.writer.into_inner()
	}

	pub fn pack<T:XdrPrimitive>(&mut self, x:T) {
		XdrPrimitive::write_to_xdr(self, x)
	}

	pub fn pack_array<T:XdrPrimitive>(&mut self, x:Vec<T>) {
		for t in x {
			XdrPrimitive::write_to_xdr(self,t);
		};
	}

}
impl<'a> XdrReader<'a> {
	pub fn new(x:&'a Vec<u8>) -> XdrReader<'a> {
		XdrReader{ reader : io::Cursor::new(&x) }
	}

	pub fn from_array(x: &'a [u8]) -> XdrReader<'a> {
		XdrReader{ reader: io::Cursor::new(x) }
	}

	pub fn unpack<T: XdrPrimitive>(&mut self) -> Result<T,Error> {
		T::read_from_xdr(self)
	}

	pub fn unpack_array<T:XdrPrimitive>(&mut self, n : usize) -> Result<Vec<T>,Error> {
		let mut result : Vec<T> = Vec::with_capacity(n);

		for _ in 0..n {
			let t =  T::read_from_xdr(self).unwrap();
			result.push(t)
		};
		Ok(result)
	}
}

pub trait XdrPrimitive {
	fn read_from_xdr(x: &mut XdrReader) -> Result<Self, Error>;
	fn write_to_xdr(x: &mut XdrWriter, v: Self);
}

impl<T:XdrPrimitive> XdrPrimitive for Vec<T> {
	fn read_from_xdr(x: &mut XdrReader) -> Result<Self, Error>{
		let count = x.unpack::<u32>().unwrap() as usize;
		let mut result : Vec<T> = Vec::with_capacity(count);

		for _ in 0..count {
			result.push(x.unpack::<T>().unwrap());
		};
		Ok(result)
	}
	fn write_to_xdr(x: &mut XdrWriter, v: Self) {
		x.pack( v.len() as u32 );
		for t in v {
			x.pack(t);
		};
	}
}

impl XdrPrimitive for String {
	fn read_from_xdr(x: &mut XdrReader) -> Result<Self, Error>{
		let len = x.unpack::<u32>().unwrap() as usize;
		let pad = PADDING - (len % PADDING);
		let bytes = x.unpack_array::<u8>(len).unwrap();

		if pad != 0 {
			let _ = x.unpack_array::<u8>(pad);
		};

		Ok(String::from_utf8(bytes).unwrap())
	}

	fn write_to_xdr(x: &mut XdrWriter, v: Self) {
		let bytes = v.into_bytes();
		let bytes_len = bytes.len();
		let pad = PADDING - (bytes_len % PADDING);
		x.pack(bytes);

		for _ in 0..pad {
			x.pack(0u8);
		};
	}
}

impl XdrPrimitive for u32 {
	fn read_from_xdr(x: &mut XdrReader) -> Result<u32, Error>{
		match x.reader.read_u32::<byteorder::BigEndian>() {
			Ok(v) => Ok(v),
			Err(v) => Err(Error::Io(v))
		}

	}
	fn write_to_xdr(x: &mut XdrWriter, v:Self) {
		x.writer.write_u32::<byteorder::BigEndian>(v).unwrap();
	}
}
impl XdrPrimitive for u16 {
	fn read_from_xdr(x: &mut XdrReader) -> Result<u16, Error>{
		match x.reader.read_u32::<byteorder::BigEndian>() {
			Ok(v) => Ok(v as u16),
			Err(v) => Err(Error::Io(v))
		}

	}
	fn write_to_xdr(x: &mut XdrWriter, v:Self) {
		x.writer.write_u32::<byteorder::BigEndian>(v as u32).unwrap();
	}
}
impl XdrPrimitive for u8 {
	fn read_from_xdr(x: &mut XdrReader) -> Result<u8, Error>{
		match x.reader.read_u32::<byteorder::BigEndian>() {
			Ok(v) => Ok(v as u8),
			Err(v) => Err(Error::Io(v))
		}

	}
	fn write_to_xdr(x: &mut XdrWriter, v:Self) {
		x.writer.write_u32::<byteorder::BigEndian>(v as u32).unwrap();
	}
}
impl XdrPrimitive for i8 {
	fn read_from_xdr(x: &mut XdrReader) -> Result<i8, Error>{
		match x.reader.read_i32::<byteorder::BigEndian>() {
			Ok(v) => Ok(v as i8),
			Err(v) => Err(Error::Io(v))
		}

	}
	fn write_to_xdr(x: &mut XdrWriter, v:Self) {
		x.writer.write_i32::<byteorder::BigEndian>(v as i32).unwrap();
	}
}
impl XdrPrimitive for i16 {
	fn read_from_xdr(x: &mut XdrReader) -> Result<i16, Error>{
		match x.reader.read_i32::<byteorder::BigEndian>() {
			Ok(v) => Ok(v as i16),
			Err(v) => Err(Error::Io(v))
		}

	}
	fn write_to_xdr(x: &mut XdrWriter, v:Self) {
		x.writer.write_i32::<byteorder::BigEndian>(v as i32).unwrap();
	}
}
impl XdrPrimitive for i32 {
	fn read_from_xdr(x: &mut XdrReader) -> Result<i32, Error>{
		match x.reader.read_i32::<byteorder::BigEndian>() {
			Ok(v) => Ok(v),
			Err(v) => Err(Error::Io(v))
		}

	}
	fn write_to_xdr(x: &mut XdrWriter, v:Self) {
		x.writer.write_i32::<byteorder::BigEndian>(v).unwrap();
	}
}
impl XdrPrimitive for i64 {
	fn read_from_xdr(x: &mut XdrReader) -> Result<i64, Error>{
		match x.reader.read_i64::<byteorder::BigEndian>() {
			Ok(v) => Ok(v),
			Err(v) => Err(Error::Io(v))
		}

	}
	fn write_to_xdr(x: &mut XdrWriter, v:Self) {
		x.writer.write_i64::<byteorder::BigEndian>(v).unwrap();
	}
}
impl XdrPrimitive for u64 {
	fn read_from_xdr(x: &mut XdrReader) -> Result<u64, Error>{
		match x.reader.read_u64::<byteorder::BigEndian>() {
			Ok(v) => Ok(v),
			Err(v) => Err(Error::Io(v))
		}

	}
	fn write_to_xdr(x: &mut XdrWriter, v:Self) {
		x.writer.write_u64::<byteorder::BigEndian>(v).unwrap();
	}

}

impl XdrPrimitive for f32 {
	fn read_from_xdr(x: &mut XdrReader) -> Result<f32, Error>{
		match x.reader.read_f32::<byteorder::BigEndian>() {
			Ok(v) => Ok(v),
			Err(v) => Err(Error::Io(v))
		}

	}
	fn write_to_xdr(x: &mut XdrWriter, v:Self) {
		x.writer.write_f32::<byteorder::BigEndian>(v).unwrap();
	}
}
impl XdrPrimitive for f64 {
	fn read_from_xdr(x: &mut XdrReader) -> Result<f64, Error>{
		match x.reader.read_f64::<byteorder::BigEndian>() {
			Ok(v) => Ok(v),
			Err(v) => Err(Error::Io(v))
		}

	}
	fn write_to_xdr(x: &mut XdrWriter, v:Self) {
		x.writer.write_f64::<byteorder::BigEndian>(v).unwrap();
	}
}

impl XdrPrimitive for bool {
	fn read_from_xdr(x: &mut XdrReader) -> Result<bool, Error>{
		match x.reader.read_u32::<byteorder::BigEndian>() {
			Ok(0) => Ok(false),
			Ok(1) => Ok(true),
			Ok(_) => Err(Error::InvalidValue),
			Err(v) => Err(Error::Io(v))
		}
	}

	fn write_to_xdr(x: &mut XdrWriter, v:Self) {
		match v {
			true => x.writer.write_u32::<byteorder::BigEndian>(1).unwrap(),
			false => x.writer.write_u32::<byteorder::BigEndian>(0).unwrap(),
		}
	}
}
<commit_msg>Identation fixes<commit_after>use std::io;
use std::fmt;
use std::error;
use std::vec::Vec;
use byteorder;
use byteorder::{BigEndian,ReadBytesExt,WriteBytesExt};

const PADDING : usize = 4;

#[derive(Debug)]
pub enum Error {
	Io(byteorder::Error),
		InvalidValue,
		InvalidType
}

impl From<byteorder::Error> for Error {
	fn from(err: byteorder::Error) -> Error { Error::Io(err) }
}

impl From<Error> for byteorder::Error {
	fn from(err: Error) -> byteorder::Error {
		match err {
			Error::Io(err) => err,
			Error::InvalidValue => byteorder::Error::Io(io::Error::new(io::ErrorKind::Other, "Invalid value")),
			Error::InvalidType => byteorder::Error::Io(io::Error::new(io::ErrorKind::Other, "Invalid type")),
		}
	}
}

impl fmt::Display for Error {
	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
		match *self {
			Error::InvalidValue => write!(f, "Invalid value."),
			Error::InvalidType => write!(f, "Invalid type."),
			Error::Io(ref err) => err.fmt(f),
		}
	}
}

impl error::Error for Error {
	fn description(&self) -> &str {
		match *self {
			Error::InvalidValue => "Invalid value.",
			Error::InvalidType => "Invalid type.",
			Error::Io(ref err) => error::Error::description(err),
		}
	}

	fn cause(&self) -> Option<&error::Error> {
		match *self {
			Error::InvalidValue => None,
			Error::InvalidType => None,
			Error::Io(ref err) => err.cause(),
		}
	}
}

pub struct XdrReader<'a> {
	reader : io::Cursor<&'a [u8]>
}

pub struct XdrWriter {
	writer : io::Cursor<Vec<u8>>
}

impl XdrWriter {
	pub fn new() -> XdrWriter {
		let v : Vec<u8>= Vec::new();
		XdrWriter{ writer: io::Cursor::new(v)}
	}

	pub fn into_buffer(self) -> Vec<u8> {
		self.writer.into_inner()
	}

	pub fn pack<T:XdrPrimitive>(&mut self, x:T) {
		XdrPrimitive::write_to_xdr(self, x)
	}

	pub fn pack_array<T:XdrPrimitive>(&mut self, x:Vec<T>) {
		for t in x {
			XdrPrimitive::write_to_xdr(self,t);
		};
	}

}
impl<'a> XdrReader<'a> {
	pub fn new(x:&'a Vec<u8>) -> XdrReader<'a> {
		XdrReader{ reader : io::Cursor::new(&x) }
	}

	pub fn from_array(x: &'a [u8]) -> XdrReader<'a> {
		XdrReader{ reader: io::Cursor::new(x) }
	}

	pub fn unpack<T: XdrPrimitive>(&mut self) -> Result<T,Error> {
		T::read_from_xdr(self)
	}

	pub fn unpack_array<T:XdrPrimitive>(&mut self, n : usize) -> Result<Vec<T>,Error> {
		let mut result : Vec<T> = Vec::with_capacity(n);

		for _ in 0..n {
			let t =  T::read_from_xdr(self).unwrap();
			result.push(t)
		};
		Ok(result)
	}
}

pub trait XdrPrimitive {
	fn read_from_xdr(x: &mut XdrReader) -> Result<Self, Error>;
	fn write_to_xdr(x: &mut XdrWriter, v: Self);
}

impl<T:XdrPrimitive> XdrPrimitive for Vec<T> {
	fn read_from_xdr(x: &mut XdrReader) -> Result<Self, Error>{
		let count = x.unpack::<u32>().unwrap() as usize;
		let mut result : Vec<T> = Vec::with_capacity(count);

		for _ in 0..count {
			result.push(x.unpack::<T>().unwrap());
		};
		Ok(result)
	}
	fn write_to_xdr(x: &mut XdrWriter, v: Self) {
		x.pack( v.len() as u32 );
		for t in v {
			x.pack(t);
		};
	}
}

impl XdrPrimitive for String {
	fn read_from_xdr(x: &mut XdrReader) -> Result<Self, Error>{
		let len = x.unpack::<u32>().unwrap() as usize;
		let pad = PADDING - (len % PADDING);
		let bytes = x.unpack_array::<u8>(len).unwrap();

		if pad != 0 {
			let _ = x.unpack_array::<u8>(pad);
		};

		Ok(String::from_utf8(bytes).unwrap())
	}

	fn write_to_xdr(x: &mut XdrWriter, v: Self) {
		let bytes = v.into_bytes();
		let bytes_len = bytes.len();
		let pad = PADDING - (bytes_len % PADDING);
		x.pack(bytes);

		for _ in 0..pad {
			x.pack(0u8);
		};
	}
}

impl XdrPrimitive for u32 {
	fn read_from_xdr(x: &mut XdrReader) -> Result<u32, Error>{
		match x.reader.read_u32::<byteorder::BigEndian>() {
			Ok(v) => Ok(v),
			Err(v) => Err(Error::Io(v))
		}
	}
	fn write_to_xdr(x: &mut XdrWriter, v:Self) {
		x.writer.write_u32::<byteorder::BigEndian>(v).unwrap();
	}
}
impl XdrPrimitive for u16 {
	fn read_from_xdr(x: &mut XdrReader) -> Result<u16, Error>{
		match x.reader.read_u32::<byteorder::BigEndian>() {
			Ok(v) => Ok(v as u16),
			Err(v) => Err(Error::Io(v))
		}
	}
	fn write_to_xdr(x: &mut XdrWriter, v:Self) {
		x.writer.write_u32::<byteorder::BigEndian>(v as u32).unwrap();
	}
}
impl XdrPrimitive for u8 {
	fn read_from_xdr(x: &mut XdrReader) -> Result<u8, Error>{
		match x.reader.read_u32::<byteorder::BigEndian>() {
			Ok(v) => Ok(v as u8),
			Err(v) => Err(Error::Io(v))
		}
	}
	fn write_to_xdr(x: &mut XdrWriter, v:Self) {
		x.writer.write_u32::<byteorder::BigEndian>(v as u32).unwrap();
	}
}
impl XdrPrimitive for i8 {
	fn read_from_xdr(x: &mut XdrReader) -> Result<i8, Error>{
		match x.reader.read_i32::<byteorder::BigEndian>() {
			Ok(v) => Ok(v as i8),
			Err(v) => Err(Error::Io(v))
		}
	}
	fn write_to_xdr(x: &mut XdrWriter, v:Self) {
		x.writer.write_i32::<byteorder::BigEndian>(v as i32).unwrap();
	}
}
impl XdrPrimitive for i16 {
	fn read_from_xdr(x: &mut XdrReader) -> Result<i16, Error>{
		match x.reader.read_i32::<byteorder::BigEndian>() {
			Ok(v) => Ok(v as i16),
			Err(v) => Err(Error::Io(v))
		}
	}
	fn write_to_xdr(x: &mut XdrWriter, v:Self) {
		x.writer.write_i32::<byteorder::BigEndian>(v as i32).unwrap();
	}
}
impl XdrPrimitive for i32 {
	fn read_from_xdr(x: &mut XdrReader) -> Result<i32, Error>{
		match x.reader.read_i32::<byteorder::BigEndian>() {
			Ok(v) => Ok(v),
			Err(v) => Err(Error::Io(v))
		}
	}
	fn write_to_xdr(x: &mut XdrWriter, v:Self) {
		x.writer.write_i32::<byteorder::BigEndian>(v).unwrap();
	}
}
impl XdrPrimitive for i64 {
	fn read_from_xdr(x: &mut XdrReader) -> Result<i64, Error>{
		match x.reader.read_i64::<byteorder::BigEndian>() {
			Ok(v) => Ok(v),
			Err(v) => Err(Error::Io(v))
		}
	}
	fn write_to_xdr(x: &mut XdrWriter, v:Self) {
		x.writer.write_i64::<byteorder::BigEndian>(v).unwrap();
	}
}
impl XdrPrimitive for u64 {
	fn read_from_xdr(x: &mut XdrReader) -> Result<u64, Error>{
		match x.reader.read_u64::<byteorder::BigEndian>() {
			Ok(v) => Ok(v),
			Err(v) => Err(Error::Io(v))
		}
	}
	fn write_to_xdr(x: &mut XdrWriter, v:Self) {
		x.writer.write_u64::<byteorder::BigEndian>(v).unwrap();
	}
}

impl XdrPrimitive for f32 {
	fn read_from_xdr(x: &mut XdrReader) -> Result<f32, Error>{
		match x.reader.read_f32::<byteorder::BigEndian>() {
			Ok(v) => Ok(v),
			Err(v) => Err(Error::Io(v))
		}
	}
	fn write_to_xdr(x: &mut XdrWriter, v:Self) {
		x.writer.write_f32::<byteorder::BigEndian>(v).unwrap();
	}
}
impl XdrPrimitive for f64 {
	fn read_from_xdr(x: &mut XdrReader) -> Result<f64, Error>{
		match x.reader.read_f64::<byteorder::BigEndian>() {
			Ok(v) => Ok(v),
			Err(v) => Err(Error::Io(v))
		}
	}
	fn write_to_xdr(x: &mut XdrWriter, v:Self) {
		x.writer.write_f64::<byteorder::BigEndian>(v).unwrap();
	}
}

impl XdrPrimitive for bool {
	fn read_from_xdr(x: &mut XdrReader) -> Result<bool, Error>{
		match x.reader.read_u32::<byteorder::BigEndian>() {
			Ok(0) => Ok(false),
			Ok(1) => Ok(true),
			Ok(_) => Err(Error::InvalidValue),
			Err(v) => Err(Error::Io(v))
		}
	}

	fn write_to_xdr(x: &mut XdrWriter, v:Self) {
		match v {
			true => x.writer.write_u32::<byteorder::BigEndian>(1).unwrap(),
			false => x.writer.write_u32::<byteorder::BigEndian>(0).unwrap(),
		}
	}
}
<|endoftext|>


<commit_before>use imgui::{Ui,ImStr,ImString};
use lichen::parse::{StreamParser,Env};

use std::fs;
use std::path::Path;

use app::AppState;

use std::io::{BufReader};
use std::fs::File;

pub struct FileState {
    /// MAX_PATH set to 260 chars
    cd: ImString,
    idx: i32,
    files: Vec<String>,
    selected: Option<String>,
    stream: Option<StreamParser<BufReader<File>>>,
    failed: String,
}

impl Default for FileState {
    fn default() -> Self {
        let mut cd = ImString::from(im_str!("./"));
        cd.reserve(261); //arbitrary max path size

        FileState {
            cd: cd,
            idx: 1,
            files: vec![],
            selected: None,
            stream: None,
            failed: "".to_owned(),
        }
    }
}

impl FileState {
    fn update(&mut self) -> Option<Env> {
        if self.idx > 1 { // chose a file?
            self.failed = "".to_owned();
            if let Some(file) = self.files.get(self.idx as usize) {
                self.cd.clear();
                self.cd.push_str(file);
                self.idx = 1;
            }
        }
        else if self.idx < 1 { // up a directory?
            self.failed = "".to_owned();
            self.to_parent();
        }

        let cd = self.cd.to_string();
        if Path::new(&cd).is_dir() {
            if let Ok(paths) = fs::read_dir(&cd) {
                self.files = paths
                    .filter(|p| p.is_ok())
                    .map(|p| {
                        p.unwrap().path().as_path().to_str().unwrap().to_owned()
                    })
                    .collect();

                self.files.insert(0,"./".to_owned());
                self.files.insert(0,"..".to_owned());
            }
            // NOTE: we should throw a warning here if directory cannot be traversed
        }
        else {
            if !cd.is_empty() {
                self.selected = Some(cd);
                self.failed = "".to_owned();
            }
        }

        if self.stream.is_some() { self.step_stream() }
        else { None }
    }

    /// Steps through the stream chunks, finally returns parsed lichen environment
    fn step_stream (&mut self) -> Option<Env> {
        let r = {
            if let Some(ref mut stream) = self.stream {
                stream.next()
            }
            else { None }
        };

        if r.is_none() { // close stream, we're probably done
            let mut env = Env::empty();
            if let Some(ref mut stream) = self.stream {
                if let Err(e) = stream.sink(&mut env) {
                    self.failed = e.to_owned();
                    return None;
                }
            }

            self.stream = None;
            self.to_parent();

            return Some(env);
        }

        None
    }

    fn to_parent(&mut self) {
        self.selected = None;
        if let Some(path) = Path::new(&self.cd.to_string()).parent() {
            self.cd.clear();
            self.cd.push_str(path.to_str().expect("ERROR: Cannot parse path into string"));
            self.idx = 1;
        }
        // NOTE: this fails if we're at the top of a relative directory listing,
        // or the root of the drive/share
    }

    pub fn render (&mut self, ui: &Ui, state: &mut AppState) {
        if !state.open_file { return }
        state.env = self.update(); // NOTE: we should check if the environment is empty
        state.open_file = !state.env.is_some(); // close file if done

        ui.window(im_str!("Select source"))
            .always_auto_resize(true)
            .movable(true)
            .show_borders(true)
            .opened(&mut state.open_file)
            .build(||{
                ui.text(im_str!("Select a source file to be parsed and evaluated.\nSource files must be parsable through the lichen crate."));
                ui.separator();
                ui.input_text(im_str!("Current Directory"), &mut self.cd)
                    .enter_returns_true(true)
                    .build();
                ui.separator();

                // NOTE: to appease the borrow checker and have the proper argument for list_box
                // we must build the list in two steps
                let paths: Vec<ImString> = self.files.iter().map(|p| {
                    im_str!("{:}", p).to_owned()
                }).collect();

                let paths: Vec<&ImStr> = paths.iter().map(|p|p.as_ref()).collect();

                ui.list_box(im_str!("Files"),
                            &mut self.idx,
                            &paths[..],
                            paths.len() as i32);

                if let Some(ref file) = self.selected {
                    ui.text(im_str!("Select File {:}?",file));

                    if ui.small_button(im_str!("open")) {
                        if let Ok(f) = File::open(file) {
                            let f = BufReader::new(f);
                            let s = StreamParser::new(f,None);
                            self.stream = Some(s);
                        }
                    }
                }

                let mut failure = "".to_owned();
                if !self.failed.is_empty() {
                    failure.push_str("Failed to parse file ");
                    failure.push_str(&self.cd.to_str());
                    failure.push_str("\nParse Error at: ");
                    failure.push_str(&self.failed);
                }

                ui.text(im_str!("{:}", failure));
            })
    }

}

<commit_msg>fix display error<commit_after>use imgui::{Ui,ImStr,ImString};
use lichen::parse::{StreamParser,Env};

use std::fs;
use std::path::Path;

use app::AppState;

use std::io::{BufReader};
use std::fs::File;

pub struct FileState {
    /// MAX_PATH set to 260 chars
    cd: ImString,
    idx: i32,
    files: Vec<String>,
    selected: Option<String>,
    stream: Option<StreamParser<BufReader<File>>>,
    failed: String,
}

impl Default for FileState {
    fn default() -> Self {
        let mut cd = ImString::from(im_str!("./"));
        cd.reserve(261); //arbitrary max path size

        FileState {
            cd: cd,
            idx: 1,
            files: vec![],
            selected: None,
            stream: None,
            failed: "".to_owned(),
        }
    }
}

impl FileState {
    fn update(&mut self) -> Option<Env> {
        if self.idx > 1 { // chose a file?
            self.failed = "".to_owned();

            if let Some(file) = self.files.get(self.idx as usize) {
                self.cd.clear();
                self.cd.push_str(file);
                self.idx = 1;
            }
        }
        else if self.idx < 1 { // up a directory?
            self.failed = "".to_owned();
            self.to_parent();
        }

        let cd = self.cd.to_string();
        if Path::new(&cd).is_dir() {
            if let Ok(paths) = fs::read_dir(&cd) {
                self.files = paths
                    .filter(|p| p.is_ok())
                    .map(|p| {
                        p.unwrap().path().as_path().to_str().unwrap().to_owned()
                    })
                    .collect();

                self.files.insert(0,"./".to_owned());
                self.files.insert(0,"..".to_owned());
            }
            // NOTE: we should throw a warning here if directory cannot be traversed
        }
        else {
            if !cd.is_empty() {
                self.selected = Some(cd);
            }
        }

        if self.stream.is_some() {
            let r = self.step_stream();
            if !self.failed.is_empty() {
                self.stream = None;
            }
            r
        }
        else { None }
    }

    /// Steps through the stream chunks, finally returns parsed lichen environment
    fn step_stream (&mut self) -> Option<Env> {
        let r = {
            if let Some(ref mut stream) = self.stream {
                stream.next()
            }
            else { None }
        };

        if r.is_none() { // close stream, we're probably done
            let mut env = Env::empty();
            if let Some(ref mut stream) = self.stream {
                if let Err(e) = stream.sink(&mut env) {
                    self.failed = e.to_owned();
                    return None;
                }
            }

            self.stream = None;
            self.to_parent();

            return Some(env);
        }

        None
    }

    fn to_parent(&mut self) {
        self.selected = None;
        if let Some(path) = Path::new(&self.cd.to_string()).parent() {
            self.cd.clear();
            self.cd.push_str(path.to_str().expect("ERROR: Cannot parse path into string"));
            self.idx = 1;
        }
        // NOTE: this fails if we're at the top of a relative directory listing,
        // or the root of the drive/share
    }

    pub fn render (&mut self, ui: &Ui, state: &mut AppState) {
        if !state.open_file { return }
        state.env = self.update(); // NOTE: we should check if the environment is empty
        state.open_file = !state.env.is_some(); // close file if done

        ui.window(im_str!("Select source"))
            .always_auto_resize(true)
            .movable(true)
            .show_borders(true)
            .opened(&mut state.open_file)
            .build(||{
                ui.text(im_str!("Select a source file to be parsed and evaluated.\nSource files must be parsable through the lichen crate."));
                ui.separator();
                ui.input_text(im_str!("Current Directory"), &mut self.cd)
                    .enter_returns_true(true)
                    .build();
                ui.separator();

                // NOTE: to appease the borrow checker and have the proper argument for list_box
                // we must build the list in two steps
                let paths: Vec<ImString> = self.files.iter().map(|p| {
                    im_str!("{:}", p).to_owned()
                }).collect();

                let paths: Vec<&ImStr> = paths.iter().map(|p|p.as_ref()).collect();

                ui.list_box(im_str!("Files"),
                            &mut self.idx,
                            &paths[..],
                            paths.len() as i32);

                if let Some(ref file) = self.selected {
                    ui.text(im_str!("Select File {:}?",file));

                    if ui.small_button(im_str!("open")) {
                        if let Ok(f) = File::open(file) {
                            let f = BufReader::new(f);
                            let s = StreamParser::new(f,None);
                            self.stream = Some(s);
                        }
                    }
                }

                let mut failure = "".to_owned();
                if !self.failed.is_empty() {
                    failure.push_str("Failed to parse file ");
                    failure.push_str(&self.cd.to_str());
                    failure.push_str("\nParse Error at: ");
                    failure.push_str(&self.failed);
                }

                ui.text(im_str!("{:}", failure));
            })
    }

}

<|endoftext|>


<commit_before>// See LICENSE file for copyright and license details.

#![macro_escape]

use std::ptr;
use std::mem;
use core_types::{Size2, ZInt};
use visualizer_types::{Color3, ZFloat, ScreenPos, VertexCoord};
use cgmath::{Matrix, Matrix4, Matrix3, ToMatrix4, Vector3, rad, Deg, ToRad};
use libc::c_void;
use gl;
use gl::Gl;
use gl::types::{GLuint, GLsizeiptr};
use std::c_str::CString;

/*
pub const GREY_3: Color3 = Color3{r: 0.3, g: 0.3, b: 0.3};
pub const BLACK_3: Color3 = Color3{r: 0.0, g: 0.0, b: 0.0};
pub const WHITE: Color4 = Color4{r: 1.0, g: 1.0, b: 1.0, a: 1.0};
pub const BLUE: Color4 = Color4{r: 0.0, g: 0.0, b: 1.0, a: 1.0};
pub const BLACK: Color4 = Color4{r: 0.0, g: 0.0, b: 0.0, a: 1.0};
*/

pub enum MeshRenderMode {
    Triangles,
    // Lines,
}

impl MeshRenderMode {
    pub fn to_gl_type(&self) -> GLuint {
        match *self {
            MeshRenderMode::Triangles => gl::TRIANGLES,
            // MeshRenderMode::Lines => gl::LINES,
        }
    }
}

pub struct Mesh {
    vertex_coords_vbo: Vbo,
    length: ZInt,
    mode: MeshRenderMode,
}

impl Mesh {
    pub fn new(zgl: &Zgl, data: &[VertexCoord]) -> Mesh {
        let length = data.len() as ZInt;
        Mesh {
            vertex_coords_vbo: Vbo::from_data(zgl, data),
            length: length,
            mode: MeshRenderMode::Triangles,
        }
    }

    pub fn draw(&self, zgl: &Zgl) {
        self.vertex_coords_vbo.bind(zgl);
        unsafe {
            let attr_id = 0; // TODO: gl.GetAttribLocation(shader, name)
            let components_count = 3;
            let is_normalized = gl::FALSE;
            let stride = 0;
            zgl.gl.VertexAttribPointer(
                attr_id,
                components_count,
                gl::FLOAT,
                is_normalized,
                stride,
                ptr::null_mut(),
            );
            zgl.check();
            zgl.gl.EnableVertexAttribArray(0); // attr_id?
            zgl.check();
            zgl.gl.DrawArrays(self.mode.to_gl_type(), 0, self.length);
            zgl.check();
        }
    }
}

pub struct Zgl {
    pub gl: Gl,
}

impl Zgl {
    pub fn new(get_proc_address: |&str| -> *const c_void) -> Zgl {
        let gl = Gl::load_with(|s| get_proc_address(s));
         Zgl{gl: gl}
    }

    pub fn print_gl_info(&self) {
        println!("GL_VERSION: {}", self.get_info(gl::VERSION));
        println!("GL_SHADING_LANGUAGE_VERSION: {}", self.get_info(gl::SHADING_LANGUAGE_VERSION));
        println!("GL_VENDOR: {}", self.get_info(gl::VENDOR));
        println!("GL_RENDERER: {}", self.get_info(gl::RENDERER));
        // println!("GL_EXTENSIONS: {}", self.get_info(gl::EXTENSIONS));
    }

    pub fn set_clear_color(&mut self, color: Color3) {
        unsafe {
            self.gl.ClearColor(color.r, color.g, color.b, 1.0);
        }
        self.check();
    }

    pub fn clear_screen(&self) {
        unsafe {
            self.gl.Clear(gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT);
        }
        self.check();
    }

    pub fn get_info(&self, name: GLuint) -> String {
        let version = unsafe {
            CString::new(self.gl.GetString(name) as *const i8, false)
        };
        String::from_str(version.as_str()
            .expect("Can`t convert gl.GetString result to rust string"))
    }

    pub fn set_viewport(&mut self, size: &Size2<ZInt>) {
        unsafe {
            self.gl.Viewport(0, 0, size.w, size.h);
        }
        self.check();
    }

    // TODO: replace with something from cgmath-rs
    // from cgmath-rs`s docs:
    // "Transformations are not usually done directly on matrices, but go
    // through transformation objects that can be converted to matrices.
    // Rotations go through the Basis types, which are guaranteed to be
    // orthogonal matrices."
    pub fn tr(&self, m: Matrix4<ZFloat>, v: Vector3<ZFloat>) -> Matrix4<ZFloat> {
        let mut t = Matrix4::<ZFloat>::identity();
        t[3][0] = v.x;
        t[3][1] = v.y;
        t[3][2] = v.z;
        m.mul_m(&t)
    }

    /*
    pub fn scale(&self, m: Matrix4<ZFloat>, scale: ZFloat) -> Matrix4<ZFloat> {
        let mut t = Matrix4::<ZFloat>::identity();
        t[0][0] = scale;
        t[1][1] = scale;
        t[2][2] = scale;
        m.mul_m(&t)
    }
    */

    pub fn rot_x(&self, m: Matrix4<ZFloat>, angle: Deg<ZFloat>) -> Matrix4<ZFloat> {
        let rad = angle.to_rad();
        let r = Matrix3::from_angle_x(rad).to_matrix4();
        m.mul_m(&r)
    }

    pub fn rot_z(&self, m: Matrix4<ZFloat>, angle: Deg<ZFloat>) -> Matrix4<ZFloat> {
        let rad = angle.to_rad();
        let r = Matrix3::from_angle_z(rad).to_matrix4();
        m.mul_m(&r)
    }

    pub fn check(&self) {
        let error_code = unsafe { self.gl.GetError() };
        if error_code != 0 {
            let description = match error_code {
                gl::INVALID_ENUM => "GL_INVALID_ENUM",
                gl::INVALID_FRAMEBUFFER_OPERATION => "GL_INVALID_FRAMEBUFFER_OPERATION",
                gl::INVALID_OPERATION => "GL_INVALID_OPERATION",
                gl::INVALID_VALUE => "GL_INVALID_VALUE",
                gl::NO_ERROR => "GL_NO_ERROR",
                gl::OUT_OF_MEMORY => "GL_OUT_OF_MEMORY",
                _ => panic!("Bad gl error code: {}", error_code),
            };
            panic!("gl error: {}({})", description, error_code);
        }
    }

    pub fn read_pixel_bytes(
        &self,
        win_size: &Size2<ZInt>,
        mouse_pos: &ScreenPos,
    ) -> (ZInt, ZInt, ZInt, ZInt) {
        let height = win_size.h;
        let reverted_h = height - mouse_pos.v.y;
        let data: [u8; 4] = [0, 0, 0, 0];
        unsafe {
            let data_ptr = mem::transmute(&data[0]);
            self.gl.ReadPixels(
                mouse_pos.v.x, reverted_h, 1, 1,
                gl::RGBA,
                gl::UNSIGNED_BYTE,
                data_ptr
            );
        }
        self.check();
        (data[0] as ZInt, data[1] as ZInt, data[2] as ZInt, data[3] as ZInt)
    }
}

/*
pub enum MeshRenderMode {
    Triangles,
    Lines,
}

impl MeshRenderMode {
    fn to_gl_type(&self) -> GLuint {
        match *self {
            MeshRenderMode::Triangles => gl::TRIANGLES,
            MeshRenderMode::Lines => gl::LINES,
        }
    }
}
*/

/*
pub fn init_opengl() {
    verify!(gl::Enable(gl::DEPTH_TEST));
    verify!(gl::Enable(gl::BLEND));
    verify!(gl::BlendFunc(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA));
}
*/

pub struct Vbo {
    id: GLuint,
}

impl Vbo {
    pub fn from_data<T>(zgl: &Zgl, data: &[T]) -> Vbo {
        let vbo = Vbo{id: Vbo::get_new_vbo_id(zgl)};
        vbo.bind(zgl);
        let size = mem::size_of::<T>();
        let buf_size = (data.len() * size) as GLsizeiptr;
        if data.len() != 0 {
            unsafe {
                let data_ptr = mem::transmute(&data[0]);
                zgl.gl.BufferData(
                    gl::ARRAY_BUFFER,
                    buf_size,
                    data_ptr,
                    gl::STATIC_DRAW,
                );
                zgl.check();
            }
        }
        vbo
    }

    fn get_new_vbo_id(zgl: &Zgl) -> GLuint {
        let mut id = 0;
        unsafe {
            zgl.gl.GenBuffers(1, &mut id);
            zgl.check();
        }
        id
    }

    pub fn bind(&self, zgl: &Zgl) {
        unsafe {
            zgl.gl.BindBuffer(gl::ARRAY_BUFFER, self.id);
        }
        zgl.check();
    }
}

/*
// TODO: how to pass Zgl to d-tor?
impl Drop for Vbo {
    fn drop(&mut self) {
        unsafe {
            verify!(gl::DeleteBuffers(1, &self.id));
        }
    }
}
*/

/*
pub fn get_2d_screen_matrix(&self, win_size: &Size2<ZInt>) -> Matrix4<ZFloat> {
    let left = 0.0;
    let right = win_size.w as ZFloat;
    let bottom = 0.0;
    let top = win_size.h as ZFloat;
    let near = -1.0;
    let far = 1.0;
    ortho(left, right, bottom, top, near, far)
}
*/

// vim: set tabstop=4 shiftwidth=4 softtabstop=4 expandtab:
<commit_msg>src/zgl.rs: Removed unneeded '#![macro_escape]'<commit_after>// See LICENSE file for copyright and license details.

use std::ptr;
use std::mem;
use core_types::{Size2, ZInt};
use visualizer_types::{Color3, ZFloat, ScreenPos, VertexCoord};
use cgmath::{Matrix, Matrix4, Matrix3, ToMatrix4, Vector3, rad, Deg, ToRad};
use libc::c_void;
use gl;
use gl::Gl;
use gl::types::{GLuint, GLsizeiptr};
use std::c_str::CString;

/*
pub const GREY_3: Color3 = Color3{r: 0.3, g: 0.3, b: 0.3};
pub const BLACK_3: Color3 = Color3{r: 0.0, g: 0.0, b: 0.0};
pub const WHITE: Color4 = Color4{r: 1.0, g: 1.0, b: 1.0, a: 1.0};
pub const BLUE: Color4 = Color4{r: 0.0, g: 0.0, b: 1.0, a: 1.0};
pub const BLACK: Color4 = Color4{r: 0.0, g: 0.0, b: 0.0, a: 1.0};
*/

pub enum MeshRenderMode {
    Triangles,
    // Lines,
}

impl MeshRenderMode {
    pub fn to_gl_type(&self) -> GLuint {
        match *self {
            MeshRenderMode::Triangles => gl::TRIANGLES,
            // MeshRenderMode::Lines => gl::LINES,
        }
    }
}

pub struct Mesh {
    vertex_coords_vbo: Vbo,
    length: ZInt,
    mode: MeshRenderMode,
}

impl Mesh {
    pub fn new(zgl: &Zgl, data: &[VertexCoord]) -> Mesh {
        let length = data.len() as ZInt;
        Mesh {
            vertex_coords_vbo: Vbo::from_data(zgl, data),
            length: length,
            mode: MeshRenderMode::Triangles,
        }
    }

    pub fn draw(&self, zgl: &Zgl) {
        self.vertex_coords_vbo.bind(zgl);
        unsafe {
            let attr_id = 0; // TODO: gl.GetAttribLocation(shader, name)
            let components_count = 3;
            let is_normalized = gl::FALSE;
            let stride = 0;
            zgl.gl.VertexAttribPointer(
                attr_id,
                components_count,
                gl::FLOAT,
                is_normalized,
                stride,
                ptr::null_mut(),
            );
            zgl.check();
            zgl.gl.EnableVertexAttribArray(0); // attr_id?
            zgl.check();
            zgl.gl.DrawArrays(self.mode.to_gl_type(), 0, self.length);
            zgl.check();
        }
    }
}

pub struct Zgl {
    pub gl: Gl,
}

impl Zgl {
    pub fn new(get_proc_address: |&str| -> *const c_void) -> Zgl {
        let gl = Gl::load_with(|s| get_proc_address(s));
         Zgl{gl: gl}
    }

    pub fn print_gl_info(&self) {
        println!("GL_VERSION: {}", self.get_info(gl::VERSION));
        println!("GL_SHADING_LANGUAGE_VERSION: {}", self.get_info(gl::SHADING_LANGUAGE_VERSION));
        println!("GL_VENDOR: {}", self.get_info(gl::VENDOR));
        println!("GL_RENDERER: {}", self.get_info(gl::RENDERER));
        // println!("GL_EXTENSIONS: {}", self.get_info(gl::EXTENSIONS));
    }

    pub fn set_clear_color(&mut self, color: Color3) {
        unsafe {
            self.gl.ClearColor(color.r, color.g, color.b, 1.0);
        }
        self.check();
    }

    pub fn clear_screen(&self) {
        unsafe {
            self.gl.Clear(gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT);
        }
        self.check();
    }

    pub fn get_info(&self, name: GLuint) -> String {
        let version = unsafe {
            CString::new(self.gl.GetString(name) as *const i8, false)
        };
        String::from_str(version.as_str()
            .expect("Can`t convert gl.GetString result to rust string"))
    }

    pub fn set_viewport(&mut self, size: &Size2<ZInt>) {
        unsafe {
            self.gl.Viewport(0, 0, size.w, size.h);
        }
        self.check();
    }

    // TODO: replace with something from cgmath-rs
    // from cgmath-rs`s docs:
    // "Transformations are not usually done directly on matrices, but go
    // through transformation objects that can be converted to matrices.
    // Rotations go through the Basis types, which are guaranteed to be
    // orthogonal matrices."
    pub fn tr(&self, m: Matrix4<ZFloat>, v: Vector3<ZFloat>) -> Matrix4<ZFloat> {
        let mut t = Matrix4::<ZFloat>::identity();
        t[3][0] = v.x;
        t[3][1] = v.y;
        t[3][2] = v.z;
        m.mul_m(&t)
    }

    /*
    pub fn scale(&self, m: Matrix4<ZFloat>, scale: ZFloat) -> Matrix4<ZFloat> {
        let mut t = Matrix4::<ZFloat>::identity();
        t[0][0] = scale;
        t[1][1] = scale;
        t[2][2] = scale;
        m.mul_m(&t)
    }
    */

    pub fn rot_x(&self, m: Matrix4<ZFloat>, angle: Deg<ZFloat>) -> Matrix4<ZFloat> {
        let rad = angle.to_rad();
        let r = Matrix3::from_angle_x(rad).to_matrix4();
        m.mul_m(&r)
    }

    pub fn rot_z(&self, m: Matrix4<ZFloat>, angle: Deg<ZFloat>) -> Matrix4<ZFloat> {
        let rad = angle.to_rad();
        let r = Matrix3::from_angle_z(rad).to_matrix4();
        m.mul_m(&r)
    }

    pub fn check(&self) {
        let error_code = unsafe { self.gl.GetError() };
        if error_code != 0 {
            let description = match error_code {
                gl::INVALID_ENUM => "GL_INVALID_ENUM",
                gl::INVALID_FRAMEBUFFER_OPERATION => "GL_INVALID_FRAMEBUFFER_OPERATION",
                gl::INVALID_OPERATION => "GL_INVALID_OPERATION",
                gl::INVALID_VALUE => "GL_INVALID_VALUE",
                gl::NO_ERROR => "GL_NO_ERROR",
                gl::OUT_OF_MEMORY => "GL_OUT_OF_MEMORY",
                _ => panic!("Bad gl error code: {}", error_code),
            };
            panic!("gl error: {}({})", description, error_code);
        }
    }

    pub fn read_pixel_bytes(
        &self,
        win_size: &Size2<ZInt>,
        mouse_pos: &ScreenPos,
    ) -> (ZInt, ZInt, ZInt, ZInt) {
        let height = win_size.h;
        let reverted_h = height - mouse_pos.v.y;
        let data: [u8; 4] = [0, 0, 0, 0];
        unsafe {
            let data_ptr = mem::transmute(&data[0]);
            self.gl.ReadPixels(
                mouse_pos.v.x, reverted_h, 1, 1,
                gl::RGBA,
                gl::UNSIGNED_BYTE,
                data_ptr
            );
        }
        self.check();
        (data[0] as ZInt, data[1] as ZInt, data[2] as ZInt, data[3] as ZInt)
    }
}

/*
pub enum MeshRenderMode {
    Triangles,
    Lines,
}

impl MeshRenderMode {
    fn to_gl_type(&self) -> GLuint {
        match *self {
            MeshRenderMode::Triangles => gl::TRIANGLES,
            MeshRenderMode::Lines => gl::LINES,
        }
    }
}
*/

/*
pub fn init_opengl() {
    verify!(gl::Enable(gl::DEPTH_TEST));
    verify!(gl::Enable(gl::BLEND));
    verify!(gl::BlendFunc(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA));
}
*/

pub struct Vbo {
    id: GLuint,
}

impl Vbo {
    pub fn from_data<T>(zgl: &Zgl, data: &[T]) -> Vbo {
        let vbo = Vbo{id: Vbo::get_new_vbo_id(zgl)};
        vbo.bind(zgl);
        let size = mem::size_of::<T>();
        let buf_size = (data.len() * size) as GLsizeiptr;
        if data.len() != 0 {
            unsafe {
                let data_ptr = mem::transmute(&data[0]);
                zgl.gl.BufferData(
                    gl::ARRAY_BUFFER,
                    buf_size,
                    data_ptr,
                    gl::STATIC_DRAW,
                );
                zgl.check();
            }
        }
        vbo
    }

    fn get_new_vbo_id(zgl: &Zgl) -> GLuint {
        let mut id = 0;
        unsafe {
            zgl.gl.GenBuffers(1, &mut id);
            zgl.check();
        }
        id
    }

    pub fn bind(&self, zgl: &Zgl) {
        unsafe {
            zgl.gl.BindBuffer(gl::ARRAY_BUFFER, self.id);
        }
        zgl.check();
    }
}

/*
// TODO: how to pass Zgl to d-tor?
impl Drop for Vbo {
    fn drop(&mut self) {
        unsafe {
            verify!(gl::DeleteBuffers(1, &self.id));
        }
    }
}
*/

/*
pub fn get_2d_screen_matrix(&self, win_size: &Size2<ZInt>) -> Matrix4<ZFloat> {
    let left = 0.0;
    let right = win_size.w as ZFloat;
    let bottom = 0.0;
    let top = win_size.h as ZFloat;
    let near = -1.0;
    let far = 1.0;
    ortho(left, right, bottom, top, near, far)
}
*/

// vim: set tabstop=4 shiftwidth=4 softtabstop=4 expandtab:
<|endoftext|>


<commit_before>use time;
use std::io;
use std::io::BufReader;
use std::io::prelude::*;
use std::os::unix::prelude::*;
use std::process::{Command, Stdio};
use std::sync::mpsc::{channel, Receiver};
use std::thread;
use super::Event;
use super::rep::Payload;

/// Represents a description of a github hook
/// A hook should describe a cmd to run and for which
/// events. It should be assumed that the github event name
/// and event payload will be available in env vars
/// `GH_EVENT_NAME` and `GH_EVENT_PAYLOAD` respectively
#[derive(Clone, Default, Debug, RustcDecodable, RustcEncodable)]
pub struct Hook {
  pub cmd: String,
  pub events: Vec<String>,
  pub name: Option<String>,
  pub organization: Option<String>,
  pub repository: Option<String>,
  pub sender: Option<String>
}

impl Hook {

  fn info(&self, msg: String) {
    write!(&mut io::stderr(),
      "{} {}: {}",
       time::now().to_utc().rfc3339(), self.name().clone(), msg);
  }

  fn err(&self, msg: String) {
    print!("{} {}: {}", time::now().to_utc().rfc3339(), self.name().clone(), msg)
  }

  /// return a debug hook that targets all events
  /// and does nothing but echo the event name and payload
  pub fn echo() -> Hook {
     Hook {
       cmd: "echo \"$GH_EVENT_NAME\": \"$GH_EVENT_PAYLOAD\"".to_owned(),
       events: vec!["*".to_owned()],
       ..Default::default()
     }
  }

  /// return true if this hook targets the provided
  /// event name, false otherwise
  pub fn targets(&self, event: &String, payload: &Payload) -> bool {
    // todo: filter on payload.{ repo, org, sender }
    (self.events.contains(event)
     || self.events.contains(&"*".to_owned()))
  }

  /// return name if defined, otherwise cmd
  pub fn name(&self) -> String {
    self.name.clone().unwrap_or(self.cmd.clone())
  }

  /// start recv'ing events
  pub fn recv(&self, rx: Receiver<Event>) {
    loop {
      match rx.recv() {
        Ok(event) => {
          self.run(&event);
          ()
        },
        Err(e) => {
          self.err(format!("Recv err: {}\n", e.to_string()));
          break
        }
      }
    }
  }

  /// run hook cmd, returning true if cmd succeeded, otherwise false
  pub fn run(&self, event: &Event) -> bool {

     fn collect<T: Read + Send + 'static>(stm: Option<T>) ->
       Receiver<io::Result<Vec<String>>> {
       let (tx, rx) = channel();
       if let Some(s) = stm {
         thread::spawn(move || {
           let mut buf = BufReader::with_capacity(64, s);
           let mut lines: Vec<String> = Vec::new();
           loop {
             let mut line = String::new();
             match buf.read_line(&mut line) {
                Ok(0) | Err(_)  => break,
                Ok(_)  => lines.push(line)
             }
           }
           tx.send(Ok(lines)).unwrap()
         });
       } else {
         tx.send(Ok(vec![])).unwrap();
       }
       rx
     }

     match Command::new("/bin/sh")
       .arg("-c")
       .arg(&self.cmd)
       .env("GH_EVENT_NAME", &event.name)
       .env("GH_EVENT_PAYLOAD", &event.payload)
       .stdin(Stdio::null())
       .stdout(Stdio::piped())
       .stderr(Stdio::piped())
       .spawn() {
         Err(e) => {
           self.err(format!("error executing {}: {}\n", self.cmd, e));
           false
         },
         Ok(mut child)  => {

           let stdout = collect(child.stdout.take());
           let stderr = collect(child.stderr.take());
           let status = child.wait();

           match stdout.recv() {
             Ok(Ok(lines)) => for l in lines {
               self.info(l);
             },
             Ok(Err(e))    => self.err(format!("stdout io err {}\n", e)),
             Err(e)        => self.err(format!("stdout recv err {}\n", e))
           };

           match stderr.recv() {
             Ok(Ok(lines)) => for l in lines {
               self.info(l)
             },
             Ok(Err(e))    => self.err(format!("stderr io err {}\n", e)),
             Err(e)        => self.err(format!("stderr recv err {}\n", e))
           };

           match status {
             Ok(s) => {
               if s.success() {
                 self.info("that worked\n".to_owned());
                 true
               } else {
                 match s.code() {
                   Some(c) => self.info(format!("hook exited with status {}\n", c)),
                   _ => if let Some(s) = s.signal() {
                     self.err(format!("process killed by signal {}\n", s))
                   }
                 };
                 false
               }
             },
             Err(e) => {
               self.err(format!("error getting exit status {}\n", e.to_string()));
               false
             }
           }
         }
       }
  }
}

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

  #[test]
  fn test_hook_run_pass() {
    let hook = Hook {
      cmd: "true".to_owned(),
      ..Default::default()
    };
    assert!(hook.run(&Event {
      ..Default::default()
    }))
  }

  #[test]
  fn test_hook_run_fail() {
    let hook = Hook {
      cmd: "false".to_owned(),
      ..Default::default()
    };
    assert!(!hook.run(&Event {
      ..Default::default()
    }))
  }

  #[test]
  fn test_hook_run_panic() {
    let hook = Hook {
      cmd: "oiusdfasdf".to_owned(),
      ..Default::default()
    };
    assert!(!hook.run(&Event {
      ..Default::default()
    }))
  }
}<commit_msg>clone not needed<commit_after>use time;
use std::io;
use std::io::BufReader;
use std::io::prelude::*;
use std::os::unix::prelude::*;
use std::process::{Command, Stdio};
use std::sync::mpsc::{channel, Receiver};
use std::thread;
use super::Event;
use super::rep::Payload;

/// Represents a description of a github hook
/// A hook should describe a cmd to run and for which
/// events. It should be assumed that the github event name
/// and event payload will be available in env vars
/// `GH_EVENT_NAME` and `GH_EVENT_PAYLOAD` respectively
#[derive(Clone, Default, Debug, RustcDecodable, RustcEncodable)]
pub struct Hook {
  pub cmd: String,
  pub events: Vec<String>,
  pub name: Option<String>,
  pub organization: Option<String>,
  pub repository: Option<String>,
  pub sender: Option<String>
}

impl Hook {

  fn info(&self, msg: String) {
    write!(&mut io::stderr(),
      "{} {}: {}",
       time::now().to_utc().rfc3339(), self.name(), msg);
  }

  fn err(&self, msg: String) {
    print!("{} {}: {}", time::now().to_utc().rfc3339(), self.name(), msg)
  }

  /// return a debug hook that targets all events
  /// and does nothing but echo the event name and payload
  pub fn echo() -> Hook {
     Hook {
       cmd: "echo \"$GH_EVENT_NAME\": \"$GH_EVENT_PAYLOAD\"".to_owned(),
       events: vec!["*".to_owned()],
       ..Default::default()
     }
  }

  /// return true if this hook targets the provided
  /// event name, false otherwise
  pub fn targets(&self, event: &String, payload: &Payload) -> bool {
    // todo: filter on payload.{ repo, org, sender }
    (self.events.contains(event)
     || self.events.contains(&"*".to_owned()))
  }

  /// return name if defined, otherwise cmd
  pub fn name(&self) -> String {
    self.name.clone().unwrap_or(self.cmd.clone())
  }

  /// start recv'ing events
  pub fn recv(&self, rx: Receiver<Event>) {
    loop {
      match rx.recv() {
        Ok(event) => {
          self.run(&event);
          ()
        },
        Err(e) => {
          self.err(format!("Recv err: {}\n", e.to_string()));
          break
        }
      }
    }
  }

  /// run hook cmd, returning true if cmd succeeded, otherwise false
  pub fn run(&self, event: &Event) -> bool {

     fn collect<T: Read + Send + 'static>(stm: Option<T>) ->
       Receiver<io::Result<Vec<String>>> {
       let (tx, rx) = channel();
       if let Some(s) = stm {
         thread::spawn(move || {
           let mut buf = BufReader::with_capacity(64, s);
           let mut lines: Vec<String> = Vec::new();
           loop {
             let mut line = String::new();
             match buf.read_line(&mut line) {
                Ok(0) | Err(_)  => break,
                Ok(_)  => lines.push(line)
             }
           }
           tx.send(Ok(lines)).unwrap()
         });
       } else {
         tx.send(Ok(vec![])).unwrap();
       }
       rx
     }

     match Command::new("/bin/sh")
       .arg("-c")
       .arg(&self.cmd)
       .env("GH_EVENT_NAME", &event.name)
       .env("GH_EVENT_PAYLOAD", &event.payload)
       .stdin(Stdio::null())
       .stdout(Stdio::piped())
       .stderr(Stdio::piped())
       .spawn() {
         Err(e) => {
           self.err(format!("error executing {}: {}\n", self.cmd, e));
           false
         },
         Ok(mut child)  => {

           let stdout = collect(child.stdout.take());
           let stderr = collect(child.stderr.take());
           let status = child.wait();

           match stdout.recv() {
             Ok(Ok(lines)) => for l in lines {
               self.info(l);
             },
             Ok(Err(e))    => self.err(format!("stdout io err {}\n", e)),
             Err(e)        => self.err(format!("stdout recv err {}\n", e))
           };

           match stderr.recv() {
             Ok(Ok(lines)) => for l in lines {
               self.info(l)
             },
             Ok(Err(e))    => self.err(format!("stderr io err {}\n", e)),
             Err(e)        => self.err(format!("stderr recv err {}\n", e))
           };

           match status {
             Ok(s) => {
               if s.success() {
                 self.info("that worked\n".to_owned());
                 true
               } else {
                 match s.code() {
                   Some(c) => self.info(format!("hook exited with status {}\n", c)),
                   _ => if let Some(s) = s.signal() {
                     self.err(format!("process killed by signal {}\n", s))
                   }
                 };
                 false
               }
             },
             Err(e) => {
               self.err(format!("error getting exit status {}\n", e.to_string()));
               false
             }
           }
         }
       }
  }
}

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

  #[test]
  fn test_hook_run_pass() {
    let hook = Hook {
      cmd: "true".to_owned(),
      ..Default::default()
    };
    assert!(hook.run(&Event {
      ..Default::default()
    }))
  }

  #[test]
  fn test_hook_run_fail() {
    let hook = Hook {
      cmd: "false".to_owned(),
      ..Default::default()
    };
    assert!(!hook.run(&Event {
      ..Default::default()
    }))
  }

  #[test]
  fn test_hook_run_panic() {
    let hook = Hook {
      cmd: "oiusdfasdf".to_owned(),
      ..Default::default()
    };
    assert!(!hook.run(&Event {
      ..Default::default()
    }))
  }
}<|endoftext|>


<commit_before>extern crate winapi;
extern crate kernel32;

use self::super::Size;

/// Gets the current terminal size
pub fn get() -> Option<Size> {
    //http://rosettacode.org/wiki/Terminal_control/Dimensions#Windows
    use self::winapi::HANDLE;
    use self::kernel32::{GetStdHandle, GetConsoleScreenBufferInfo};
    use self::winapi::{
        CONSOLE_SCREEN_BUFFER_INFO, COORD, SMALL_RECT,
        STD_OUTPUT_HANDLE, INVALID_HANDLE_VALUE
    };
    let stdout: HANDLE = unsafe {
        GetStdHandle(STD_OUTPUT_HANDLE)
    };
    println!("stdout {:?}", stdout);
    if stdout == INVALID_HANDLE_VALUE {
        println!("handle was invalid");
        return None
    }
    let zc = COORD { X: 0, Y: 0 };
    let mut info = CONSOLE_SCREEN_BUFFER_INFO {
        dwSize: zc.clone(),
        dwCursorPosition: zc.clone(),
        wAttributes: 0,
        srWindow: SMALL_RECT { Left:0, Top: 0, Right: 0, Bottom: 0 },
        dwMaximumWindowSize: zc
    };
    let success: bool = unsafe {
        let result = GetConsoleScreenBufferInfo(stdout, &mut info);
        println!("result was {}", result);
        result != 0
    };
    println!("info {:?}", info);
    if success {
        Some(
            Size {
                rows: (info.srWindow.Bottom - info.srWindow.Top + 1) as u16,
                cols: (info.srWindow.Right - info.srWindow.Left + 1) as u16
            }
        )
    } else {
        None
    }
}
<commit_msg>print last error when console info req fails in windows<commit_after>extern crate winapi;
extern crate kernel32;

use self::super::Size;

/// Gets the current terminal size
pub fn get() -> Option<Size> {
    //http://rosettacode.org/wiki/Terminal_control/Dimensions#Windows
    use self::winapi::HANDLE;
    use self::kernel32::{GetStdHandle, GetConsoleScreenBufferInfo, GetLastError};
    use self::winapi::{
        CONSOLE_SCREEN_BUFFER_INFO, COORD, SMALL_RECT,
        STD_OUTPUT_HANDLE, INVALID_HANDLE_VALUE
    };
    let stdout: HANDLE = unsafe {
        GetStdHandle(STD_OUTPUT_HANDLE)
    };
    println!("stdout {:?}", stdout);
    if stdout == INVALID_HANDLE_VALUE {
        println!("handle was invalid");
        return None
    }
    let zc = COORD { X: 0, Y: 0 };
    // screen buffer info is a container of console display information
    // https://msdn.microsoft.com/en-us/library/windows/desktop/ms682093(v=vs.85).aspx
    let mut info = CONSOLE_SCREEN_BUFFER_INFO {
        dwSize: zc.clone(),
        dwCursorPosition: zc.clone(),
        wAttributes: 0,
        srWindow: SMALL_RECT { Left:0, Top: 0, Right: 0, Bottom: 0 },
        dwMaximumWindowSize: zc
    };
    let success: bool = unsafe {
        // https://msdn.microsoft.com/en-us/library/windows/desktop/ms683171(v=vs.85).aspx
        let result = GetConsoleScreenBufferInfo(stdout, &mut info);
        println!("result was {}", result);
        if result == 0 {
            println!("last error was {:?}", GetLastError());
        }
        result != 0
    };
    println!("info {:?}", info);
    if success {
        Some(
            Size {
                rows: (info.srWindow.Bottom - info.srWindow.Top + 1) as u16,
                cols: (info.srWindow.Right - info.srWindow.Left + 1) as u16
            }
        )
    } else {
        None
    }
}
<|endoftext|>