<commit_before>use std::iter::Rev;
use core::slice::Items;
use std::collections::hash_map::{HashMap, Entries};
use std::collections::HashSet;

pub const NS_XMLNS_PREFIX: &'static str = "xmlns";
pub const NS_XMLNS_URI: &'static str    = "http://www.w3.org/2000/xmlns/";
pub const NS_XML_PREFIX: &'static str   = "xml";
pub const NS_XML_URI: &'static str      = "http://www.w3.org/XML/1998/namespace";
pub const NS_EMPTY_URI: &'static str    = "";

/// Denotes something which contains namespace URI mappings.
///
/// A URI mapping is a pair of type `(Option<&str>, &str)`, where the first item
/// is namespace prefix (`None` meaning default prefix) and the second item
/// is a URI mapped to the prefix.
pub trait NamespaceIterable<'a, I: Iterator<(Option<&'a str>, &'a str)>> {
    fn uri_mappings(&'a self) -> I;
}

/// Namespace is a map from prefixes to namespace URIs.
///
/// `None` prefix means no prefix (i.e. default namespace).
#[deriving(PartialEq, Clone)]
pub struct Namespace(pub HashMap<Option<String>, String>);

impl Namespace {
    /// Returns an empty namespace.
    #[inline]
    pub fn empty() -> Namespace { Namespace(HashMap::with_capacity(2)) }

    /// Checks whether this namespace is empty.
    #[inline]
    pub fn is_empty(&self) -> bool {
        self.0.is_empty()
    }

    /// Checks whether this namespace is essentially empty, that is, it does not contain
    /// anything but default mappings.
    pub fn is_essentially_empty(&self) -> bool {
        for (k, v) in self.0.iter() {
            match (k.as_ref().map(|k| k.as_slice()), v.as_slice()) {
                (None, u)    if u == NS_EMPTY_URI                         => {},
                (Some(p), u) if p == NS_XMLNS_PREFIX && u == NS_XMLNS_URI => {},
                (Some(p), u) if p == NS_XML_PREFIX   && u == NS_XML_URI   => {},
                _ => return false
            }
        }
        true
    }

    /// Puts a mapping into this namespace.
    ///
    /// This method does not override already existing mapping.
    ///
    /// Returns a boolean flag indicating whether the map already contained
    /// the given prefix.
    ///
    /// # Parameters
    /// * `prefix` --- namespace prefix (`None` means default namespace);
    /// * `uri`    --- namespace URI.
    ///
    /// # Return value
    /// `true` if `prefix` has been inserted successfully; `false` if the `prefix`
    /// was already present in the namespace.
    pub fn put(&mut self, prefix: Option<String>, uri: String) -> bool {
        self.0.insert(prefix, uri)
    }

    /// Queries the namespace for the given prefix.
    ///
    /// # Parameters
    /// * `prefix` --- namespace prefix (`None` means default namespace).
    ///
    /// # Return value
    /// Namespace URI corresponding to the given prefix, if it is present.
    pub fn get<'a>(&'a self, prefix: &Option<String>) -> Option<&'a str> {
        self.0.find(prefix).map(|s| s.as_slice())
    }
}

/// An iterator over mappings from prefixes to URIs in a namespace.
pub struct NamespaceMappings<'a> {
    entries: Entries<'a, Option<String>, String>
}

impl<'a> Iterator<(Option<&'a str>, &'a str)> for NamespaceMappings<'a> {
    fn next(&mut self) -> Option<(Option<&'a str>, &'a str)> {
        self.entries.next().map(|(prefix, uri)| {
            (prefix.as_ref().map(|p| p.as_slice()), uri.as_slice())
        })
    }
}

impl<'a> NamespaceIterable<'a, NamespaceMappings<'a>> for Namespace {
    fn uri_mappings(&'a self) -> NamespaceMappings<'a> {
        NamespaceMappings { entries: self.0.iter() }
    }
}

/// Namespace stack is a sequence of namespaces.
///
/// Namespace stack is used to represent cumulative namespace consisting of
/// combined namespaces from nested elements.
#[deriving(Clone, PartialEq)]
pub struct NamespaceStack(pub Vec<Namespace>);

impl NamespaceStack {
    /// Returns an empty namespace stack.
    #[inline]
    pub fn empty() -> NamespaceStack { NamespaceStack(Vec::with_capacity(2)) }

    /// Returns a namespace stack with default items in it.
    ///
    /// Default items are the following:
    ///
    /// * `xml` → `http://www.w3.org/XML/1998/namespace`;
    /// * `xmlns` → `http://www.w3.org/2000/xmlns/`.
    #[inline]
    pub fn default() -> NamespaceStack {
        let mut nst = NamespaceStack::empty();
        nst.push_empty();
        // xml namespace
        nst.put(Some(NS_XML_PREFIX.to_string()), NS_XML_URI.to_string());
        // xmlns namespace
        nst.put(Some(NS_XMLNS_PREFIX.to_string()), NS_XMLNS_URI.to_string());
        // empty namespace
        nst.put(None, NS_EMPTY_URI.to_string());
        nst
    }

    /// Adds an empty namespace to the top of this stack.
    #[inline]
    pub fn push_empty(&mut self) {
        self.0.push(Namespace::empty());
    }

    /// Removes a namespace at the top of the stack.
    ///
    /// Fails if the stack is empty.
    #[inline]
    pub fn pop(&mut self) -> Namespace {
        self.0.pop().unwrap()
    }

    /// Returns a namespace at the top of the stack, leaving the stack intact.
    ///
    /// Fails if the stack is empty.
    #[inline]
    pub fn peek<'a>(&'a mut self) -> &'a mut Namespace {
        self.0.last_mut().unwrap()
    }

    /// Puts a mapping into the topmost namespace in this stack.
    ///
    /// This method does not override a mapping in the topmost namespace if it is
    /// already present, however, it does not depend on other namespaces in the stack,
    /// so it is possible to put a mapping which is present in lower namespaces.
    ///
    /// Returns a boolean flag indicating whether the topmost namespace
    /// already contained the given prefix.
    ///
    /// # Parameters
    /// * `prefix` --- namespace prefix (`None` means default namespace);
    /// * `uri`    --- namespace URI.
    ///
    /// # Return value
    /// `true` if `prefix` has been inserted successfully; `false` if the `prefix`
    /// was already present in the namespace.
    #[inline]
    pub fn put(&mut self, prefix: Option<String>, uri: String) -> bool {
        self.0.last_mut().unwrap().put(prefix, uri)
    }

    /// Performs a search for the given prefix in the whole stack.
    ///
    /// This method walks the stack from top to bottom, querying each namespace
    /// in order for the given prefix. If none of the namespaces contains the prefix,
    /// `None` is returned.
    ///
    /// # Parameters
    /// * `prefix` --- namespace prefix (`None` means default namespace)
    #[inline]
    pub fn get<'a>(&'a self, prefix: &Option<String>) -> Option<&'a str> {
        for ns in self.0.iter().rev() {
            match ns.get(prefix) {
                None => {},
                r => return r,
            }
        }
        None
    }

    /// Combines this stack of namespaces into a single namespace.
    ///
    /// Namespaces are combined in left-to-right order, that is, rightmost namespace
    /// elements take priority over leftmost ones.
    pub fn squash(&self) -> Namespace {
        let mut result = HashMap::new();
        for ns in self.0.iter() {
            result.extend(ns.0.iter().map(|(k, v)| (k.clone(), v.to_string())));
        }
        Namespace(result)
    }
}

/// An iterator over mappings from prefixes to URIs in a namespace stack.
pub struct NamespaceStackMappings<'a> {
    namespaces: Rev<Items<'a, Namespace>>,
    current_namespace: Option<NamespaceMappings<'a>>,
    used_keys: HashSet<Option<&'a str>>
}

impl<'a> NamespaceStackMappings<'a> {
    fn to_next_namespace(&mut self) -> bool {
        self.current_namespace = self.namespaces.next().map(|ns| ns.uri_mappings());
        self.current_namespace.is_some()
    }
}

impl<'a> Iterator<(Option<&'a str>, &'a str)> for NamespaceStackMappings<'a> {
    fn next(&mut self) -> Option<(Option<&'a str>, &'a str)> {
        // If there is no current namespace and no next namespace, we're finished
        if self.current_namespace.is_none() && !self.to_next_namespace() {
            return None;
        }
        let next_item = self.current_namespace.as_mut().unwrap().next();

        match next_item {
            // There is an element in the current namespace
            Some((k, v)) => if self.used_keys.contains(&k) {
                // If the current key is used, go to the next one
                self.next()
            } else {
                // Otherwise insert the current key to the set of used keys and
                // return the mapping
                self.used_keys.insert(k);
                Some((k, v))
            },
            // Current namespace is exhausted
            None => if self.to_next_namespace() {
                // If there is next namespace, continue from it
                self.next()
            } else {
                // No next namespace, exiting
                None
            }
        }
    }
}

impl<'a> NamespaceIterable<'a, NamespaceStackMappings<'a>> for NamespaceStack {
    fn uri_mappings(&'a self) -> NamespaceStackMappings<'a> {
        NamespaceStackMappings {
            namespaces: self.0.iter().rev(),
            current_namespace: None,
            used_keys: HashSet::new()
        }
    }
}
<commit_msg>Update for rustc<commit_after>use std::iter::Rev;
use core::slice::Items;
use std::collections::hash_map::{HashMap, Entries};
use std::collections::HashSet;

pub const NS_XMLNS_PREFIX: &'static str = "xmlns";
pub const NS_XMLNS_URI: &'static str    = "http://www.w3.org/2000/xmlns/";
pub const NS_XML_PREFIX: &'static str   = "xml";
pub const NS_XML_URI: &'static str      = "http://www.w3.org/XML/1998/namespace";
pub const NS_EMPTY_URI: &'static str    = "";

/// Denotes something which contains namespace URI mappings.
///
/// A URI mapping is a pair of type `(Option<&str>, &str)`, where the first item
/// is namespace prefix (`None` meaning default prefix) and the second item
/// is a URI mapped to the prefix.
pub trait NamespaceIterable<'a, I: Iterator<(Option<&'a str>, &'a str)>> {
    fn uri_mappings(&'a self) -> I;
}

/// Namespace is a map from prefixes to namespace URIs.
///
/// `None` prefix means no prefix (i.e. default namespace).
#[deriving(PartialEq, Clone)]
pub struct Namespace(pub HashMap<Option<String>, String>);

impl Namespace {
    /// Returns an empty namespace.
    #[inline]
    pub fn empty() -> Namespace { Namespace(HashMap::with_capacity(2)) }

    /// Checks whether this namespace is empty.
    #[inline]
    pub fn is_empty(&self) -> bool {
        self.0.is_empty()
    }

    /// Checks whether this namespace is essentially empty, that is, it does not contain
    /// anything but default mappings.
    pub fn is_essentially_empty(&self) -> bool {
        for (k, v) in self.0.iter() {
            match (k.as_ref().map(|k| k.as_slice()), v.as_slice()) {
                (None, u)    if u == NS_EMPTY_URI                         => {},
                (Some(p), u) if p == NS_XMLNS_PREFIX && u == NS_XMLNS_URI => {},
                (Some(p), u) if p == NS_XML_PREFIX   && u == NS_XML_URI   => {},
                _ => return false
            }
        }
        true
    }

    /// Puts a mapping into this namespace.
    ///
    /// This method does not override already existing mapping.
    ///
    /// Returns a boolean flag indicating whether the map already contained
    /// the given prefix.
    ///
    /// # Parameters
    /// * `prefix` --- namespace prefix (`None` means default namespace);
    /// * `uri`    --- namespace URI.
    ///
    /// # Return value
    /// `true` if `prefix` has been inserted successfully; `false` if the `prefix`
    /// was already present in the namespace.
    pub fn put(&mut self, prefix: Option<String>, uri: String) -> bool {
        self.0.insert(prefix, uri).is_none()
    }

    /// Queries the namespace for the given prefix.
    ///
    /// # Parameters
    /// * `prefix` --- namespace prefix (`None` means default namespace).
    ///
    /// # Return value
    /// Namespace URI corresponding to the given prefix, if it is present.
    pub fn get<'a>(&'a self, prefix: &Option<String>) -> Option<&'a str> {
        self.0.find(prefix).map(|s| s.as_slice())
    }
}

/// An iterator over mappings from prefixes to URIs in a namespace.
pub struct NamespaceMappings<'a> {
    entries: Entries<'a, Option<String>, String>
}

impl<'a> Iterator<(Option<&'a str>, &'a str)> for NamespaceMappings<'a> {
    fn next(&mut self) -> Option<(Option<&'a str>, &'a str)> {
        self.entries.next().map(|(prefix, uri)| {
            (prefix.as_ref().map(|p| p.as_slice()), uri.as_slice())
        })
    }
}

impl<'a> NamespaceIterable<'a, NamespaceMappings<'a>> for Namespace {
    fn uri_mappings(&'a self) -> NamespaceMappings<'a> {
        NamespaceMappings { entries: self.0.iter() }
    }
}

/// Namespace stack is a sequence of namespaces.
///
/// Namespace stack is used to represent cumulative namespace consisting of
/// combined namespaces from nested elements.
#[deriving(Clone, PartialEq)]
pub struct NamespaceStack(pub Vec<Namespace>);

impl NamespaceStack {
    /// Returns an empty namespace stack.
    #[inline]
    pub fn empty() -> NamespaceStack { NamespaceStack(Vec::with_capacity(2)) }

    /// Returns a namespace stack with default items in it.
    ///
    /// Default items are the following:
    ///
    /// * `xml` → `http://www.w3.org/XML/1998/namespace`;
    /// * `xmlns` → `http://www.w3.org/2000/xmlns/`.
    #[inline]
    pub fn default() -> NamespaceStack {
        let mut nst = NamespaceStack::empty();
        nst.push_empty();
        // xml namespace
        nst.put(Some(NS_XML_PREFIX.to_string()), NS_XML_URI.to_string());
        // xmlns namespace
        nst.put(Some(NS_XMLNS_PREFIX.to_string()), NS_XMLNS_URI.to_string());
        // empty namespace
        nst.put(None, NS_EMPTY_URI.to_string());
        nst
    }

    /// Adds an empty namespace to the top of this stack.
    #[inline]
    pub fn push_empty(&mut self) {
        self.0.push(Namespace::empty());
    }

    /// Removes a namespace at the top of the stack.
    ///
    /// Fails if the stack is empty.
    #[inline]
    pub fn pop(&mut self) -> Namespace {
        self.0.pop().unwrap()
    }

    /// Returns a namespace at the top of the stack, leaving the stack intact.
    ///
    /// Fails if the stack is empty.
    #[inline]
    pub fn peek<'a>(&'a mut self) -> &'a mut Namespace {
        self.0.last_mut().unwrap()
    }

    /// Puts a mapping into the topmost namespace in this stack.
    ///
    /// This method does not override a mapping in the topmost namespace if it is
    /// already present, however, it does not depend on other namespaces in the stack,
    /// so it is possible to put a mapping which is present in lower namespaces.
    ///
    /// Returns a boolean flag indicating whether the topmost namespace
    /// already contained the given prefix.
    ///
    /// # Parameters
    /// * `prefix` --- namespace prefix (`None` means default namespace);
    /// * `uri`    --- namespace URI.
    ///
    /// # Return value
    /// `true` if `prefix` has been inserted successfully; `false` if the `prefix`
    /// was already present in the namespace.
    #[inline]
    pub fn put(&mut self, prefix: Option<String>, uri: String) -> bool {
        self.0.last_mut().unwrap().put(prefix, uri)
    }

    /// Performs a search for the given prefix in the whole stack.
    ///
    /// This method walks the stack from top to bottom, querying each namespace
    /// in order for the given prefix. If none of the namespaces contains the prefix,
    /// `None` is returned.
    ///
    /// # Parameters
    /// * `prefix` --- namespace prefix (`None` means default namespace)
    #[inline]
    pub fn get<'a>(&'a self, prefix: &Option<String>) -> Option<&'a str> {
        for ns in self.0.iter().rev() {
            match ns.get(prefix) {
                None => {},
                r => return r,
            }
        }
        None
    }

    /// Combines this stack of namespaces into a single namespace.
    ///
    /// Namespaces are combined in left-to-right order, that is, rightmost namespace
    /// elements take priority over leftmost ones.
    pub fn squash(&self) -> Namespace {
        let mut result = HashMap::new();
        for ns in self.0.iter() {
            result.extend(ns.0.iter().map(|(k, v)| (k.clone(), v.to_string())));
        }
        Namespace(result)
    }
}

/// An iterator over mappings from prefixes to URIs in a namespace stack.
pub struct NamespaceStackMappings<'a> {
    namespaces: Rev<Items<'a, Namespace>>,
    current_namespace: Option<NamespaceMappings<'a>>,
    used_keys: HashSet<Option<&'a str>>
}

impl<'a> NamespaceStackMappings<'a> {
    fn to_next_namespace(&mut self) -> bool {
        self.current_namespace = self.namespaces.next().map(|ns| ns.uri_mappings());
        self.current_namespace.is_some()
    }
}

impl<'a> Iterator<(Option<&'a str>, &'a str)> for NamespaceStackMappings<'a> {
    fn next(&mut self) -> Option<(Option<&'a str>, &'a str)> {
        // If there is no current namespace and no next namespace, we're finished
        if self.current_namespace.is_none() && !self.to_next_namespace() {
            return None;
        }
        let next_item = self.current_namespace.as_mut().unwrap().next();

        match next_item {
            // There is an element in the current namespace
            Some((k, v)) => if self.used_keys.contains(&k) {
                // If the current key is used, go to the next one
                self.next()
            } else {
                // Otherwise insert the current key to the set of used keys and
                // return the mapping
                self.used_keys.insert(k);
                Some((k, v))
            },
            // Current namespace is exhausted
            None => if self.to_next_namespace() {
                // If there is next namespace, continue from it
                self.next()
            } else {
                // No next namespace, exiting
                None
            }
        }
    }
}

impl<'a> NamespaceIterable<'a, NamespaceStackMappings<'a>> for NamespaceStack {
    fn uri_mappings(&'a self) -> NamespaceStackMappings<'a> {
        NamespaceStackMappings {
            namespaces: self.0.iter().rev(),
            current_namespace: None,
            used_keys: HashSet::new()
        }
    }
}
<|endoftext|>


<commit_before>pub mod ffi;
pub mod status;

pub use self::status::Status;

use std::result;
use std::mem;

use libc::c_long;

struct Wrapper<R> {
   pub raw: *mut R,
}

impl<R> Wrapper<R> {
   pub fn new(raw: *mut R) -> Self {
      Wrapper::<R> {
         raw: raw
      }
   }
}

pub type Connection = Wrapper<ffi::ngx_connection_t>;

impl Connection {
   pub fn log(&self) -> Option<Log> {
      let raw = unsafe { (*self.raw).log };

      if raw.is_null() {
         None
      } else {
         Some(
            Log::new(raw)
         )
      }
   }
}

pub type Log = Wrapper<ffi::ngx_log_t>;

pub type HttpRequest = Wrapper<ffi::ngx_http_request_t>;

impl HttpRequest {
   pub fn connection(&self) -> Option<Connection> {
      let raw = unsafe { (*self.raw).connection };

      if raw.is_null() {
         None
      } else {
         Some(
            Connection::new(raw)
         )
      }
   }

   pub fn headers_out(&self) -> HttpHeadersOut {
      let raw: *mut ffi::ngx_http_headers_out_t = unsafe { &mut (*self.raw).headers_out };

      HttpHeadersOut::new(raw)
   }

   pub fn http_send_header(&self) -> Status {
      let rc = unsafe { ffi::ngx_http_send_header(self.raw) };
      Status::new(rc)
   }

   pub fn pool(&self) -> Option<Pool> {
      let raw = unsafe { (*self.raw).pool };

      if raw.is_null() {
         None
      } else {
         Some(
            Pool::new(raw)
         )
      }
   }
}

pub type HttpHeadersOut = Wrapper<ffi::ngx_http_headers_out_t>;

impl HttpHeadersOut {
   pub fn set_status(&mut self, status: ffi::ngx_uint_t) {
      unsafe {
         (*self.raw).status = status;
      };
   }

   pub fn set_content_length_n(&mut self, content_length_n: ffi::off_t) {
      unsafe {
         (*self.raw).content_length_n = content_length_n;
      };
   }
}

pub type Pool = Wrapper<ffi::ngx_pool_t>;

impl Pool {
   #[inline]
   pub fn raw_palloc<T>(&self) -> *mut T {
      unsafe {
         ffi::ngx_palloc(self.raw, mem::size_of::<T>() as ffi::size_t) as *mut T
      }
   }

   #[inline]
   pub fn raw_pnalloc<T>(&self) -> *mut T {
      unsafe {
         ffi::ngx_pnalloc(self.raw, mem::size_of::<T>() as ffi::size_t) as *mut T
      }
   }

   #[inline]
   pub fn raw_pcalloc<T>(&self) -> *mut T {
      unsafe {
         ffi::ngx_pcalloc(self.raw, mem::size_of::<T>() as ffi::size_t) as *mut T
      }
   }

}
<commit_msg>Getter macro for defining functions for access of ffi struct members.<commit_after>pub mod ffi;
pub mod status;

pub use self::status::Status;

use std::result;
use std::mem;

use libc::c_long;

macro_rules! getter {
   ( $field:ident, $restype:ident ) => {
      pub fn $field(&self) -> Option<$restype> {
         let raw = unsafe { (*self.raw).$field };

         if raw.is_null() {
            None
         } else {
            Some(
               $restype::new(raw)
            )
         }
      }
   };
}

struct Wrapper<R> {
   pub raw: *mut R,
}

impl<R> Wrapper<R> {
   pub fn new(raw: *mut R) -> Self {
      Wrapper::<R> {
         raw: raw
      }
   }
}

pub type Connection = Wrapper<ffi::ngx_connection_t>;

impl Connection {
   getter!(log, Log);
}

pub type Log = Wrapper<ffi::ngx_log_t>;

pub type HttpRequest = Wrapper<ffi::ngx_http_request_t>;

impl HttpRequest {
   getter!(connection, Connection);
   getter!(pool, Pool);

   pub fn headers_out(&self) -> HttpHeadersOut {
      let raw: *mut ffi::ngx_http_headers_out_t = unsafe { &mut (*self.raw).headers_out };

      HttpHeadersOut::new(raw)
   }

   pub fn http_send_header(&self) -> Status {
      let rc = unsafe { ffi::ngx_http_send_header(self.raw) };
      Status::new(rc)
   }
}

pub type HttpHeadersOut = Wrapper<ffi::ngx_http_headers_out_t>;

impl HttpHeadersOut {
   pub fn set_status(&mut self, status: ffi::ngx_uint_t) {
      unsafe {
         (*self.raw).status = status;
      };
   }

   pub fn set_content_length_n(&mut self, content_length_n: ffi::off_t) {
      unsafe {
         (*self.raw).content_length_n = content_length_n;
      };
   }
}

pub type Pool = Wrapper<ffi::ngx_pool_t>;

impl Pool {
   #[inline]
   pub fn raw_palloc<T>(&self) -> *mut T {
      unsafe {
         ffi::ngx_palloc(self.raw, mem::size_of::<T>() as ffi::size_t) as *mut T
      }
   }

   #[inline]
   pub fn raw_pnalloc<T>(&self) -> *mut T {
      unsafe {
         ffi::ngx_pnalloc(self.raw, mem::size_of::<T>() as ffi::size_t) as *mut T
      }
   }

   #[inline]
   pub fn raw_pcalloc<T>(&self) -> *mut T {
      unsafe {
         ffi::ngx_pcalloc(self.raw, mem::size_of::<T>() as ffi::size_t) as *mut T
      }
   }

}
<|endoftext|>


<commit_before>// External modules:
use darwin_rs::{Individual, SimulationBuilder, Population, PopulationBuilder};
use image;
// use image::{GenericImage, FilterType, DynamicImage, imageops};
use image::{GenericImage, DynamicImage, Rgba};
use rand::Rng;
use rand;
use walkdir::{WalkDir};

// use std::path::Path;
use std::f64::consts::PI;
use std::cmp;
use std::sync::Arc;

// Internal modules:
use config::PanolutionConfig;

#[derive(Clone)]
pub struct ImageArrangement {
    pub image: Arc<DynamicImage>,
    pub samples: u32,
    pub x0: u32,
    pub y0: u32,
    pub x1: u32,
    pub y1: u32,
    pub angle: f64,
    // TODO: add more image operations
}

#[derive(Clone)]
pub struct Solution {
    pub arrangement: Vec<ImageArrangement>,
}

struct SamplePixel{
    x: u32,
    y: u32,
    pixels: Vec<(u8, u8, u8)>
}

fn valid_image_file(file_name: &str) -> bool {
    let extension = file_name.split(".").last().unwrap_or("").to_lowercase();

    let supported: Vec<String> = vec!["jpg", "jpeg", "gif", "png", "tif", "tiff"].iter().map(|s| s.to_string()).collect();

    supported.contains(&extension)
}

fn calc_canvas_size(arrangement: &Vec<ImageArrangement>) -> (u32, u32, u32, u32) {
    arrangement.iter().fold((u32::max_value(), u32::max_value(), 0, 0), |(cx0, cy0, cx1, cy1), im_ar| {
        (cmp::min(cx0, im_ar.x0), cmp::min(cy0, im_ar.y0), cmp::max(cx1, im_ar.x1), cmp::max(cy1, im_ar.y1))
    })
}

fn get_pixel(cx: f64, cy: f64, im_ar: &ImageArrangement) -> Option<(u8, u8, u8)> {
    // Center of image
    let mx = ((im_ar.x0 + im_ar.x1) / 2) as f64;
    let my = ((im_ar.y0 + im_ar.y1) / 2) as f64;

    // Move center of rotation to origin (0,0)
    let ox = cx - mx;
    let oy = cy - my;

    // Rotate canvas point in reverse of image orientation (clockwise)
    let cos_a = im_ar.angle.cos();
    let sin_a = im_ar.angle.sin();

    let rx = (ox * cos_a) + (oy * sin_a);
    let ry = (-ox * sin_a) + (oy * cos_a);

    // Move back to image coordinate system
    let imx = ((rx + mx) as u32) - im_ar.x0;
    let imy = ((ry + my) as u32) - im_ar.y0;

    // Check if point is inside the image:

    if (imx >= 0) && (imx < im_ar.image.width()) && (imy >= 0) && (imy < im_ar.image.height()) {
        let pixel = im_ar.image.get_pixel(imx, imy) as Rgba<u8>;
        return Some((pixel.data[0], pixel.data[1], pixel.data[2]));
    }

    None
}

fn make_all_populations(num_of_individuals: u32, num_of_populations: u32, initial_solution: &Solution) -> Vec<Population<Solution>> {
    let mut result = Vec::new();

    let mut initial_population = Vec::new();

    for _ in 0..num_of_individuals {
        initial_population.push(initial_solution.clone());
    }

    for i in 0..num_of_populations {
        let pop = PopulationBuilder::<Solution>::new()
            .set_id(i + 1)
            .initial_population(&initial_population)
            .reset_limit_end(0)
            .increasing_mutation_rate()
            .finalize().unwrap();

        result.push(pop);
    }

    result
}

impl Individual for Solution {
    fn mutate(&mut self) {
        let mut rng = rand::thread_rng();
        let num_of_operations: u8 = 4;

        let index1: usize = rng.gen_range(0, self.arrangement.len());

        // Probably use this create and enums for operation:
        // https://github.com/andersk/enum_primitive-rs
        let operation: u8 = rng.gen_range(0, num_of_operations);

        match operation {
            0 => {
                // Change image order
                let mut index2: usize = rng.gen_range(0, self.arrangement.len());
                while index1 == index2 {
                    index2 = rng.gen_range(0, self.arrangement.len());
                }

                let px0 = self.arrangement[index1].x0;
                let py0 = self.arrangement[index1].y0;
                let px1 = self.arrangement[index1].x1;
                let py1 = self.arrangement[index1].y1;

                self.arrangement[index1].x0 = self.arrangement[index2].x0;
                self.arrangement[index1].y0 = self.arrangement[index2].y0;
                self.arrangement[index1].x1 = self.arrangement[index2].x1;
                self.arrangement[index1].y1 = self.arrangement[index2].y1;

                self.arrangement[index2].x0 = px0;
                self.arrangement[index2].y0 = py0;
                self.arrangement[index2].x1 = px1;
                self.arrangement[index2].y1 = py1;
            },
            1 => {
                // Move x
                let dx = rng.gen_range::<i32>(0, 500) - 250;
                let new_x = (self.arrangement[index1].x0 as i32) + dx;

                if new_x >= 0 {
                    self.arrangement[index1].x0 = new_x as u32;
                    self.arrangement[index1].x1 = ((self.arrangement[index1].x1 as i32) + dx) as u32;
                }
            },
            2 => {
                // Move y
                let dy = rng.gen_range::<i32>(0, 500) - 250;
                let new_y = (self.arrangement[index1].y0 as i32) + dy;

                if new_y >= 0 {
                    self.arrangement[index1].y0 = new_y as u32;
                    self.arrangement[index1].y1 = ((self.arrangement[index1].y1 as i32) + dy) as u32;
                }
            },
            3 => {
                // Rotate
                self.arrangement[index1].angle = self.arrangement[index1].angle + (rng.next_f64() * 0.1);
                if self.arrangement[index1].angle < 0.0 {
                    self.arrangement[index1].angle = 0.0;
                } else if self.arrangement[index1].angle > 2.0 * PI {
                    self.arrangement[index1].angle = 0.0;
                }
            },
            op => {
                warn!("mutate(): unknown operation: {}", op)
            }
        }
    }

    fn calculate_fitness(&mut self) -> f64 {
        let num_of_samples = self.arrangement[0].samples;

        let (cx0, cy0, cx1, cy1) = calc_canvas_size(&self.arrangement);

        // Create sample points on canvas:
        let mut rng = rand::thread_rng();
        let mut sample_pixels = Vec::new();

        for _ in 0..num_of_samples {
            sample_pixels.push(SamplePixel{
                x: rng.gen_range::<u32>(cx0, cx1),
                y: rng.gen_range::<u32>(cy0, cy1),
                pixels: Vec::new()
            });
        }

        for im_ar in &self.arrangement {
            for sp in &mut sample_pixels {
                if let Some(pixel) = get_pixel(sp.x as f64, sp.y as f64, &im_ar) {
                    sp.pixels.push(pixel);
                }
            }
        }

        let error_sum = sample_pixels.iter().fold(0, |sum, sp| {
            if sp.pixels.len() > 0 {
                let r0 = sp.pixels[0].0;
                let g0 = sp.pixels[0].1;
                let b0 = sp.pixels[0].2;
                let max_diff = sp.pixels.iter().fold((0_u32, r0, g0, b0), |(diff, r1, g1, b1), &(r2, g2, b2)| {
                    let rdiff = if r1 > r2 {r1 - r2} else {r2 - r1};
                    let gdiff  = if g1 > g2 {g1 - g2} else {g2 - g1};
                    let bdiff  = if b1 > b2 {b1 - b2} else {b2 - b1};

                    (cmp::max(diff, (rdiff as u32) + (gdiff as u32) + (bdiff as u32)), r2, g2, b2)
                });
                sum + max_diff.0
            } else {
                sum
            }

        });

        (error_sum as f64) / (num_of_samples as f64)
    }

    fn reset(&mut self) {
        for arrangement in &mut self.arrangement {
            // Reset all image operation to original image
            arrangement.x1 = arrangement.x1 - arrangement.x0;
            arrangement.y1 = arrangement.y1 - arrangement.y0;
            arrangement.x0 = 0;
            arrangement.y0 = 0;
            arrangement.angle = 0.0;
        }
    }
}

fn run_darwin(solution: &Solution, config: &PanolutionConfig) -> Solution {
    let pano = SimulationBuilder::<Solution>::new()
        .iterations(config.max_iteration)
        .threads(4)
        .add_multiple_populations(make_all_populations(20, 8, &solution))
        .finalize();

    match pano {
        Err(e) => {
            error!("An error occured");

            for e in e.iter().skip(1) {
                error!("Caused by '{}'", e)
            }

            solution.clone()
        },
        Ok(mut pano_simulation) => {
            pano_simulation.run();

            info!("Total run time: {} ms", pano_simulation.total_time_in_ms);
            info!("Improvement factor: {}", pano_simulation.simulation_result.improvement_factor);
            info!("Number of iterations: {}", pano_simulation.simulation_result.iteration_counter);
            info!("New fitness: {}", pano_simulation.simulation_result.fittest[0].fitness);

            pano_simulation.simulation_result.fittest[0].individual.clone()
        }
    }
}

pub fn optimize(solution: Option<&Solution>, config: &PanolutionConfig, sample_index: usize) -> Solution {
    match solution {
        None => {
            let mut arrangement = Vec::new();

            for entry in WalkDir::new(&config.input_path) {
                if let Ok(entry) = entry {
                    if entry.file_type().is_file() {
                        if let Some(file_name) = entry.file_name().to_str() {
                            if valid_image_file(file_name) {
                                if let Some(full_path) = entry.path().to_str() {

                                    if let Ok(img) = image::open(&full_path) {
                                        arrangement.push(ImageArrangement{
                                            image: Arc::new(img.clone()),
                                            samples: config.num_of_samples[sample_index],
                                            x0: 0,
                                            y0: 0,
                                            x1: img.width(),
                                            y1: img.height(),
                                            angle: 0.0,
                                        });
                                    } else {
                                        error!("Could not open image: {}", full_path)
                                    }
                                } else {
                                    error!("Could not convert full path to str: {:?}", entry);
                                }
                            } else {
                                error!("Image format currently not supported: {}", file_name);
                            }
                        } else {
                            error!("Could not convert file name to str: {:?}", entry);
                        }
                    } else {
                        info!("Ignore non-file: {:?}", entry);
                    }
                } else {
                    error!("Error in WalkDir");
                }
            }

            let solution = Solution{arrangement: arrangement};

            if solution.arrangement.len() > 0 {
                run_darwin(&solution, &config)
            } else {
                // An error occured
                // TODO: return Result<Soultion> and use chain_err
                solution
            }
        },
        Some(solution) => {
            let mut updated_arrangement = solution.arrangement.clone();

            for im_ar in &mut updated_arrangement {
                im_ar.samples = config.num_of_samples[sample_index];
            }

            run_darwin(&Solution{arrangement: updated_arrangement}, &config)
        }
    }
}
<commit_msg>make type intent more clear<commit_after>// External modules:
use darwin_rs::{Individual, SimulationBuilder, Population, PopulationBuilder};
use image;
// use image::{GenericImage, FilterType, DynamicImage, imageops};
use image::{GenericImage, DynamicImage, Rgba};
use rand::Rng;
use rand;
use walkdir::{WalkDir};

// use std::path::Path;
use std::f64::consts::PI;
use std::cmp;
use std::sync::Arc;

// Internal modules:
use config::PanolutionConfig;

#[derive(Clone)]
pub struct ImageArrangement {
    pub image: Arc<DynamicImage>,
    pub samples: u32,
    pub x0: u32,
    pub y0: u32,
    pub x1: u32,
    pub y1: u32,
    pub angle: f64,
    // TODO: add more image operations
}

#[derive(Clone)]
pub struct Solution {
    pub arrangement: Vec<ImageArrangement>,
}

struct SamplePixel{
    x: u32,
    y: u32,
    pixels: Vec<(u8, u8, u8)>
}

fn valid_image_file(file_name: &str) -> bool {
    let extension = file_name.split(".").last().unwrap_or("").to_lowercase();

    let supported: Vec<String> = vec!["jpg", "jpeg", "gif", "png", "tif", "tiff"].iter().map(|s| s.to_string()).collect();

    supported.contains(&extension)
}

fn calc_canvas_size(arrangement: &Vec<ImageArrangement>) -> (u32, u32, u32, u32) {
    arrangement.iter().fold((u32::max_value(), u32::max_value(), 0, 0), |(cx0, cy0, cx1, cy1), im_ar| {
        (cmp::min(cx0, im_ar.x0), cmp::min(cy0, im_ar.y0), cmp::max(cx1, im_ar.x1), cmp::max(cy1, im_ar.y1))
    })
}

fn get_pixel(cx: f64, cy: f64, im_ar: &ImageArrangement) -> Option<(u8, u8, u8)> {
    // Center of image
    let mx = ((im_ar.x0 + im_ar.x1) / 2) as f64;
    let my = ((im_ar.y0 + im_ar.y1) / 2) as f64;

    // Move center of rotation to origin (0,0)
    let ox = cx - mx;
    let oy = cy - my;

    // Rotate canvas point in reverse of image orientation (clockwise)
    let cos_a = im_ar.angle.cos();
    let sin_a = im_ar.angle.sin();

    let rx = (ox * cos_a) + (oy * sin_a);
    let ry = (-ox * sin_a) + (oy * cos_a);

    // Move back to image coordinate system
    let imx = ((rx + mx) as u32) - im_ar.x0;
    let imy = ((ry + my) as u32) - im_ar.y0;

    // Check if point is inside the image:

    if (imx >= 0) && (imx < im_ar.image.width()) && (imy >= 0) && (imy < im_ar.image.height()) {
        let pixel = im_ar.image.get_pixel(imx, imy) as Rgba<u8>;
        return Some((pixel.data[0], pixel.data[1], pixel.data[2]));
    }

    None
}

fn make_all_populations(num_of_individuals: u32, num_of_populations: u32, initial_solution: &Solution) -> Vec<Population<Solution>> {
    let mut result = Vec::new();

    let mut initial_population = Vec::new();

    for _ in 0..num_of_individuals {
        initial_population.push(initial_solution.clone());
    }

    for i in 0..num_of_populations {
        let pop = PopulationBuilder::<Solution>::new()
            .set_id(i + 1)
            .initial_population(&initial_population)
            .reset_limit_end(0)
            .increasing_mutation_rate()
            .finalize().unwrap();

        result.push(pop);
    }

    result
}

impl Individual for Solution {
    fn mutate(&mut self) {
        let mut rng = rand::thread_rng();
        let num_of_operations: u8 = 4;

        let index1: usize = rng.gen_range(0, self.arrangement.len());

        // Probably use this create and enums for operation:
        // https://github.com/andersk/enum_primitive-rs
        let operation: u8 = rng.gen_range(0, num_of_operations);

        match operation {
            0 => {
                // Change image order
                let mut index2: usize = rng.gen_range(0, self.arrangement.len());
                while index1 == index2 {
                    index2 = rng.gen_range(0, self.arrangement.len());
                }

                let px0 = self.arrangement[index1].x0;
                let py0 = self.arrangement[index1].y0;
                let px1 = self.arrangement[index1].x1;
                let py1 = self.arrangement[index1].y1;

                self.arrangement[index1].x0 = self.arrangement[index2].x0;
                self.arrangement[index1].y0 = self.arrangement[index2].y0;
                self.arrangement[index1].x1 = self.arrangement[index2].x1;
                self.arrangement[index1].y1 = self.arrangement[index2].y1;

                self.arrangement[index2].x0 = px0;
                self.arrangement[index2].y0 = py0;
                self.arrangement[index2].x1 = px1;
                self.arrangement[index2].y1 = py1;
            },
            1 => {
                // Move x
                let dx = rng.gen_range::<i32>(0, 500) - 250;
                let new_x = (self.arrangement[index1].x0 as i32) + dx;

                if new_x >= 0 {
                    self.arrangement[index1].x0 = new_x as u32;
                    self.arrangement[index1].x1 = ((self.arrangement[index1].x1 as i32) + dx) as u32;
                }
            },
            2 => {
                // Move y
                let dy = rng.gen_range::<i32>(0, 500) - 250;
                let new_y = (self.arrangement[index1].y0 as i32) + dy;

                if new_y >= 0 {
                    self.arrangement[index1].y0 = new_y as u32;
                    self.arrangement[index1].y1 = ((self.arrangement[index1].y1 as i32) + dy) as u32;
                }
            },
            3 => {
                // Rotate
                self.arrangement[index1].angle = self.arrangement[index1].angle + (rng.next_f64() * 0.1);
                if self.arrangement[index1].angle < 0.0 {
                    self.arrangement[index1].angle = 0.0;
                } else if self.arrangement[index1].angle > 2.0 * PI {
                    self.arrangement[index1].angle = 0.0;
                }
            },
            op => {
                warn!("mutate(): unknown operation: {}", op)
            }
        }
    }

    fn calculate_fitness(&mut self) -> f64 {
        let num_of_samples = self.arrangement[0].samples;

        let (cx0, cy0, cx1, cy1) = calc_canvas_size(&self.arrangement);

        // Create sample points on canvas:
        let mut rng = rand::thread_rng();
        let mut sample_pixels = Vec::new();

        for _ in 0..num_of_samples {
            sample_pixels.push(SamplePixel{
                x: rng.gen_range::<u32>(cx0, cx1),
                y: rng.gen_range::<u32>(cy0, cy1),
                pixels: Vec::new()
            });
        }

        for im_ar in &self.arrangement {
            for sp in &mut sample_pixels {
                if let Some(pixel) = get_pixel(sp.x as f64, sp.y as f64, &im_ar) {
                    sp.pixels.push(pixel);
                }
            }
        }

        let error_sum = sample_pixels.iter().fold(0_u32, |sum, sp| {
            if sp.pixels.len() > 0 {
                let r0 = sp.pixels[0].0;
                let g0 = sp.pixels[0].1;
                let b0 = sp.pixels[0].2;
                let max_diff = sp.pixels.iter().fold((0_u32, r0, g0, b0), |(diff, r1, g1, b1), &(r2, g2, b2)| {
                    let rdiff = if r1 > r2 {r1 - r2} else {r2 - r1};
                    let gdiff  = if g1 > g2 {g1 - g2} else {g2 - g1};
                    let bdiff  = if b1 > b2 {b1 - b2} else {b2 - b1};

                    (cmp::max(diff, (rdiff as u32) + (gdiff as u32) + (bdiff as u32)), r2, g2, b2)
                });
                sum + max_diff.0
            } else {
                sum
            }

        });

        (error_sum as f64) / (num_of_samples as f64)
    }

    fn reset(&mut self) {
        for arrangement in &mut self.arrangement {
            // Reset all image operation to original image
            arrangement.x1 = arrangement.x1 - arrangement.x0;
            arrangement.y1 = arrangement.y1 - arrangement.y0;
            arrangement.x0 = 0;
            arrangement.y0 = 0;
            arrangement.angle = 0.0;
        }
    }
}

fn run_darwin(solution: &Solution, config: &PanolutionConfig) -> Solution {
    let pano = SimulationBuilder::<Solution>::new()
        .iterations(config.max_iteration)
        .threads(4)
        .add_multiple_populations(make_all_populations(20, 8, &solution))
        .finalize();

    match pano {
        Err(e) => {
            error!("An error occured");

            for e in e.iter().skip(1) {
                error!("Caused by '{}'", e)
            }

            solution.clone()
        },
        Ok(mut pano_simulation) => {
            pano_simulation.run();

            info!("Total run time: {} ms", pano_simulation.total_time_in_ms);
            info!("Improvement factor: {}", pano_simulation.simulation_result.improvement_factor);
            info!("Number of iterations: {}", pano_simulation.simulation_result.iteration_counter);
            info!("New fitness: {}", pano_simulation.simulation_result.fittest[0].fitness);

            pano_simulation.simulation_result.fittest[0].individual.clone()
        }
    }
}

pub fn optimize(solution: Option<&Solution>, config: &PanolutionConfig, sample_index: usize) -> Solution {
    match solution {
        None => {
            let mut arrangement = Vec::new();

            for entry in WalkDir::new(&config.input_path) {
                if let Ok(entry) = entry {
                    if entry.file_type().is_file() {
                        if let Some(file_name) = entry.file_name().to_str() {
                            if valid_image_file(file_name) {
                                if let Some(full_path) = entry.path().to_str() {

                                    if let Ok(img) = image::open(&full_path) {
                                        arrangement.push(ImageArrangement{
                                            image: Arc::new(img.clone()),
                                            samples: config.num_of_samples[sample_index],
                                            x0: 0,
                                            y0: 0,
                                            x1: img.width(),
                                            y1: img.height(),
                                            angle: 0.0,
                                        });
                                    } else {
                                        error!("Could not open image: {}", full_path)
                                    }
                                } else {
                                    error!("Could not convert full path to str: {:?}", entry);
                                }
                            } else {
                                error!("Image format currently not supported: {}", file_name);
                            }
                        } else {
                            error!("Could not convert file name to str: {:?}", entry);
                        }
                    } else {
                        info!("Ignore non-file: {:?}", entry);
                    }
                } else {
                    error!("Error in WalkDir");
                }
            }

            let solution = Solution{arrangement: arrangement};

            if solution.arrangement.len() > 0 {
                run_darwin(&solution, &config)
            } else {
                // An error occured
                // TODO: return Result<Soultion> and use chain_err
                solution
            }
        },
        Some(solution) => {
            let mut updated_arrangement = solution.arrangement.clone();

            for im_ar in &mut updated_arrangement {
                im_ar.samples = config.num_of_samples[sample_index];
            }

            run_darwin(&Solution{arrangement: updated_arrangement}, &config)
        }
    }
}
<|endoftext|>


<commit_before>#![crate_name = "rust-hl-lua-modules"]
#![feature(plugin_registrar)]
#![feature(quote)]

extern crate rustc;
extern crate syntax;

use std::gc::{GC, Gc};
use syntax::parse::token;
use syntax::ast;
use syntax::attr::AttrMetaMethods;
use syntax::ext::build::AstBuilder;
use syntax::ext::base;
use syntax::ext::quote::rt::ToSource;
use syntax::codemap;

#[plugin_registrar]
#[doc(hidden)]
pub fn plugin_registrar(reg: &mut ::rustc::plugin::Registry) {
    reg.register_syntax_extension(token::intern("export_lua_module"),
        base::ItemModifier(expand_lua_module));
}

// handler for export_lua_module
pub fn expand_lua_module(ecx: &mut base::ExtCtxt, span: codemap::Span,
                        _: Gc<ast::MetaItem>, input_item: Gc<ast::Item>)
    -> Gc<ast::Item>
{
    let ecx: &base::ExtCtxt = &*ecx;

    // checking that the input item is a module
    let module = match input_item.node {
        ast::ItemMod(ref module) => module,
        _ => {
            ecx.span_err(input_item.span,
                "`export_lua_module` extension is only allowed on modules");
            return input_item
        }
    };

    // creating the new item that will be returned by the function
    // it is just a clone of the input with more elements added to it
    let mut new_item = input_item.deref().clone();
    new_item.vis = ast::Public;
    if input_item.vis != ast::Public {
        ecx.span_warn(input_item.span,
            "`export_lua_module` will turn the module into a public module");
    }

    // creating an array of the statements to add to the main Lua entry point
    let module_handler_body: Vec<Gc<ast::Stmt>> = {
        let mut module_handler_body = Vec::new();

        // iterating over elements inside the module
        for moditem in module.items.iter() {
            let moditem_name = moditem.ident.to_source();

            match moditem.node {
                ast::ItemFn(..) | ast::ItemStatic(..) => {
                    let moditem_name_slice = moditem_name.as_slice();
                    let moditem_name_item = ecx.ident_of(moditem_name_slice);
                    module_handler_body.push(
                        quote_stmt!(&*ecx,
                            table.set($moditem_name_slice.to_string(), $moditem_name_item);
                        )
                    )
                },

                _ => {
                    ecx.span_warn(moditem.span,
                        format!("item `{}` is neiter a function nor a static
                            and will thus be ignored by `export_lua_module`",
                            moditem_name).as_slice());
                    continue
                }
            };

            // handling lua_module_init
            if moditem.attrs.iter().find(|at| at.check_name("lua_module_init")).is_some() {
                let moditem_name_item = ecx.ident_of(moditem.ident.to_source().as_slice());
                module_handler_body.push(
                    quote_stmt!(ecx, $moditem_name_item())
                );
            }
        }

        module_handler_body
    };

    // adding extern crate declarations
    {
        let view_items = quote_item!(ecx,
            mod x {
                extern crate lua = "rust-hl-lua";
                extern crate libc;
            }
        );

        let view_items = match view_items {
            Some(a) => a,
            None => {
                ecx.span_err(input_item.span,
                    "internal error in the library (could not parse view items)");
                return input_item;
            }
        };

        // getting all the items
        let view_items = match view_items.node {
            ast::ItemMod(ref m) => m,
            _ => { ecx.span_err(span, "internal error in the library"); return input_item; }
        };

        let ref mut mut_new_item = match &mut new_item.node {
            &ast::ItemMod(ref mut m) => m,
            _ => { ecx.span_err(span, "internal error in the library"); return input_item; }
        };

        for i in view_items.view_items.iter() {
            mut_new_item.view_items.unshift(i.clone())
        }
    }

    // generating the function that we will add inside the module
    {
        let function_body = {
            let mut function_body = Vec::new();

            function_body.push(quote_stmt!(ecx,
                let mut lua = self::lua::Lua::from_existing_state(lua, false);
            ));

            function_body.push(quote_stmt!(ecx,
                let mut table = lua.load_new_table();
            ));

            function_body.push_all_move(module_handler_body);

            function_body.push(quote_stmt!(ecx,
                ::std::mem::forget(table);
            ));

            ecx.block(span.clone(), function_body, Some(
                quote_expr!(ecx, 1)
            ))
        };

        // identifier for "luaopen_mylib"
        let luaopen_id = ecx.ident_of(format!("luaopen_{}", input_item.ident.to_source())
            .as_slice());

        // building the function itself
        let function = quote_item!(ecx,
            #[no_mangle]
            pub extern "C" fn $luaopen_id(lua: *mut self::libc::c_void)
                                                    -> self::libc::c_int {
                unsafe {
                    $function_body
               }
            }
        );

        let function = match function {
            Some(f) => f,
            None => {
                ecx.span_err(input_item.span,
                    "internal error in the library (could not parse function body)");
                return input_item;
            }
        };

        // adding the function to the module
        match &mut new_item.node {
            &ast::ItemMod(ref mut m) => {
                m.items.push(function)
            },
            _ => { ecx.span_err(span, "internal error in the library"); return input_item; }
        };
    }

    // returning
    box(GC) new_item
}
<commit_msg>Fix deprecated function call<commit_after>#![crate_name = "rust-hl-lua-modules"]
#![feature(plugin_registrar)]
#![feature(quote)]

extern crate rustc;
extern crate syntax;

use std::gc::{GC, Gc};
use syntax::parse::token;
use syntax::ast;
use syntax::attr::AttrMetaMethods;
use syntax::ext::build::AstBuilder;
use syntax::ext::base;
use syntax::ext::quote::rt::ToSource;
use syntax::codemap;

#[plugin_registrar]
#[doc(hidden)]
pub fn plugin_registrar(reg: &mut ::rustc::plugin::Registry) {
    reg.register_syntax_extension(token::intern("export_lua_module"),
        base::ItemModifier(expand_lua_module));
}

// handler for export_lua_module
pub fn expand_lua_module(ecx: &mut base::ExtCtxt, span: codemap::Span,
                        _: Gc<ast::MetaItem>, input_item: Gc<ast::Item>)
    -> Gc<ast::Item>
{
    let ecx: &base::ExtCtxt = &*ecx;

    // checking that the input item is a module
    let module = match input_item.node {
        ast::ItemMod(ref module) => module,
        _ => {
            ecx.span_err(input_item.span,
                "`export_lua_module` extension is only allowed on modules");
            return input_item
        }
    };

    // creating the new item that will be returned by the function
    // it is just a clone of the input with more elements added to it
    let mut new_item = input_item.deref().clone();
    new_item.vis = ast::Public;
    if input_item.vis != ast::Public {
        ecx.span_warn(input_item.span,
            "`export_lua_module` will turn the module into a public module");
    }

    // creating an array of the statements to add to the main Lua entry point
    let module_handler_body: Vec<Gc<ast::Stmt>> = {
        let mut module_handler_body = Vec::new();

        // iterating over elements inside the module
        for moditem in module.items.iter() {
            let moditem_name = moditem.ident.to_source();

            match moditem.node {
                ast::ItemFn(..) | ast::ItemStatic(..) => {
                    let moditem_name_slice = moditem_name.as_slice();
                    let moditem_name_item = ecx.ident_of(moditem_name_slice);
                    module_handler_body.push(
                        quote_stmt!(&*ecx,
                            table.set($moditem_name_slice.to_string(), $moditem_name_item);
                        )
                    )
                },

                _ => {
                    ecx.span_warn(moditem.span,
                        format!("item `{}` is neiter a function nor a static
                            and will thus be ignored by `export_lua_module`",
                            moditem_name).as_slice());
                    continue
                }
            };

            // handling lua_module_init
            if moditem.attrs.iter().find(|at| at.check_name("lua_module_init")).is_some() {
                let moditem_name_item = ecx.ident_of(moditem.ident.to_source().as_slice());
                module_handler_body.push(
                    quote_stmt!(ecx, $moditem_name_item())
                );
            }
        }

        module_handler_body
    };

    // adding extern crate declarations
    {
        let view_items = quote_item!(ecx,
            mod x {
                extern crate lua = "rust-hl-lua";
                extern crate libc;
            }
        );

        let view_items = match view_items {
            Some(a) => a,
            None => {
                ecx.span_err(input_item.span,
                    "internal error in the library (could not parse view items)");
                return input_item;
            }
        };

        // getting all the items
        let view_items = match view_items.node {
            ast::ItemMod(ref m) => m,
            _ => { ecx.span_err(span, "internal error in the library"); return input_item; }
        };

        let ref mut mut_new_item = match &mut new_item.node {
            &ast::ItemMod(ref mut m) => m,
            _ => { ecx.span_err(span, "internal error in the library"); return input_item; }
        };

        for i in view_items.view_items.iter() {
            mut_new_item.view_items.insert(0, i.clone())
        }
    }

    // generating the function that we will add inside the module
    {
        let function_body = {
            let mut function_body = Vec::new();

            function_body.push(quote_stmt!(ecx,
                let mut lua = self::lua::Lua::from_existing_state(lua, false);
            ));

            function_body.push(quote_stmt!(ecx,
                let mut table = lua.load_new_table();
            ));

            function_body.push_all_move(module_handler_body);

            function_body.push(quote_stmt!(ecx,
                ::std::mem::forget(table);
            ));

            ecx.block(span.clone(), function_body, Some(
                quote_expr!(ecx, 1)
            ))
        };

        // identifier for "luaopen_mylib"
        let luaopen_id = ecx.ident_of(format!("luaopen_{}", input_item.ident.to_source())
            .as_slice());

        // building the function itself
        let function = quote_item!(ecx,
            #[no_mangle]
            pub extern "C" fn $luaopen_id(lua: *mut self::libc::c_void)
                                                    -> self::libc::c_int {
                unsafe {
                    $function_body
               }
            }
        );

        let function = match function {
            Some(f) => f,
            None => {
                ecx.span_err(input_item.span,
                    "internal error in the library (could not parse function body)");
                return input_item;
            }
        };

        // adding the function to the module
        match &mut new_item.node {
            &ast::ItemMod(ref mut m) => {
                m.items.push(function)
            },
            _ => { ecx.span_err(span, "internal error in the library"); return input_item; }
        };
    }

    // returning
    box(GC) new_item
}
<|endoftext|>


<commit_before>use std::iter::FromIterator;

use num::{Zero, One};

use number::Number;
use error::{RuntimeError, RuntimeErrorKind};
use runtime::{DatumCast, RDatum, DatumType};

pub trait PrimFunc {
    fn call(&self, &[RDatum]) -> Result<RDatum, RuntimeError>;
}

pub struct Fold<P> {
    fold: fn(&[P]) -> P
}

#[derive(Copy)]
pub struct FoldDatum {
    fold: fn(&[RDatum]) -> RDatum
}

pub struct Fold1<P> {
    fold1: fn(&P, &[P]) -> P
}

pub struct Fold1Err<P> {
    fold1: fn(&P, &[P]) -> Result<P, RuntimeError>
}

pub struct F1<T0, R> {
    f1: fn(T0) -> R
}

pub struct R1<R> {
    f1: fn(&RDatum) -> R
}

impl<T> PrimFunc for Fold<T> where T: DatumCast {
    fn call(&self, args: &[RDatum]) -> Result<RDatum, RuntimeError> {
        let p_args:Result<Vec<T>, RuntimeError> = args.iter().map(DatumCast::unwrap).collect();
        let f = self.fold;
        p_args.map(|v| f(v.as_slice()).wrap())
    }
}

impl PrimFunc for FoldDatum {
    fn call(&self, args: &[RDatum]) -> Result<RDatum, RuntimeError> {
        let f = self.fold;
        Ok(f(args))
    }
}

impl<T> PrimFunc for Fold1<T> where T: DatumCast {
    fn call(&self, args: &[RDatum]) -> Result<RDatum, RuntimeError> {
        let p_args:Result<Vec<T>, RuntimeError> = args.iter().map(DatumCast::unwrap).collect();
        let f = self.fold1;
        p_args.and_then(|v|
            if v.len() < 1 {
                Err(RuntimeError {
                    kind: RuntimeErrorKind::NumArgs,
                    desc: "Expected at least 1 arguments, received 0".to_string()
                })
            } else {
                Ok(f(&v[0], &v[1..]).wrap())
            }
        )
    }
}

impl<T> PrimFunc for Fold1Err<T> where T: DatumCast {
    fn call(&self, args: &[RDatum]) -> Result<RDatum, RuntimeError> {
        let p_args:Result<Vec<T>, RuntimeError> = args.iter().map(DatumCast::unwrap).collect();
        let f = self.fold1;
        p_args.and_then(|v|
            if v.len() < 1 {
                Err(RuntimeError {
                    kind: RuntimeErrorKind::NumArgs,
                    desc: "Expected at least 1 arguments, received 0".to_string()
                })
            } else {
                f(&v[0], &v[1..]).map(|res| res.wrap())
            }
        )
    }
}

impl<R: DatumCast> PrimFunc for R1<R> {
    fn call(&self, args: &[RDatum]) -> Result<RDatum, RuntimeError> {
        if args.len() != 1 {
            return Err(RuntimeError {
                kind: RuntimeErrorKind::NumArgs,
                desc: format!("Expected 1 argument, received {:?}", args.len())
            });
        }
        let f = self.f1;

        Ok(f(&args[0]).wrap())
    }
}

impl<T0: DatumCast> PrimFunc for F1<T0, RDatum> {
    fn call(&self, args: &[RDatum]) -> Result<RDatum, RuntimeError> {
        if args.len() != 1 {
            return Err(RuntimeError {
                kind: RuntimeErrorKind::NumArgs,
                desc: format!("Expected 1 argument, received {:?}", args.len())
            });
        }
        DatumCast::unwrap(&args[0]).map(self.f1)
    }
}

fn add(args: &[Number]) -> Number {
    let mut sum:Number = Zero::zero();
    for a in args.iter() {
        sum = &sum + a;
    }
    return sum;
}

/// `(+ n0 n1 ...)`
pub static PRIM_ADD:Fold<Number> = Fold { fold: add };

fn mul(args: &[Number]) -> Number {
    let mut product:Number = One::one();
    for a in args.iter() {
        product = &product * a;
    }
    return product;
}

/// `(* n0 n1 ...)`
pub static PRIM_MUL:Fold<Number> = Fold { fold: mul };

fn sub(arg0: &Number, args: &[Number]) -> Number {
    if args.len() == 0 {
        return -arg0;
    }
    let mut sum:Number = arg0.clone();
    for a in args.iter() {
        sum = &sum - a;
    }
    return sum;
}

/// `(- n0 n1 ...)`
pub static PRIM_SUB:Fold1<Number> = Fold1 { fold1: sub };

fn div(arg0: &Number, args: &[Number]) -> Result<Number, RuntimeError> {
    if args.len() == 0 {
        if arg0.is_exact() && arg0.is_zero() {
            return Err(RuntimeError {
                kind: RuntimeErrorKind::DivideByZero,
                desc: "Tried to divied by 0".to_string()
            });
        }
        return Ok(-arg0);
    }

    let mut product:Number = arg0.clone();
    for a in args.iter() {
        if a.is_exact() && a.is_zero() {
            return Err(RuntimeError {
                kind: RuntimeErrorKind::DivideByZero,
                desc: "Tried to divied by 0".to_string()
            });
        }
        product = &product / a;
    }

    return Ok(product);
}

/// `(/ n0 n1 ...)`
pub static PRIM_DIV:Fold1Err<Number> = Fold1Err { fold1: div };

fn list(args: &[RDatum]) -> RDatum {
    FromIterator::from_iter(args.iter().map(Clone::clone))
}

/// `(list a0 a1 ...)`
pub static PRIM_LIST:FoldDatum = FoldDatum { fold: list };

fn car(arg: (RDatum, RDatum)) -> RDatum {
    arg.0
}

/// `(car x)`
pub static PRIM_CAR: F1<(RDatum, RDatum), RDatum> = F1 { f1: car };

fn cdr(arg: (RDatum, RDatum)) -> RDatum {
    arg.1
}

/// `(cdr x)`
pub static PRIM_CDR: F1<(RDatum, RDatum), RDatum> = F1 { f1: cdr };

macro_rules! impl_typecheck {
    ($static_name:ident, $func_name:ident, $type_name:ident) => (
        fn $func_name(arg: &RDatum) -> bool {
            DatumType::get_type(arg) == DatumType::$type_name
        }

        pub static $static_name: R1<bool> = R1 { f1: $func_name };
    )
}

impl_typecheck!(PRIM_BOOLEAN, is_boolean, Bool);
impl_typecheck!(PRIM_PAIR, is_pair, Pair);
impl_typecheck!(PRIM_SYMBOL, is_symbol, Sym);
impl_typecheck!(PRIM_NUMBER, is_number, Num);
impl_typecheck!(PRIM_CHAR, is_char, Char);
impl_typecheck!(PRIM_STRING, is_string, String);
impl_typecheck!(PRIM_PROCEDURE, is_procedure, Callable);
impl_typecheck!(PRIM_NULL, is_null, Null);

/// Lists all primitive functions with its name
pub fn libprimitive() -> Vec<(&'static str, &'static (PrimFunc + 'static))> {
    vec![
        ("+", &PRIM_ADD),
        ("-", &PRIM_SUB),
        ("*", &PRIM_MUL),
        ("/", &PRIM_DIV),
        ("list", &PRIM_LIST),
        ("boolean?", &PRIM_BOOLEAN),
        ("pair?", &PRIM_PAIR),
        ("symbol?", &PRIM_SYMBOL),
        ("number?", &PRIM_NUMBER),
        ("char?", &PRIM_CHAR),
        ("string?", &PRIM_STRING),
        ("procedure?", &PRIM_PROCEDURE),
        ("null?", &PRIM_NULL),
        ("car", &PRIM_CAR),
        ("cdr", &PRIM_CDR)
    ]
}
<commit_msg>Slightly generalize functions<commit_after>use std::iter::FromIterator;

use num::{Zero, One};

use number::Number;
use error::{RuntimeError, RuntimeErrorKind};
use runtime::{DatumCast, RDatum, DatumType};

pub trait PrimFunc {
    fn call(&self, &[RDatum]) -> Result<RDatum, RuntimeError>;
}

pub struct Fold<P> {
    fold: fn(&[P]) -> P
}

pub struct Fold1<P> {
    fold1: fn(&P, &[P]) -> P
}

pub struct Fold1Err<P> {
    fold1: fn(&P, &[P]) -> Result<P, RuntimeError>
}

pub struct F1<T0, R> {
    f1: fn(T0) -> R
}

pub struct R1<R> {
    f1: fn(&RDatum) -> R
}

impl<T> PrimFunc for Fold<T> where T: DatumCast {
    fn call(&self, args: &[RDatum]) -> Result<RDatum, RuntimeError> {
        let p_args:Result<Vec<T>, RuntimeError> = args.iter().map(DatumCast::unwrap).collect();
        let f = self.fold;
        p_args.map(|v| f(v.as_slice()).wrap())
    }
}

impl PrimFunc for Fold<RDatum> {
    fn call(&self, args: &[RDatum]) -> Result<RDatum, RuntimeError> {
        let f = self.fold;
        Ok(f(args))
    }
}

impl<T> PrimFunc for Fold1<T> where T: DatumCast {
    fn call(&self, args: &[RDatum]) -> Result<RDatum, RuntimeError> {
        let p_args:Result<Vec<T>, RuntimeError> = args.iter().map(DatumCast::unwrap).collect();
        let f = self.fold1;
        p_args.and_then(|v|
            if v.len() < 1 {
                Err(RuntimeError {
                    kind: RuntimeErrorKind::NumArgs,
                    desc: "Expected at least 1 arguments, received 0".to_string()
                })
            } else {
                Ok(f(&v[0], &v[1..]).wrap())
            }
        )
    }
}

impl<T> PrimFunc for Fold1Err<T> where T: DatumCast {
    fn call(&self, args: &[RDatum]) -> Result<RDatum, RuntimeError> {
        let p_args:Result<Vec<T>, RuntimeError> = args.iter().map(DatumCast::unwrap).collect();
        let f = self.fold1;
        p_args.and_then(|v|
            if v.len() < 1 {
                Err(RuntimeError {
                    kind: RuntimeErrorKind::NumArgs,
                    desc: "Expected at least 1 arguments, received 0".to_string()
                })
            } else {
                f(&v[0], &v[1..]).map(|res| res.wrap())
            }
        )
    }
}

impl<R: DatumCast> PrimFunc for R1<R> {
    fn call(&self, args: &[RDatum]) -> Result<RDatum, RuntimeError> {
        if args.len() != 1 {
            return Err(RuntimeError {
                kind: RuntimeErrorKind::NumArgs,
                desc: format!("Expected 1 argument, received {:?}", args.len())
            });
        }
        let f = self.f1;

        Ok(f(&args[0]).wrap())
    }
}

impl<T0: DatumCast> PrimFunc for F1<T0, RDatum> {
    fn call(&self, args: &[RDatum]) -> Result<RDatum, RuntimeError> {
        if args.len() != 1 {
            return Err(RuntimeError {
                kind: RuntimeErrorKind::NumArgs,
                desc: format!("Expected 1 argument, received {:?}", args.len())
            });
        }
        DatumCast::unwrap(&args[0]).map(self.f1)
    }
}

fn add(args: &[Number]) -> Number {
    let mut sum:Number = Zero::zero();
    for a in args.iter() {
        sum = &sum + a;
    }
    return sum;
}

/// `(+ n0 n1 ...)`
pub static PRIM_ADD:Fold<Number> = Fold { fold: add };

fn mul(args: &[Number]) -> Number {
    let mut product:Number = One::one();
    for a in args.iter() {
        product = &product * a;
    }
    return product;
}

/// `(* n0 n1 ...)`
pub static PRIM_MUL:Fold<Number> = Fold { fold: mul };

fn sub(arg0: &Number, args: &[Number]) -> Number {
    if args.len() == 0 {
        return -arg0;
    }
    let mut sum:Number = arg0.clone();
    for a in args.iter() {
        sum = &sum - a;
    }
    return sum;
}

/// `(- n0 n1 ...)`
pub static PRIM_SUB:Fold1<Number> = Fold1 { fold1: sub };

fn div(arg0: &Number, args: &[Number]) -> Result<Number, RuntimeError> {
    if args.len() == 0 {
        if arg0.is_exact() && arg0.is_zero() {
            return Err(RuntimeError {
                kind: RuntimeErrorKind::DivideByZero,
                desc: "Tried to divied by 0".to_string()
            });
        }
        return Ok(-arg0);
    }

    let mut product:Number = arg0.clone();
    for a in args.iter() {
        if a.is_exact() && a.is_zero() {
            return Err(RuntimeError {
                kind: RuntimeErrorKind::DivideByZero,
                desc: "Tried to divied by 0".to_string()
            });
        }
        product = &product / a;
    }

    return Ok(product);
}

/// `(/ n0 n1 ...)`
pub static PRIM_DIV:Fold1Err<Number> = Fold1Err { fold1: div };

fn list(args: &[RDatum]) -> RDatum {
    FromIterator::from_iter(args.iter().map(Clone::clone))
}

/// `(list a0 a1 ...)`
pub static PRIM_LIST:Fold<RDatum> = Fold { fold: list };

fn car(arg: (RDatum, RDatum)) -> RDatum {
    arg.0
}

/// `(car x)`
pub static PRIM_CAR: F1<(RDatum, RDatum), RDatum> = F1 { f1: car };

fn cdr(arg: (RDatum, RDatum)) -> RDatum {
    arg.1
}

/// `(cdr x)`
pub static PRIM_CDR: F1<(RDatum, RDatum), RDatum> = F1 { f1: cdr };

macro_rules! impl_typecheck {
    ($static_name:ident, $func_name:ident, $type_name:ident) => (
        fn $func_name(arg: &RDatum) -> bool {
            DatumType::get_type(arg) == DatumType::$type_name
        }

        pub static $static_name: R1<bool> = R1 { f1: $func_name };
    )
}

impl_typecheck!(PRIM_BOOLEAN, is_boolean, Bool);
impl_typecheck!(PRIM_PAIR, is_pair, Pair);
impl_typecheck!(PRIM_SYMBOL, is_symbol, Sym);
impl_typecheck!(PRIM_NUMBER, is_number, Num);
impl_typecheck!(PRIM_CHAR, is_char, Char);
impl_typecheck!(PRIM_STRING, is_string, String);
impl_typecheck!(PRIM_PROCEDURE, is_procedure, Callable);
impl_typecheck!(PRIM_NULL, is_null, Null);

/// Lists all primitive functions with its name
pub fn libprimitive() -> Vec<(&'static str, &'static (PrimFunc + 'static))> {
    vec![
        ("+", &PRIM_ADD),
        ("-", &PRIM_SUB),
        ("*", &PRIM_MUL),
        ("/", &PRIM_DIV),
        ("list", &PRIM_LIST),
        ("boolean?", &PRIM_BOOLEAN),
        ("pair?", &PRIM_PAIR),
        ("symbol?", &PRIM_SYMBOL),
        ("number?", &PRIM_NUMBER),
        ("char?", &PRIM_CHAR),
        ("string?", &PRIM_STRING),
        ("procedure?", &PRIM_PROCEDURE),
        ("null?", &PRIM_NULL),
        ("car", &PRIM_CAR),
        ("cdr", &PRIM_CDR)
    ]
}
<|endoftext|>


<commit_before>use crate::plot_data::PlotData;
use anyhow::{anyhow, Result};
use chrono::prelude::*;
use crossterm::event::{KeyEvent, KeyModifiers};
use crossterm::{
    event::{self, DisableMouseCapture, EnableMouseCapture, Event as CEvent, KeyCode},
    execute,
    terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
};
use dns_lookup::lookup_host;
use pinger::{ping, PingResult};
use std::io;
use std::iter;
use std::net::IpAddr;
use std::ops::Add;
use std::process::{Command, Stdio};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::mpsc::Sender;
use std::sync::{mpsc, Arc};
use std::thread;
use std::thread::JoinHandle;
use std::time::{Duration, Instant};
use structopt::StructOpt;
use tui::backend::CrosstermBackend;
use tui::layout::{Constraint, Direction, Layout};
use tui::style::{Color, Style};
use tui::text::Span;
use tui::widgets::{Axis, Block, Borders, Chart, Dataset};
use tui::Terminal;
mod plot_data;

#[derive(Debug, StructOpt)]
#[structopt(name = "gping", about = "Ping, but with a graph.")]
struct Args {
    #[structopt(
        long,
        help = "Graph the execution time for a list of commands rather than pinging hosts"
    )]
    cmd: bool,
    #[structopt(
        short = "n",
        long,
        help = "Watch interval seconds (provide partial seconds like '0.5')",
        default_value = "0.5"
    )]
    watch_interval: f32,
    #[structopt(
        help = "Hosts or IPs to ping, or commands to run if --cmd is provided.",
        required = true
    )]
    hosts_or_commands: Vec<String>,
    #[structopt(
        short,
        long,
        default_value = "30",
        help = "Determines the number of seconds to display in the graph."
    )]
    buffer: u64,
    /// Resolve ping targets to IPv4 address
    #[structopt(short = "4", conflicts_with = "ipv6")]
    ipv4: bool,
    /// Resolve ping targets to IPv6 address
    #[structopt(short = "6", conflicts_with = "ipv4")]
    ipv6: bool,
    #[structopt(short = "s", long, help = "Uses dot characters instead of braille")]
    simple_graphics: bool,
}

struct App {
    data: Vec<PlotData>,
    display_interval: chrono::Duration,
    started: chrono::DateTime<Local>,
}

impl App {
    fn new(data: Vec<PlotData>, buffer: u64) -> Self {
        App {
            data,
            display_interval: chrono::Duration::from_std(Duration::from_secs(buffer)).unwrap(),
            started: Local::now(),
        }
    }

    fn update(&mut self, host_idx: usize, item: Option<Duration>) {
        let host = &mut self.data[host_idx];
        host.update(item);
    }

    fn y_axis_bounds(&self) -> [f64; 2] {
        // Find the Y axis bounds for our chart.
        // This is trickier than the x-axis. We iterate through all our PlotData structs
        // and find the min/max of all the values. Then we add a 10% buffer to them.
        let iter = self
            .data
            .iter()
            .map(|b| b.data.as_slice())
            .flatten()
            .map(|v| v.1);
        let min = iter.clone().fold(f64::INFINITY, |a, b| a.min(b));
        let max = iter.fold(0f64, |a, b| a.max(b));
        // Add a 10% buffer to the top and bottom
        let max_10_percent = (max * 10_f64) / 100_f64;
        let min_10_percent = (min * 10_f64) / 100_f64;
        [min - min_10_percent, max + max_10_percent]
    }

    fn x_axis_bounds(&self) -> [f64; 2] {
        let now = Local::now();
        let now_idx;
        let before_idx;
        if (now - self.started) < self.display_interval {
            now_idx = (self.started + self.display_interval).timestamp_millis() as f64 / 1_000f64;
            before_idx = self.started.timestamp_millis() as f64 / 1_000f64;
        } else {
            now_idx = now.timestamp_millis() as f64 / 1_000f64;
            let before = now - self.display_interval;
            before_idx = before.timestamp_millis() as f64 / 1_000f64;
        }

        [before_idx, now_idx]
    }

    fn x_axis_labels(&self, bounds: [f64; 2]) -> Vec<Span> {
        let lower_utc = NaiveDateTime::from_timestamp(bounds[0] as i64, 0);
        let upper_utc = NaiveDateTime::from_timestamp(bounds[1] as i64, 0);
        let lower = Local::from_utc_datetime(&Local, &lower_utc);
        let upper = Local::from_utc_datetime(&Local, &upper_utc);
        let diff = (upper - lower) / 2;
        let midpoint = lower + diff;
        return vec![
            Span::raw(format!("{:?}", lower.time())),
            Span::raw(format!("{:?}", midpoint.time())),
            Span::raw(format!("{:?}", upper.time())),
        ];
    }

    fn y_axis_labels(&self, bounds: [f64; 2]) -> Vec<Span> {
        // Create 7 labels for our y axis, based on the y-axis bounds we computed above.
        let min = bounds[0];
        let max = bounds[1];

        let difference = max - min;
        let num_labels = 7;
        // Split difference into one chunk for each of the 7 labels
        let increment = Duration::from_micros((difference / num_labels as f64) as u64);
        let duration = Duration::from_micros(min as u64);

        (0..num_labels)
            .map(|i| Span::raw(format!("{:?}", duration.add(increment * i))))
            .collect()
    }
}

#[derive(Debug)]
enum Update {
    Result(Duration),
    Timeout,
    Unknown,
}

impl From<PingResult> for Update {
    fn from(result: PingResult) -> Self {
        match result {
            PingResult::Pong(duration, _) => Update::Result(duration),
            PingResult::Timeout(_) => Update::Timeout,
            PingResult::Unknown(_) => Update::Unknown,
        }
    }
}

#[derive(Debug)]
enum Event {
    Update(usize, Update),
    Input(KeyEvent),
}

fn start_cmd_thread(
    watch_cmd: &str,
    host_id: usize,
    watch_interval: f32,
    cmd_tx: Sender<Event>,
    kill_event: Arc<AtomicBool>,
) -> JoinHandle<Result<()>> {
    let mut words = watch_cmd.split_ascii_whitespace();
    let cmd = words
        .next()
        .expect("Must specify a command to watch")
        .to_string();
    let cmd_args = words
        .into_iter()
        .map(|w| w.to_string())
        .collect::<Vec<String>>();

    let interval = Duration::from_millis((watch_interval * 1000.0) as u64);

    // Pump cmd watches into the queue
    thread::spawn(move || -> Result<()> {
        while !kill_event.load(Ordering::Acquire) {
            let start = Instant::now();
            let mut child = Command::new(&cmd)
                .args(&cmd_args)
                .stderr(Stdio::null())
                .stdout(Stdio::null())
                .spawn()?;
            let status = child.wait()?;
            let duration = start.elapsed();
            let update = if status.success() {
                Update::Result(duration)
            } else {
                Update::Timeout
            };
            cmd_tx.send(Event::Update(host_id, update))?;
            thread::sleep(interval);
        }
        Ok(())
    })
}

fn start_ping_thread(
    host: String,
    host_id: usize,
    ping_tx: Sender<Event>,
    kill_event: Arc<AtomicBool>,
) -> JoinHandle<Result<()>> {
    // Pump ping messages into the queue
    thread::spawn(move || -> Result<()> {
        let stream = ping(host)?;
        while !kill_event.load(Ordering::Acquire) {
            ping_tx.send(Event::Update(host_id, stream.recv()?.into()))?;
        }
        Ok(())
    })
}

fn get_host_ipaddr(host: &str, force_ipv4: bool, force_ipv6: bool) -> Result<String> {
    let ipaddr: Vec<IpAddr> = match lookup_host(host) {
        Ok(ip) => ip,
        Err(_) => return Err(anyhow!("Could not resolve hostname {}", host)),
    };
    let ipaddr = if force_ipv4 {
        ipaddr
            .iter()
            .find(|ip| matches!(ip, IpAddr::V4(_)))
            .ok_or_else(|| anyhow!("Could not resolve '{}' to IPv4", host))
    } else if force_ipv6 {
        ipaddr
            .iter()
            .find(|ip| matches!(ip, IpAddr::V6(_)))
            .ok_or_else(|| anyhow!("Could not resolve '{}' to IPv6", host))
    } else {
        ipaddr
            .first()
            .ok_or_else(|| anyhow!("Could not resolve '{}' to IP", host))
    };
    Ok(ipaddr?.to_string())
}

fn main() -> Result<()> {
    let args = Args::from_args();

    let mut data = vec![];

    for (idx, host_or_cmd) in args.hosts_or_commands.iter().enumerate() {
        let display = match args.cmd {
            true => host_or_cmd.to_string(),
            false => format!(
                "{} ({})",
                host_or_cmd,
                get_host_ipaddr(host_or_cmd, args.ipv4, args.ipv6)?
            ),
        };
        data.push(PlotData::new(
            display,
            args.buffer,
            Style::default().fg(Color::Indexed(idx as u8 + 2)),
            args.simple_graphics,
        ));
    }

    let mut app = App::new(data, args.buffer);
    enable_raw_mode()?;
    let mut stdout = io::stdout();
    execute!(stdout, EnterAlternateScreen, EnableMouseCapture)?;
    let backend = CrosstermBackend::new(stdout);

    let mut terminal = Terminal::new(backend)?;

    terminal.clear()?;

    let (key_tx, rx) = mpsc::channel();

    let mut threads = vec![];

    let killed = std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false));

    for (host_id, host_or_cmd) in args.hosts_or_commands.iter().cloned().enumerate() {
        if args.cmd {
            let cmd_thread = start_cmd_thread(
                &host_or_cmd,
                host_id,
                args.watch_interval,
                key_tx.clone(),
                std::sync::Arc::clone(&killed),
            );
            threads.push(cmd_thread);
        } else {
            threads.push(start_ping_thread(
                host_or_cmd,
                host_id,
                key_tx.clone(),
                std::sync::Arc::clone(&killed),
            ));
        }
    }

    // Pump keyboard messages into the queue
    let killed_thread = std::sync::Arc::clone(&killed);
    let key_thread = thread::spawn(move || -> Result<()> {
        while !killed_thread.load(Ordering::Acquire) {
            if event::poll(Duration::from_millis(100))? {
                if let CEvent::Key(key) = event::read()? {
                    key_tx.send(Event::Input(key))?;
                }
            }
        }
        Ok(())
    });
    threads.push(key_thread);

    loop {
        match rx.recv()? {
            Event::Update(host_id, update) => {
                match update {
                    Update::Result(duration) => app.update(host_id, Some(duration)),
                    Update::Timeout => app.update(host_id, None),
                    Update::Unknown => (),
                };
                terminal.draw(|f| {
                    // Split our
                    let chunks = Layout::default()
                        .direction(Direction::Vertical)
                        .margin(1)
                        .constraints(
                            iter::repeat(Constraint::Length(1))
                                .take(app.data.len())
                                .chain(iter::once(Constraint::Percentage(10)))
                                .collect::<Vec<_>>()
                                .as_ref(),
                        )
                        .split(f.size());

                    let total_chunks = chunks.len();

                    let header_chunks = chunks[0..total_chunks - 1].to_owned();
                    let chart_chunk = chunks[total_chunks - 1].to_owned();

                    for (plot_data, chunk) in app.data.iter().zip(header_chunks) {
                        let header_layout = Layout::default()
                            .direction(Direction::Horizontal)
                            .constraints(
                                [
                                    Constraint::Percentage(20),
                                    Constraint::Percentage(20),
                                    Constraint::Percentage(20),
                                    Constraint::Percentage(20),
                                    Constraint::Percentage(20),
                                ]
                                .as_ref(),
                            )
                            .split(chunk);

                        for (area, paragraph) in
                            header_layout.into_iter().zip(plot_data.header_stats())
                        {
                            f.render_widget(paragraph, area);
                        }
                    }

                    let datasets: Vec<Dataset> = app.data.iter().map(|d| d.into()).collect();

                    let y_axis_bounds = app.y_axis_bounds();
                    let x_axis_bounds = app.x_axis_bounds();

                    let chart = Chart::new(datasets)
                        .block(Block::default().borders(Borders::NONE))
                        .x_axis(
                            Axis::default()
                                .style(Style::default().fg(Color::Gray))
                                .bounds(x_axis_bounds)
                                .labels(app.x_axis_labels(x_axis_bounds)),
                        )
                        .y_axis(
                            Axis::default()
                                .style(Style::default().fg(Color::Gray))
                                .bounds(y_axis_bounds)
                                .labels(app.y_axis_labels(y_axis_bounds)),
                        );

                    f.render_widget(chart, chart_chunk)
                })?;
            }
            Event::Input(input) => match input.code {
                KeyCode::Char('q') | KeyCode::Esc => {
                    killed.store(true, Ordering::Release);
                    break;
                }
                KeyCode::Char('c') if input.modifiers == KeyModifiers::CONTROL => {
                    killed.store(true, Ordering::Release);
                    break;
                }
                _ => {}
            },
        }
    }

    for thread in threads {
        thread.join().unwrap()?;
    }

    disable_raw_mode()?;
    execute!(
        terminal.backend_mut(),
        LeaveAlternateScreen,
        DisableMouseCapture
    )?;
    terminal.show_cursor()?;

    Ok(())
}
<commit_msg>Make watch_interval also apply to ping (#150)<commit_after>use crate::plot_data::PlotData;
use anyhow::{anyhow, Result};
use chrono::prelude::*;
use crossterm::event::{KeyEvent, KeyModifiers};
use crossterm::{
    event::{self, DisableMouseCapture, EnableMouseCapture, Event as CEvent, KeyCode},
    execute,
    terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
};
use dns_lookup::lookup_host;
use pinger::{ping_with_interval, PingResult};
use std::io;
use std::iter;
use std::net::IpAddr;
use std::ops::Add;
use std::process::{Command, Stdio};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::mpsc::Sender;
use std::sync::{mpsc, Arc};
use std::thread;
use std::thread::JoinHandle;
use std::time::{Duration, Instant};
use structopt::StructOpt;
use tui::backend::CrosstermBackend;
use tui::layout::{Constraint, Direction, Layout};
use tui::style::{Color, Style};
use tui::text::Span;
use tui::widgets::{Axis, Block, Borders, Chart, Dataset};
use tui::Terminal;
mod plot_data;

#[derive(Debug, StructOpt)]
#[structopt(name = "gping", about = "Ping, but with a graph.")]
struct Args {
    #[structopt(
        long,
        help = "Graph the execution time for a list of commands rather than pinging hosts"
    )]
    cmd: bool,
    #[structopt(
        short = "n",
        long,
        help = "Watch interval seconds (provide partial seconds like '0.5'). Default for ping is 0.2, default for cmd is 0.5."
    )]
    watch_interval: Option<f32>,
    #[structopt(
        help = "Hosts or IPs to ping, or commands to run if --cmd is provided.",
        required = true
    )]
    hosts_or_commands: Vec<String>,
    #[structopt(
        short,
        long,
        default_value = "30",
        help = "Determines the number of seconds to display in the graph."
    )]
    buffer: u64,
    /// Resolve ping targets to IPv4 address
    #[structopt(short = "4", conflicts_with = "ipv6")]
    ipv4: bool,
    /// Resolve ping targets to IPv6 address
    #[structopt(short = "6", conflicts_with = "ipv4")]
    ipv6: bool,
    #[structopt(short = "s", long, help = "Uses dot characters instead of braille")]
    simple_graphics: bool,
}

struct App {
    data: Vec<PlotData>,
    display_interval: chrono::Duration,
    started: chrono::DateTime<Local>,
}

impl App {
    fn new(data: Vec<PlotData>, buffer: u64) -> Self {
        App {
            data,
            display_interval: chrono::Duration::from_std(Duration::from_secs(buffer)).unwrap(),
            started: Local::now(),
        }
    }

    fn update(&mut self, host_idx: usize, item: Option<Duration>) {
        let host = &mut self.data[host_idx];
        host.update(item);
    }

    fn y_axis_bounds(&self) -> [f64; 2] {
        // Find the Y axis bounds for our chart.
        // This is trickier than the x-axis. We iterate through all our PlotData structs
        // and find the min/max of all the values. Then we add a 10% buffer to them.
        let iter = self
            .data
            .iter()
            .map(|b| b.data.as_slice())
            .flatten()
            .map(|v| v.1);
        let min = iter.clone().fold(f64::INFINITY, |a, b| a.min(b));
        let max = iter.fold(0f64, |a, b| a.max(b));
        // Add a 10% buffer to the top and bottom
        let max_10_percent = (max * 10_f64) / 100_f64;
        let min_10_percent = (min * 10_f64) / 100_f64;
        [min - min_10_percent, max + max_10_percent]
    }

    fn x_axis_bounds(&self) -> [f64; 2] {
        let now = Local::now();
        let now_idx;
        let before_idx;
        if (now - self.started) < self.display_interval {
            now_idx = (self.started + self.display_interval).timestamp_millis() as f64 / 1_000f64;
            before_idx = self.started.timestamp_millis() as f64 / 1_000f64;
        } else {
            now_idx = now.timestamp_millis() as f64 / 1_000f64;
            let before = now - self.display_interval;
            before_idx = before.timestamp_millis() as f64 / 1_000f64;
        }

        [before_idx, now_idx]
    }

    fn x_axis_labels(&self, bounds: [f64; 2]) -> Vec<Span> {
        let lower_utc = NaiveDateTime::from_timestamp(bounds[0] as i64, 0);
        let upper_utc = NaiveDateTime::from_timestamp(bounds[1] as i64, 0);
        let lower = Local::from_utc_datetime(&Local, &lower_utc);
        let upper = Local::from_utc_datetime(&Local, &upper_utc);
        let diff = (upper - lower) / 2;
        let midpoint = lower + diff;
        return vec![
            Span::raw(format!("{:?}", lower.time())),
            Span::raw(format!("{:?}", midpoint.time())),
            Span::raw(format!("{:?}", upper.time())),
        ];
    }

    fn y_axis_labels(&self, bounds: [f64; 2]) -> Vec<Span> {
        // Create 7 labels for our y axis, based on the y-axis bounds we computed above.
        let min = bounds[0];
        let max = bounds[1];

        let difference = max - min;
        let num_labels = 7;
        // Split difference into one chunk for each of the 7 labels
        let increment = Duration::from_micros((difference / num_labels as f64) as u64);
        let duration = Duration::from_micros(min as u64);

        (0..num_labels)
            .map(|i| Span::raw(format!("{:?}", duration.add(increment * i))))
            .collect()
    }
}

#[derive(Debug)]
enum Update {
    Result(Duration),
    Timeout,
    Unknown,
}

impl From<PingResult> for Update {
    fn from(result: PingResult) -> Self {
        match result {
            PingResult::Pong(duration, _) => Update::Result(duration),
            PingResult::Timeout(_) => Update::Timeout,
            PingResult::Unknown(_) => Update::Unknown,
        }
    }
}

#[derive(Debug)]
enum Event {
    Update(usize, Update),
    Input(KeyEvent),
}

fn start_cmd_thread(
    watch_cmd: &str,
    host_id: usize,
    watch_interval: Option<f32>,
    cmd_tx: Sender<Event>,
    kill_event: Arc<AtomicBool>,
) -> JoinHandle<Result<()>> {
    let mut words = watch_cmd.split_ascii_whitespace();
    let cmd = words
        .next()
        .expect("Must specify a command to watch")
        .to_string();
    let cmd_args = words
        .into_iter()
        .map(|w| w.to_string())
        .collect::<Vec<String>>();

    let interval = Duration::from_millis((watch_interval.unwrap_or(0.5) * 1000.0) as u64);

    // Pump cmd watches into the queue
    thread::spawn(move || -> Result<()> {
        while !kill_event.load(Ordering::Acquire) {
            let start = Instant::now();
            let mut child = Command::new(&cmd)
                .args(&cmd_args)
                .stderr(Stdio::null())
                .stdout(Stdio::null())
                .spawn()?;
            let status = child.wait()?;
            let duration = start.elapsed();
            let update = if status.success() {
                Update::Result(duration)
            } else {
                Update::Timeout
            };
            cmd_tx.send(Event::Update(host_id, update))?;
            thread::sleep(interval);
        }
        Ok(())
    })
}

fn start_ping_thread(
    host: String,
    host_id: usize,
    watch_interval: Option<f32>,
    ping_tx: Sender<Event>,
    kill_event: Arc<AtomicBool>,
) -> JoinHandle<Result<()>> {
    let interval = Duration::from_millis((watch_interval.unwrap_or(0.2) * 1000.0) as u64);
    // Pump ping messages into the queue
    thread::spawn(move || -> Result<()> {
        let stream = ping_with_interval(host, interval)?;
        while !kill_event.load(Ordering::Acquire) {
            ping_tx.send(Event::Update(host_id, stream.recv()?.into()))?;
        }
        Ok(())
    })
}

fn get_host_ipaddr(host: &str, force_ipv4: bool, force_ipv6: bool) -> Result<String> {
    let ipaddr: Vec<IpAddr> = match lookup_host(host) {
        Ok(ip) => ip,
        Err(_) => return Err(anyhow!("Could not resolve hostname {}", host)),
    };
    let ipaddr = if force_ipv4 {
        ipaddr
            .iter()
            .find(|ip| matches!(ip, IpAddr::V4(_)))
            .ok_or_else(|| anyhow!("Could not resolve '{}' to IPv4", host))
    } else if force_ipv6 {
        ipaddr
            .iter()
            .find(|ip| matches!(ip, IpAddr::V6(_)))
            .ok_or_else(|| anyhow!("Could not resolve '{}' to IPv6", host))
    } else {
        ipaddr
            .first()
            .ok_or_else(|| anyhow!("Could not resolve '{}' to IP", host))
    };
    Ok(ipaddr?.to_string())
}

fn main() -> Result<()> {
    let args = Args::from_args();

    let mut data = vec![];

    for (idx, host_or_cmd) in args.hosts_or_commands.iter().enumerate() {
        let display = match args.cmd {
            true => host_or_cmd.to_string(),
            false => format!(
                "{} ({})",
                host_or_cmd,
                get_host_ipaddr(host_or_cmd, args.ipv4, args.ipv6)?
            ),
        };
        data.push(PlotData::new(
            display,
            args.buffer,
            Style::default().fg(Color::Indexed(idx as u8 + 2)),
            args.simple_graphics,
        ));
    }

    let mut app = App::new(data, args.buffer);
    enable_raw_mode()?;
    let mut stdout = io::stdout();
    execute!(stdout, EnterAlternateScreen, EnableMouseCapture)?;
    let backend = CrosstermBackend::new(stdout);

    let mut terminal = Terminal::new(backend)?;

    terminal.clear()?;

    let (key_tx, rx) = mpsc::channel();

    let mut threads = vec![];

    let killed = std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false));

    for (host_id, host_or_cmd) in args.hosts_or_commands.iter().cloned().enumerate() {
        if args.cmd {
            let cmd_thread = start_cmd_thread(
                &host_or_cmd,
                host_id,
                args.watch_interval,
                key_tx.clone(),
                std::sync::Arc::clone(&killed),
            );
            threads.push(cmd_thread);
        } else {
            threads.push(start_ping_thread(
                host_or_cmd,
                host_id,
                args.watch_interval,
                key_tx.clone(),
                std::sync::Arc::clone(&killed),
            ));
        }
    }

    // Pump keyboard messages into the queue
    let killed_thread = std::sync::Arc::clone(&killed);
    let key_thread = thread::spawn(move || -> Result<()> {
        while !killed_thread.load(Ordering::Acquire) {
            if event::poll(Duration::from_millis(100))? {
                if let CEvent::Key(key) = event::read()? {
                    key_tx.send(Event::Input(key))?;
                }
            }
        }
        Ok(())
    });
    threads.push(key_thread);

    loop {
        match rx.recv()? {
            Event::Update(host_id, update) => {
                match update {
                    Update::Result(duration) => app.update(host_id, Some(duration)),
                    Update::Timeout => app.update(host_id, None),
                    Update::Unknown => (),
                };
                terminal.draw(|f| {
                    // Split our
                    let chunks = Layout::default()
                        .direction(Direction::Vertical)
                        .margin(1)
                        .constraints(
                            iter::repeat(Constraint::Length(1))
                                .take(app.data.len())
                                .chain(iter::once(Constraint::Percentage(10)))
                                .collect::<Vec<_>>()
                                .as_ref(),
                        )
                        .split(f.size());

                    let total_chunks = chunks.len();

                    let header_chunks = chunks[0..total_chunks - 1].to_owned();
                    let chart_chunk = chunks[total_chunks - 1].to_owned();

                    for (plot_data, chunk) in app.data.iter().zip(header_chunks) {
                        let header_layout = Layout::default()
                            .direction(Direction::Horizontal)
                            .constraints(
                                [
                                    Constraint::Percentage(20),
                                    Constraint::Percentage(20),
                                    Constraint::Percentage(20),
                                    Constraint::Percentage(20),
                                    Constraint::Percentage(20),
                                ]
                                .as_ref(),
                            )
                            .split(chunk);

                        for (area, paragraph) in
                            header_layout.into_iter().zip(plot_data.header_stats())
                        {
                            f.render_widget(paragraph, area);
                        }
                    }

                    let datasets: Vec<Dataset> = app.data.iter().map(|d| d.into()).collect();

                    let y_axis_bounds = app.y_axis_bounds();
                    let x_axis_bounds = app.x_axis_bounds();

                    let chart = Chart::new(datasets)
                        .block(Block::default().borders(Borders::NONE))
                        .x_axis(
                            Axis::default()
                                .style(Style::default().fg(Color::Gray))
                                .bounds(x_axis_bounds)
                                .labels(app.x_axis_labels(x_axis_bounds)),
                        )
                        .y_axis(
                            Axis::default()
                                .style(Style::default().fg(Color::Gray))
                                .bounds(y_axis_bounds)
                                .labels(app.y_axis_labels(y_axis_bounds)),
                        );

                    f.render_widget(chart, chart_chunk)
                })?;
            }
            Event::Input(input) => match input.code {
                KeyCode::Char('q') | KeyCode::Esc => {
                    killed.store(true, Ordering::Release);
                    break;
                }
                KeyCode::Char('c') if input.modifiers == KeyModifiers::CONTROL => {
                    killed.store(true, Ordering::Release);
                    break;
                }
                _ => {}
            },
        }
    }

    for thread in threads {
        thread.join().unwrap()?;
    }

    disable_raw_mode()?;
    execute!(
        terminal.backend_mut(),
        LeaveAlternateScreen,
        DisableMouseCapture
    )?;
    terminal.show_cursor()?;

    Ok(())
}
<|endoftext|>