<commit_before>// Copyright 2017 Samuel Loretan <tynril@gmail.com>

//! Triangulate a set of points

/// A two dimentional point.
#[derive(Clone, Copy, Debug)]
pub struct Point {
    x: f64,
    y: f64,
}

impl Point {
    /// Makes a new point from xy coordinates.
    pub fn new(x: f64, y: f64) -> Point {
        Point { x: x, y: y }
    }
}

/// A triangle, represented by indexes into a vertice list.
#[derive(Debug, PartialEq)]
pub struct Triangle(usize, usize, usize);

/// An edge, represented by indexes into a vertice list.
#[derive(Debug, PartialEq)]
struct Edge(usize, usize);

/// Triangulate a given set of points. The returned triangles are indices into the  list of points.
pub fn triangulate(points: &mut Vec<Point>) -> Vec<Triangle> {
    // Make sure we have enough points to do a triangulation.
    let points_count = points.len();
    if points_count < 3 {
        panic!("Can't triangulate less than three points.")
    }
    let max_triangles = points_count * 4;

    // Find the bounds of the space that contains our points.
    let mut min_point = points[0];
    let mut max_point = min_point;
    for point in points.iter() {
        if point.x > max_point.x {
            max_point.x = point.x;
        }
        if point.y > max_point.y {
            max_point.y = point.y;
        }
        if point.x < min_point.x {
            min_point.x = point.x;
        }
        if point.y < min_point.y {
            min_point.y = point.y;
        }
    }
    let delta_point = Point {
        x: max_point.x - min_point.x,
        y: max_point.y - min_point.y,
    };
    let delta_max = if delta_point.x > delta_point.y {
        delta_point.x
    } else {
        delta_point.y
    };
    let mid_point = Point {
        x: (max_point.x + min_point.x) * 0.5,
        y: (max_point.y + min_point.y) * 0.5,
    };

    // Compute the supertriangle, which encompasses all the input points.
    points.push(Point::new(mid_point.x - 2.0 * delta_max, mid_point.y - delta_max));
    points.push(Point::new(mid_point.x, mid_point.y + 2.0 * delta_max));
    points.push(Point::new(mid_point.x + 2.0 * delta_max, mid_point.y - delta_max));

    // The list of triangles we're gonna fill, initialized with the super-triangle.
    let mut triangles = vec![Triangle(points_count, points_count + 1, points_count + 2)];

    // Include each of the input point into the mesh.
    for i in 0..points_count {
        // Storage for the edges
        let mut edges = Vec::<Edge>::new();

        {
            let mut j = 0;
            while j < triangles.len() {
                println!("in_circle {:?} -> {:?} {:?} {:?}",
                         points[i],
                         points[triangles[j].0],
                         points[triangles[j].1],
                         points[triangles[j].2]);
                if in_circle(points[i],
                             points[triangles[j].0],
                             points[triangles[j].1],
                             points[triangles[j].2]) {
                    println!("YES");
                    edges.push(Edge(triangles[j].0, triangles[j].1));
                    edges.push(Edge(triangles[j].1, triangles[j].2));
                    edges.push(Edge(triangles[j].2, triangles[j].0));
                    triangles.remove(j);
                    println!("Triangle remove {}", j);
                } else {
                    println!("Nope");
                    j += 1;
                }
            }
        }

        // Continue if we've exhausted the array.
        if i >= points_count {
            continue;
        }

        // Remove duplicate edges.
        {
            let mut j = edges.len() - 2;
            loop {
                let mut k = edges.len() - 1;
                while k >= j + 1 {
                    if edges[j] == edges[k] {
                        edges.remove(k);
                        edges.remove(j);
                        k -= 2;
                    } else {
                        k -= 1;
                    }
                }
                if j > 0 {
                    j -= 1;
                } else {
                    break;
                }
            }
        }

        // Form new triangles from the remaining edges. Edges are added in clockwise order.
        {
            let mut j = 0;
            while j < edges.len() {
                if triangles.len() >= max_triangles {
                    panic!("Exceeded maximum edges");
                }
                let new_tri = Triangle(edges[j].0, edges[j].1, i);
                println!("Add triangle {:?}", new_tri);
                triangles.push(new_tri);
                j += 1;
            }
        }
    }

    // Remove triangles with supertriangle vertices
    let mut i = triangles.len() - 1;
    loop {
        if triangles[i].0 >= points_count || triangles[i].1 >= points_count ||
           triangles[i].2 >= points_count {
            triangles.remove(i);
        }
        if i > 0 {
            i -= 1;
        } else {
            break;
        }
    }

    // Remove the supertriangle vertices
    points.pop();
    points.pop();
    points.pop();

    triangles
}

/// Returns true if the first point lies inside (or on the edge of) the circumcircle made from the
/// three other points.
fn in_circle(p: Point, pc1: Point, pc2: Point, pc3: Point) -> bool {
    // Handle coincident points.
    if (pc1.y - pc2.y).abs() < std::f64::EPSILON && (pc2.y - pc3.y) < std::f64::EPSILON {
        return false;
    }

    let m1: f64;
    let m2: f64;
    let mx1: f64;
    let mx2: f64;
    let my1: f64;
    let my2: f64;
    let xc: f64;
    let yc: f64;

    if (pc2.y - pc1.y).abs() < std::f64::EPSILON {
        m2 = 0.0 - (pc3.x - pc2.x) / (pc3.y - pc2.y);
        mx2 = (pc2.x + pc3.x) * 0.5;
        my2 = (pc2.y + pc3.y) * 0.5;
        xc = (pc2.x + pc1.x) * 0.5;
        yc = m2 * (xc - mx2) + my2;
    } else if (pc3.y - pc2.y).abs() < std::f64::EPSILON {
        m1 = 0.0 - (pc2.x - pc1.x) / (pc2.y - pc1.y);
        mx1 = (pc1.x + pc2.x) * 0.5;
        my1 = (pc1.y + pc2.y) * 0.5;
        xc = (pc3.x + pc2.x) * 0.5;
        yc = m1 * (xc - mx1) + my1;
    } else {
        m1 = 0.0 - (pc2.x - pc1.x) / (pc2.y - pc1.y);
        m2 = 0.0 - (pc3.x - pc2.x) / (pc3.y - pc2.y);
        mx1 = (pc1.x + pc2.x) * 0.5;
        mx2 = (pc2.x + pc3.x) * 0.5;
        my1 = (pc1.y + pc2.y) * 0.5;
        my2 = (pc2.y + pc3.y) * 0.5;
        xc = (m1 * mx1 - m2 * mx2 + my2 - my1) / (m1 - m2);
        yc = m1 * (xc - mx1) + my1;
    }

    let rsqr = (pc2.x - xc).powf(2.0) + (pc2.y - yc).powf(2.0);
    let drsqr = (p.x - xc).powf(2.0) + (p.y - yc).powf(2.0);

    drsqr <= rsqr
}

#[cfg(test)]
mod test {
    use super::{Point, Triangle, triangulate};

    #[test]
    fn test_simple() {
        let mut points =
            vec![Point::new(10.0, 10.0), Point::new(25.0, 15.0), Point::new(15.0, 25.0)];

        let tris: Vec<Triangle> = triangulate(&mut points);

        assert_eq!(tris.len(), 1);
        assert_eq!(tris[..], [Triangle(1, 0, 2)][..]);
    }

    #[test]
    fn test_four_triangles() {
        let mut points = vec![Point::new(10.0, 10.0),
                              Point::new(25.0, 15.0),
                              Point::new(15.0, 25.0),
                              Point::new(30.0, 25.0),
                              Point::new(40.0, 15.0)];

        let tris: Vec<Triangle> = triangulate(&mut points);
        assert_eq!(tris.len(), 4);

        let expected_tris =
            [Triangle(1, 0, 2), Triangle(1, 2, 3), Triangle(0, 1, 4), Triangle(1, 3, 4)];
        assert_eq!(tris[..], expected_tris[..]);
    }

    #[test]
    fn test_overlapping() {
        let mut points =
            vec![Point::new(10.0, 10.0), Point::new(25.0, 15.0), Point::new(25.0, 15.0)];

        let tris: Vec<Triangle> = triangulate(&mut points);

        assert_eq!(tris.len(), 1);
        assert_eq!(tris[..], [Triangle(0, 1, 2)][..]);
    }
}
<commit_msg>And now with less bugs, and less print spam.<commit_after>// Copyright 2017 Samuel Loretan <tynril@gmail.com>

//! Triangulate a set of points

/// A two dimentional point.
#[derive(Clone, Copy, Debug)]
pub struct Point {
    x: f64,
    y: f64,
}

impl Point {
    /// Makes a new point from xy coordinates.
    pub fn new(x: f64, y: f64) -> Point {
        Point { x: x, y: y }
    }
}

/// A triangle, represented by indexes into a vertice list.
#[derive(Debug, PartialEq)]
pub struct Triangle(usize, usize, usize);

/// An edge, represented by indexes into a vertice list.
#[derive(Debug)]
struct Edge(usize, usize);

impl PartialEq for Edge {
    fn eq(&self, other: &Edge) -> bool {
        (self.0 == other.1 && self.1 == other.0) || (self.0 == other.0 && self.1 == other.1)
    }
}

/// Triangulate a given set of points. The returned triangles are indices into the  list of points.
pub fn triangulate(points: &mut Vec<Point>) -> Vec<Triangle> {
    // Make sure we have enough points to do a triangulation.
    let points_count = points.len();
    if points_count < 3 {
        panic!("Can't triangulate less than three points.")
    }
    let max_triangles = points_count * 4;

    // Find the bounds of the space that contains our points.
    let mut min_point = points[0];
    let mut max_point = min_point;
    for point in points.iter() {
        if point.x > max_point.x {
            max_point.x = point.x;
        }
        if point.y > max_point.y {
            max_point.y = point.y;
        }
        if point.x < min_point.x {
            min_point.x = point.x;
        }
        if point.y < min_point.y {
            min_point.y = point.y;
        }
    }
    let delta_point = Point {
        x: max_point.x - min_point.x,
        y: max_point.y - min_point.y,
    };
    let delta_max = if delta_point.x > delta_point.y {
        delta_point.x
    } else {
        delta_point.y
    };
    let mid_point = Point {
        x: (max_point.x + min_point.x) * 0.5,
        y: (max_point.y + min_point.y) * 0.5,
    };

    // Compute the supertriangle, which encompasses all the input points.
    points.push(Point::new(mid_point.x - 2.0 * delta_max, mid_point.y - delta_max));
    points.push(Point::new(mid_point.x, mid_point.y + 2.0 * delta_max));
    points.push(Point::new(mid_point.x + 2.0 * delta_max, mid_point.y - delta_max));

    // The list of triangles we're gonna fill, initialized with the super-triangle.
    let mut triangles = vec![Triangle(points_count, points_count + 1, points_count + 2)];

    // Include each of the input point into the mesh.
    for i in 0..points_count {
        // Storage for the edges
        let mut edges = Vec::<Edge>::new();

        {
            let mut j = 0;
            while j < triangles.len() {
                if in_circle(points[i],
                             points[triangles[j].0],
                             points[triangles[j].1],
                             points[triangles[j].2]) {
                    edges.push(Edge(triangles[j].0, triangles[j].1));
                    edges.push(Edge(triangles[j].1, triangles[j].2));
                    edges.push(Edge(triangles[j].2, triangles[j].0));
                    triangles.remove(j);
                } else {
                    j += 1;
                }
            }
        }

        // Continue if we've exhausted the array.
        if i >= points_count {
            continue;
        }

        // Remove duplicate edges.

        if edges.len() > 1 {
            let mut j = edges.len() - 2;
            loop {
                let mut k = edges.len() - 1;
                while k >= j + 1 {
                    if edges[j] == edges[k] {
                        edges.remove(k);
                        edges.remove(j);
                        k -= 2;
                    } else {
                        k -= 1;
                    }
                }
                if j > 0 {
                    j -= 1;
                } else {
                    break;
                }
            }
        }

        // Form new triangles from the remaining edges. Edges are added in clockwise order.
        {
            let mut j = 0;
            while j < edges.len() {
                if triangles.len() >= max_triangles {
                    panic!("Exceeded maximum edges");
                }
                triangles.push(Triangle(edges[j].0, edges[j].1, i));
                j += 1;
            }
        }
    }

    // Remove triangles with supertriangle vertices
    let mut i = triangles.len() - 1;
    loop {
        if triangles[i].0 >= points_count || triangles[i].1 >= points_count ||
           triangles[i].2 >= points_count {
            triangles.remove(i);
        }
        if i > 0 {
            i -= 1;
        } else {
            break;
        }
    }

    // Remove the supertriangle vertices
    points.pop();
    points.pop();
    points.pop();

    triangles
}

/// Returns true if the first point lies inside (or on the edge of) the circumcircle made from the
/// three other points.
pub fn in_circle(p: Point, pc1: Point, pc2: Point, pc3: Point) -> bool {
    // Handle coincident points.
    if (pc1.y - pc2.y).abs() < std::f64::EPSILON && (pc2.y - pc3.y).abs() < std::f64::EPSILON {
        return false;
    }

    let m1: f64;
    let m2: f64;
    let mx1: f64;
    let mx2: f64;
    let my1: f64;
    let my2: f64;
    let xc: f64;
    let yc: f64;

    if (pc2.y - pc1.y).abs() < std::f64::EPSILON {
        m2 = 0.0 - (pc3.x - pc2.x) / (pc3.y - pc2.y);
        mx2 = (pc2.x + pc3.x) * 0.5;
        my2 = (pc2.y + pc3.y) * 0.5;
        xc = (pc2.x + pc1.x) * 0.5;
        yc = m2 * (xc - mx2) + my2;
    } else if (pc3.y - pc2.y).abs() < std::f64::EPSILON {
        m1 = 0.0 - (pc2.x - pc1.x) / (pc2.y - pc1.y);
        mx1 = (pc1.x + pc2.x) * 0.5;
        my1 = (pc1.y + pc2.y) * 0.5;
        xc = (pc3.x + pc2.x) * 0.5;
        yc = m1 * (xc - mx1) + my1;
    } else {
        m1 = 0.0 - (pc2.x - pc1.x) / (pc2.y - pc1.y);
        m2 = 0.0 - (pc3.x - pc2.x) / (pc3.y - pc2.y);
        mx1 = (pc1.x + pc2.x) * 0.5;
        mx2 = (pc2.x + pc3.x) * 0.5;
        my1 = (pc1.y + pc2.y) * 0.5;
        my2 = (pc2.y + pc3.y) * 0.5;
        xc = (m1 * mx1 - m2 * mx2 + my2 - my1) / (m1 - m2);
        yc = m1 * (xc - mx1) + my1;
    }

    let rsqr = (pc2.x - xc).powf(2.0) + (pc2.y - yc).powf(2.0);
    let drsqr = (p.x - xc).powf(2.0) + (p.y - yc).powf(2.0);

    drsqr <= rsqr
}

#[cfg(test)]
mod test {
    use super::{Point, Triangle, triangulate};

    #[test]
    fn test_simple() {
        let mut points =
            vec![Point::new(10.0, 10.0), Point::new(25.0, 15.0), Point::new(15.0, 25.0)];

        let tris: Vec<Triangle> = triangulate(&mut points);

        assert_eq!(tris.len(), 1);
        assert_eq!(tris[..], [Triangle(1, 0, 2)][..]);
    }

    #[test]
    fn test_four_triangles() {
        let mut points = vec![Point::new(10.0, 10.0),
                              Point::new(25.0, 15.0),
                              Point::new(15.0, 25.0),
                              Point::new(30.0, 25.0),
                              Point::new(40.0, 15.0)];

        let tris: Vec<Triangle> = triangulate(&mut points);
        assert_eq!(tris.len(), 4);

        let expected_tris =
            [Triangle(1, 0, 2), Triangle(1, 2, 3), Triangle(0, 1, 4), Triangle(1, 3, 4)];
        assert_eq!(tris[..], expected_tris[..]);
    }

    #[test]
    fn test_overlapping() {
        let mut points =
            vec![Point::new(10.0, 10.0), Point::new(25.0, 15.0), Point::new(25.0, 15.0)];

        let tris: Vec<Triangle> = triangulate(&mut points);

        assert_eq!(tris.len(), 1);
        assert_eq!(tris[..], [Triangle(0, 1, 2)][..]);
    }

    #[test]
    fn test_complex() {
        let mut points = vec![Point::new(601.0, 535.0),
                              Point::new(895.0, 666.0),
                              Point::new(876.0, 110.0),
                              Point::new(448.0, 36.0),
                              Point::new(829.0, 512.0),
                              Point::new(742.0, 363.0),
                              Point::new(267.0, 152.0),
                              Point::new(331.0, 244.0),
                              Point::new(623.0, 335.0),
                              Point::new(245.0, 119.0),
                              Point::new(104.0, 522.0),
                              Point::new(285.0, 561.0),
                              Point::new(282.0, 17.0),
                              Point::new(836.0, 20.0),
                              Point::new(667.0, 462.0),
                              Point::new(65.0, 216.0),
                              Point::new(839.0, 178.0),
                              Point::new(11.0, 264.0),
                              Point::new(181.0, 479.0),
                              Point::new(168.0, 90.0),
                              Point::new(348.0, 504.0),
                              Point::new(688.0, 605.0),
                              Point::new(329.0, 432.0),
                              Point::new(627.0, 461.0),
                              Point::new(450.0, 514.0)];

        let tris: Vec<Triangle> = triangulate(&mut points);

        assert_eq!(tris.len(), 39);
        // assert_eq!(tris[..], [Triangle(0, 1, 2)][..]);
    }
}
<|endoftext|>


<commit_before>
extern crate crc16;
extern crate time;
extern crate serial;

mod stub;
mod protocol;

use std::io::prelude::*;
use std::io;
use time::Duration;
use serial::prelude::*;
use std::rc::Rc;
use std::cell::RefCell;
use std::collections::BTreeMap;

pub use protocol::ProtocolSnoop;

struct PlugwiseInner<'a, I> {
    protocol: Rc<RefCell<protocol::Protocol<'a, I>>>
}

struct CircleInner<'a, I> {
    protocol: Rc<RefCell<protocol::Protocol<'a, I>>>,
    mac: u64,
    calibration_data: protocol::ResCalibration
}

impl<'a, I: Read+Write+'a> PlugwiseInner<'a, I> {
    fn initialize(port: I) -> io::Result<PlugwiseInner<'a, I>> {
        let plugwise = PlugwiseInner {
            protocol: Rc::new(RefCell::new(protocol::Protocol::new(port)))
        };

        let result = try!(plugwise.protocol.borrow_mut().initialize());

        if !result.is_online {
            return Err(io::Error::new(io::ErrorKind::Other, "not online"));
        }

        Ok(plugwise)
    }
}

pub trait Plugwise<'a> {
    fn create_circle(&self, mac: u64) -> io::Result<Box<Circle + 'a>>;
    fn set_snoop(&self, snoop: ProtocolSnoop<'a>);
}

pub trait Circle {
    fn switch_on(&self) -> io::Result<()>;
    fn switch_off(&self) -> io::Result<()>;
    fn is_switched_on(&self) -> io::Result<bool>;
    fn get_actual_watt_usage(&self) -> io::Result<f64>;
    fn get_clock(&self) -> io::Result<time::Tm>;
    fn set_clock(&self, tm: time::Tm) -> io::Result<()>;
    fn get_power_buffer(&self, max_entries: Option<u32>) -> io::Result<BTreeMap<time::Timespec, f64>>;
}

impl<'a, I:Read+Write+'a> Plugwise<'a> for PlugwiseInner<'a, I> {
    fn create_circle(&self, mac: u64) -> io::Result<Box<Circle+ 'a>> {
        let calibration_data = try!(self.protocol.borrow_mut().calibrate(mac));
        Ok(Box::new(CircleInner {
            protocol: self.protocol.clone(),
            mac: mac,
            calibration_data: calibration_data
        }))
    }

    fn set_snoop(&self, snoop: ProtocolSnoop<'a>) {
        self.protocol.borrow_mut().set_snoop(snoop);
    }
}

impl<'a, I:Read+Write+'a> Circle for CircleInner<'a, I> {
    fn switch_on(&self) -> io::Result<()> {
        try!(self.protocol.borrow_mut().switch(self.mac, true));
        Ok(())
    }

    fn switch_off(&self) -> io::Result<()> {
        try!(self.protocol.borrow_mut().switch(self.mac, false));
        Ok(())
    }

    fn is_switched_on(&self) -> io::Result<bool> {
        let info = try!(self.protocol.borrow_mut().get_info(self.mac));
        Ok(info.relay_state)
    }

    fn get_actual_watt_usage(&self) -> io::Result<f64> {
        let power_usage = try!(self.protocol.borrow_mut().get_power_usage(self.mac));
        Ok(power_usage.pulse_8s.to_watts(self.calibration_data))
    }

    fn get_clock(&self) -> io::Result<time::Tm> {
        let info = try!(self.protocol.borrow_mut().get_info(self.mac));
        let clock = try!(self.protocol.borrow_mut().get_clock_info(self.mac));

        let mut tm = match info.datetime.to_tm() {
            Some(tm) => tm,
            None => return Err(io::Error::new(io::ErrorKind::Other, "circle returns an invalid timestamp"))
        };
        tm.tm_sec = clock.second as i32;
        tm.tm_min = clock.minute as i32;
        tm.tm_hour = clock.hour as i32;
        tm.tm_wday = (clock.day_of_week % 7) as i32;
        Ok(tm)
    }

    fn set_clock(&self, tm: time::Tm) -> io::Result<()> {
        let clock_set = protocol::ReqClockSet::new_from_tm(tm);
        try!(self.protocol.borrow_mut().set_clock(self.mac, clock_set));
        Ok(())
    }

    fn get_power_buffer(&self, max_entries: Option<u32>) -> io::Result<BTreeMap<time::Timespec, f64>> {
        let mut result = BTreeMap::<time::Timespec, f64>::new();
        let info = try!(self.protocol.borrow_mut().get_info(self.mac));
        let start = match max_entries {
            None => 0,
            Some(n) => {
                let n_of_calls = n / 4; // each power buffer request retrieves 4 power usage statics
                if info.last_logaddr > n_of_calls {
                    info.last_logaddr - n_of_calls
                } else {
                    0
                }
            }
        };

        for index in (start..(info.last_logaddr + 1)) {
            let buffer = try!(self.protocol.borrow_mut().get_power_buffer(self.mac, index));

            self.get_power_buffer_helper(&mut result, &buffer.datetime1, &buffer.pulses1);
            self.get_power_buffer_helper(&mut result, &buffer.datetime2, &buffer.pulses2);
            self.get_power_buffer_helper(&mut result, &buffer.datetime3, &buffer.pulses3);
            self.get_power_buffer_helper(&mut result, &buffer.datetime4, &buffer.pulses4);
        }

        Ok(result)
    }
}

impl <'a, I:Read+Write+'a>  CircleInner<'a, I> {
    fn get_power_buffer_helper(&self,
                               map:&mut BTreeMap<time::Timespec, f64>,
                               datetime: &protocol::DateTime,
                               pulses: &protocol::Pulses) {
        if let Some(tm) = datetime.to_tm() {
            let _ = map.insert(tm.to_timespec(), pulses.to_kwh(self.calibration_data));
        }
    }
}

pub fn plugwise_device<'a>(device: &str) -> io::Result<Box<Plugwise<'a>+ 'a>> {
    let mut port = try!(serial::open(device));
    try!(port.configure(|settings| {
        settings.set_baud_rate(serial::Baud115200);
        settings.set_char_size(serial::Bits8);
        settings.set_parity(serial::ParityNone);
        settings.set_stop_bits(serial::Stop1);
    }));

    port.set_timeout(Duration::milliseconds(1000));
    let plugwise = try!(PlugwiseInner::initialize(port));

    Ok(Box::new(plugwise))
}

pub fn plugwise_simulator<'a>() -> io::Result<Box<Plugwise<'a>+ 'a>> {
    let port = stub::Stub::new();

    let plugwise = try!(PlugwiseInner::initialize(port));

    Ok(Box::new(plugwise))
}

#[test]
fn smoke_external_stub() {
    let stub = plugwise_simulator().unwrap();
    let circle = stub.create_circle(0x01234567890ABCDEF).unwrap();
    circle.switch_on().unwrap();
    assert_eq!(circle.is_switched_on().unwrap(), true);
    circle.switch_off().unwrap();
    assert_eq!(circle.is_switched_on().unwrap(), false);
    circle.get_actual_watt_usage().unwrap();
    let tm = circle.get_clock().unwrap();
    circle.set_clock(tm).unwrap();
    circle.get_power_buffer(None);
}
<commit_msg>Fix warning in test<commit_after>
extern crate crc16;
extern crate time;
extern crate serial;

mod stub;
mod protocol;

use std::io::prelude::*;
use std::io;
use time::Duration;
use serial::prelude::*;
use std::rc::Rc;
use std::cell::RefCell;
use std::collections::BTreeMap;

pub use protocol::ProtocolSnoop;

struct PlugwiseInner<'a, I> {
    protocol: Rc<RefCell<protocol::Protocol<'a, I>>>
}

struct CircleInner<'a, I> {
    protocol: Rc<RefCell<protocol::Protocol<'a, I>>>,
    mac: u64,
    calibration_data: protocol::ResCalibration
}

impl<'a, I: Read+Write+'a> PlugwiseInner<'a, I> {
    fn initialize(port: I) -> io::Result<PlugwiseInner<'a, I>> {
        let plugwise = PlugwiseInner {
            protocol: Rc::new(RefCell::new(protocol::Protocol::new(port)))
        };

        let result = try!(plugwise.protocol.borrow_mut().initialize());

        if !result.is_online {
            return Err(io::Error::new(io::ErrorKind::Other, "not online"));
        }

        Ok(plugwise)
    }
}

pub trait Plugwise<'a> {
    fn create_circle(&self, mac: u64) -> io::Result<Box<Circle + 'a>>;
    fn set_snoop(&self, snoop: ProtocolSnoop<'a>);
}

pub trait Circle {
    fn switch_on(&self) -> io::Result<()>;
    fn switch_off(&self) -> io::Result<()>;
    fn is_switched_on(&self) -> io::Result<bool>;
    fn get_actual_watt_usage(&self) -> io::Result<f64>;
    fn get_clock(&self) -> io::Result<time::Tm>;
    fn set_clock(&self, tm: time::Tm) -> io::Result<()>;
    fn get_power_buffer(&self, max_entries: Option<u32>) -> io::Result<BTreeMap<time::Timespec, f64>>;
}

impl<'a, I:Read+Write+'a> Plugwise<'a> for PlugwiseInner<'a, I> {
    fn create_circle(&self, mac: u64) -> io::Result<Box<Circle+ 'a>> {
        let calibration_data = try!(self.protocol.borrow_mut().calibrate(mac));
        Ok(Box::new(CircleInner {
            protocol: self.protocol.clone(),
            mac: mac,
            calibration_data: calibration_data
        }))
    }

    fn set_snoop(&self, snoop: ProtocolSnoop<'a>) {
        self.protocol.borrow_mut().set_snoop(snoop);
    }
}

impl<'a, I:Read+Write+'a> Circle for CircleInner<'a, I> {
    fn switch_on(&self) -> io::Result<()> {
        try!(self.protocol.borrow_mut().switch(self.mac, true));
        Ok(())
    }

    fn switch_off(&self) -> io::Result<()> {
        try!(self.protocol.borrow_mut().switch(self.mac, false));
        Ok(())
    }

    fn is_switched_on(&self) -> io::Result<bool> {
        let info = try!(self.protocol.borrow_mut().get_info(self.mac));
        Ok(info.relay_state)
    }

    fn get_actual_watt_usage(&self) -> io::Result<f64> {
        let power_usage = try!(self.protocol.borrow_mut().get_power_usage(self.mac));
        Ok(power_usage.pulse_8s.to_watts(self.calibration_data))
    }

    fn get_clock(&self) -> io::Result<time::Tm> {
        let info = try!(self.protocol.borrow_mut().get_info(self.mac));
        let clock = try!(self.protocol.borrow_mut().get_clock_info(self.mac));

        let mut tm = match info.datetime.to_tm() {
            Some(tm) => tm,
            None => return Err(io::Error::new(io::ErrorKind::Other, "circle returns an invalid timestamp"))
        };
        tm.tm_sec = clock.second as i32;
        tm.tm_min = clock.minute as i32;
        tm.tm_hour = clock.hour as i32;
        tm.tm_wday = (clock.day_of_week % 7) as i32;
        Ok(tm)
    }

    fn set_clock(&self, tm: time::Tm) -> io::Result<()> {
        let clock_set = protocol::ReqClockSet::new_from_tm(tm);
        try!(self.protocol.borrow_mut().set_clock(self.mac, clock_set));
        Ok(())
    }

    fn get_power_buffer(&self, max_entries: Option<u32>) -> io::Result<BTreeMap<time::Timespec, f64>> {
        let mut result = BTreeMap::<time::Timespec, f64>::new();
        let info = try!(self.protocol.borrow_mut().get_info(self.mac));
        let start = match max_entries {
            None => 0,
            Some(n) => {
                let n_of_calls = n / 4; // each power buffer request retrieves 4 power usage statics
                if info.last_logaddr > n_of_calls {
                    info.last_logaddr - n_of_calls
                } else {
                    0
                }
            }
        };

        for index in (start..(info.last_logaddr + 1)) {
            let buffer = try!(self.protocol.borrow_mut().get_power_buffer(self.mac, index));

            self.get_power_buffer_helper(&mut result, &buffer.datetime1, &buffer.pulses1);
            self.get_power_buffer_helper(&mut result, &buffer.datetime2, &buffer.pulses2);
            self.get_power_buffer_helper(&mut result, &buffer.datetime3, &buffer.pulses3);
            self.get_power_buffer_helper(&mut result, &buffer.datetime4, &buffer.pulses4);
        }

        Ok(result)
    }
}

impl <'a, I:Read+Write+'a>  CircleInner<'a, I> {
    fn get_power_buffer_helper(&self,
                               map:&mut BTreeMap<time::Timespec, f64>,
                               datetime: &protocol::DateTime,
                               pulses: &protocol::Pulses) {
        if let Some(tm) = datetime.to_tm() {
            let _ = map.insert(tm.to_timespec(), pulses.to_kwh(self.calibration_data));
        }
    }
}

pub fn plugwise_device<'a>(device: &str) -> io::Result<Box<Plugwise<'a>+ 'a>> {
    let mut port = try!(serial::open(device));
    try!(port.configure(|settings| {
        settings.set_baud_rate(serial::Baud115200);
        settings.set_char_size(serial::Bits8);
        settings.set_parity(serial::ParityNone);
        settings.set_stop_bits(serial::Stop1);
    }));

    port.set_timeout(Duration::milliseconds(1000));
    let plugwise = try!(PlugwiseInner::initialize(port));

    Ok(Box::new(plugwise))
}

pub fn plugwise_simulator<'a>() -> io::Result<Box<Plugwise<'a>+ 'a>> {
    let port = stub::Stub::new();

    let plugwise = try!(PlugwiseInner::initialize(port));

    Ok(Box::new(plugwise))
}

#[test]
fn smoke_external_stub() {
    let stub = plugwise_simulator().unwrap();
    let circle = stub.create_circle(0x01234567890ABCDEF).unwrap();
    circle.switch_on().unwrap();
    assert_eq!(circle.is_switched_on().unwrap(), true);
    circle.switch_off().unwrap();
    assert_eq!(circle.is_switched_on().unwrap(), false);
    circle.get_actual_watt_usage().unwrap();
    let tm = circle.get_clock().unwrap();
    circle.set_clock(tm).unwrap();
    circle.get_power_buffer(None).unwrap();
}
<|endoftext|>


<commit_before>//! Bindings to LLVM's C API, version 3.7.
//!
//! Refer to the [LLVM documentation](http://llvm.org/docs/) for more
//! information.

#![allow(non_upper_case_globals)]

extern crate libc;

use self::prelude::*;

pub enum LLVMMemoryBuffer {}
pub enum LLVMContext {}
pub enum LLVMModule {}
pub enum LLVMType {}
pub enum LLVMValue {}
pub enum LLVMBasicBlock {}
pub enum LLVMBuilder {}
pub enum LLVMModuleProvider {}
pub enum LLVMPassManager {}
pub enum LLVMPassRegistry {}
pub enum LLVMUse {}
pub enum LLVMDiagnosticInfo {}
pub enum LLVMOpaqueAttributeRef {}

/// Core types used throughout LLVM.
///
/// In most cases you will want to `use llvm::prelude::*`.
pub mod prelude {
    pub type LLVMBool = ::libc::c_int;
    pub type LLVMMemoryBufferRef = *mut super::LLVMMemoryBuffer;
    pub type LLVMContextRef = *mut super::LLVMContext;
    pub type LLVMModuleRef = *mut super::LLVMModule;
    pub type LLVMTypeRef = *mut super::LLVMType;
    pub type LLVMValueRef = *mut super::LLVMValue;
    pub type LLVMBasicBlockRef = *mut super::LLVMBasicBlock;
    pub type LLVMBuilderRef = *mut super::LLVMBuilder;
    pub type LLVMModuleProviderRef = *mut super::LLVMModuleProvider;
    pub type LLVMPassManagerRef = *mut super::LLVMPassManager;
    pub type LLVMPassRegistryRef = *mut super::LLVMPassRegistry;
    pub type LLVMUseRef = *mut super::LLVMUse;
    pub type LLVMDiagnosticInfoRef = *mut super::LLVMDiagnosticInfo;
    pub type LLVMAttributeRef = *mut super::LLVMOpaqueAttributeRef;
}

pub mod analysis;
pub mod bit_reader;
pub mod bit_writer;
pub mod core;
pub mod disassembler;
pub mod error_handling;
pub mod execution_engine;
pub mod initialization;
pub mod ir_reader;
pub mod link_time_optimizer;
pub mod linker;
pub mod lto;
pub mod object;
pub mod orc;
pub mod target;
pub mod support;
pub mod target_machine;

pub mod transforms {
    pub mod ipo;
    pub mod pass_manager_builder;
    pub mod scalar;
    pub mod vectorize;
}

#[repr(C)]
pub enum LLVMOpcode {
    LLVMRet = 1,
    LLVMBr = 2,
    LLVMSwitch = 3,
    LLVMIndirectBr = 4,
    LLVMInvoke = 5,
    LLVMUnreachable = 7,
    LLVMAdd = 8,
    LLVMFAdd = 9,
    LLVMSub = 10,
    LLVMFSub = 11,
    LLVMMul = 12,
    LLVMFMul = 13,
    LLVMUDiv = 14,
    LLVMSDiv = 15,
    LLVMFDiv = 16,
    LLVMURem = 17,
    LLVMSRem = 18,
    LLVMFRem = 19,
    LLVMShl = 20,
    LLVMLShr = 21,
    LLVMAShr = 22,
    LLVMAnd = 23,
    LLVMOr = 24,
    LLVMXor = 25,
    LLVMAlloca = 26,
    LLVMLoad = 27,
    LLVMStore = 28,
    LLVMGetElementPtr = 29,
    LLVMTrunc = 30,
    LLVMZExt = 31,
    LLVMSExt = 32,
    LLVMFPToUI = 33,
    LLVMFPToSI = 34,
    LLVMUIToFP = 35,
    LLVMSIToFP = 36,
    LLVMFPTrunc = 37,
    LLVMFPExt = 38,
    LLVMPtrToInt = 39,
    LLVMIntToPtr = 40,
    LLVMBitCast = 41,
    LLVMAddrSpaceCast = 60,
    LLVMICmp = 42,
    LLVMFCmp = 43,
    LLVMPHI = 44,
    LLVMCall = 45,
    LLVMSelect = 46,
    LLVMUserOp1 = 47,
    LLVMUserOp2 = 48,
    LLVMVAArg = 49,
    LLVMExtractElement = 50,
    LLVMInsertElement = 51,
    LLVMShuffleVector = 52,
    LLVMExtractValue = 53,
    LLVMInsertValue = 54,
    LLVMFence = 55,
    LLVMAtomicCmpXchg = 56,
    LLVMAtomicRMW = 57,
    LLVMResume = 58,
    LLVMLandingPad = 59,
    LLVMCleanupRet = 61,
    LLVMCatchRet = 62,
    LLVMCatchPad = 63,
    LLVMCleanupPad = 64,
    LLVMCatchSwitch = 65,
}

#[repr(C)]
pub enum LLVMTypeKind {
    LLVMVoidTypeKind = 0,
    LLVMHalfTypeKind = 1,
    LLVMFloatTypeKind = 2,
    LLVMDoubleTypeKind = 3,
    LLVMX86_FP80TypeKind = 4,
    LLVMFP128TypeKind = 5,
    LLVMPPC_FP128TypeKind = 6,
    LLVMLabelTypeKind = 7,
    LLVMIntegerTypeKind = 8,
    LLVMFunctionTypeKind = 9,
    LLVMStructTypeKind = 10,
    LLVMArrayTypeKind = 11,
    LLVMPointerTypeKind = 12,
    LLVMVectorTypeKind = 13,
    LLVMMetadataTypeKind = 14,
    LLVMX86_MMXTypeKind = 15,
    LLVMTokenTypeKind = 16,
}

#[repr(C)]
pub enum LLVMLinkage {
    LLVMExternalLinkage = 0,
    LLVMAvailableExternallyLinkage = 1,
    LLVMLinkOnceAnyLinkage = 2,
    LLVMLinkOnceODRLinkage = 3,
    LLVMLinkOnceODRAutoHideLinkage = 4,
    LLVMWeakAnyLinkage = 5,
    LLVMWeakODRLinkage = 6,
    LLVMAppendingLinkage = 7,
    LLVMInternalLinkage = 8,
    LLVMPrivateLinkage = 9,
    LLVMDLLImportLinkage = 10,
    LLVMDLLExportLinkage = 11,
    LLVMExternalWeakLinkage = 12,
    LLVMGhostLinkage = 13,
    LLVMCommonLinkage = 14,
    LLVMLinkerPrivateLinkage = 15,
    LLVMLinkerPrivateWeakLinkage = 16,
}

#[repr(C)]
pub enum LLVMVisibility {
    LLVMDefaultVisibility = 0,
    LLVMHiddenVisibility = 1,
    LLVMProtectedVisibility = 2,
}

#[repr(C)]
pub enum LLVMDLLStorageClass {
    LLVMDefaultStorageClass = 0,
    LLVMDLLImportStorageClass = 1,
    LLVMDLLExportStorageClass = 2,
}

#[repr(C)]
pub enum LLVMCallConv {
    LLVMCCallConv = 0,
    LLVMFastCallConv = 8,
    LLVMColdCallConv = 9,
    LLVMWebKitJSCallConv = 12,
    LLVMAnyRegCallConv = 13,
    LLVMX86StdcallCallConv = 64,
    LLVMX86FastcallCallConv = 65,
}

#[repr(C)]
pub enum LLVMValueKind {
    LLVMArgumentValueKind,
    LLVMBasicBlockValueKind,
    LLVMMemoryUseValueKind,
    LLVMMemoryDefValueKind,
    LLVMMemoryPhiValueKind,

    LLVMFunctionValueKind,
    LLVMGlobalAliasValueKind,
    LLVMGlobalIFuncValueKind,
    LLVMGlobalVariableValueKind,
    LLVMBlockAddressValueKind,
    LLVMConstantExprValueKind,
    LLVMConstantArrayValueKind,
    LLVMConstantStructValueKind,
    LLVMConstantVectorValueKind,
    LLVMUndefValueValueKind,
    LLVMConstantAggregateZeroValueKind,
    LLVMConstantDataArrayValueKind,
    LLVMConstantDataVectorValueKind,
    LLVMConstantIntValueKind,
    LLVMConstantFPValueKind,
    LLVMConstantPointerNullValueKind,
    LLVMConstantTokenNoneValueKind,

    LLVMMetadataAsValueValueKind,
    LLVMInlineAsmValueKind,

    LLVMInstructionValueKind,
}

#[repr(C)]
pub enum LLVMIntPredicate {
    LLVMIntEQ = 32,
    LLVMIntNE = 33,
    LLVMIntUGT = 34,
    LLVMIntUGE = 35,
    LLVMIntULT = 36,
    LLVMIntULE = 37,
    LLVMIntSGT = 38,
    LLVMIntSGE = 39,
    LLVMIntSLT = 40,
    LLVMIntSLE = 41,
}

#[repr(C)]
pub enum LLVMRealPredicate {
    LLVMRealPredicateFalse = 0,
    LLVMRealOEQ = 1,
    LLVMRealOGT = 2,
    LLVMRealOGE = 3,
    LLVMRealOLT = 4,
    LLVMRealOLE = 5,
    LLVMRealONE = 6,
    LLVMRealORD = 7,
    LLVMRealUNO = 8,
    LLVMRealUEQ = 9,
    LLVMRealUGT = 10,
    LLVMRealUGE = 11,
    LLVMRealULT = 12,
    LLVMRealULE = 13,
    LLVMRealUNE = 14,
    LLVMRealPredicateTrue = 15,
}

#[repr(C)]
pub enum LLVMLandingPadClauseTy {
    LLVMLandingPadCatch = 0,
    LLVMLandingPadFilter = 1,
}

#[repr(C)]
pub enum LLVMThreadLocalMode {
    LLVMNotThreadLocal = 0,
    LLVMGeneralDynamicTLSModel = 1,
    LLVMLocalDynamicTLSModel = 2,
    LLVMInitialExecTLSModel = 3,
    LLVMLocalExecTLSModel = 4,
}

#[repr(C)]
pub enum LLVMAtomicOrdering {
    LLVMAtomicOrderingNotAtomic = 0,
    LLVMAtomicOrderingUnordered = 1,
    LLVMAtomicOrderingMonotonic = 2,
    LLVMAtomicOrderingAcquire = 4,
    LLVMAtomicOrderingRelease = 5,
    LLVMAtomicOrderingAcquireRelease = 6,
    LLVMAtomicOrderingSequentiallyConsistent = 7,
}

#[repr(C)]
pub enum LLVMAtomicRMWBinOp {
    LLVMAtomicRMWBinOpXchg = 0,
    LLVMAtomicRMWBinOpAdd = 1,
    LLVMAtomicRMWBinOpSub = 2,
    LLVMAtomicRMWBinOpAnd = 3,
    LLVMAtomicRMWBinOpNand = 4,
    LLVMAtomicRMWBinOpOr = 5,
    LLVMAtomicRMWBinOpXor = 6,
    LLVMAtomicRMWBinOpMax = 7,
    LLVMAtomicRMWBinOpMin = 8,
    LLVMAtomicRMWBinOpUMax = 9,
    LLVMAtomicRMWBinOpUMin = 10,
}

#[repr(C)]
pub enum LLVMDiagnosticSeverity {
    LLVMDSError = 0,
    LLVMDSWarning = 1,
    LLVMDSRemark = 2,
    LLVMDSNote = 3,
}

pub const LLVMAttributeReturnIndex: ::libc::c_uint = 0;
pub const LLVMAttributeFunctionIndex: ::libc::c_uint = !0; // -1
/// Either LLVMAttributeReturnIndex, LLVMAttributeFunctionIndex, or a parameter
/// number from 1 to N.
pub type LLVMAttributeIndex = ::libc::c_uint;

pub type LLVMDiagnosticHandler = extern "C" fn(arg1: LLVMDiagnosticInfoRef,
                                               arg2: *mut ::libc::c_void);
pub type LLVMYieldCallback = extern "C" fn(arg1: LLVMContextRef, arg2: *mut ::libc::c_void);
<commit_msg>Correct reported version in root docs.<commit_after>//! Bindings to LLVM's C API, version 4.0.
//!
//! Refer to the [LLVM documentation](http://llvm.org/docs/) for more
//! information.

#![allow(non_upper_case_globals)]

extern crate libc;

use self::prelude::*;

pub enum LLVMMemoryBuffer {}
pub enum LLVMContext {}
pub enum LLVMModule {}
pub enum LLVMType {}
pub enum LLVMValue {}
pub enum LLVMBasicBlock {}
pub enum LLVMBuilder {}
pub enum LLVMModuleProvider {}
pub enum LLVMPassManager {}
pub enum LLVMPassRegistry {}
pub enum LLVMUse {}
pub enum LLVMDiagnosticInfo {}
pub enum LLVMOpaqueAttributeRef {}

/// Core types used throughout LLVM.
///
/// In most cases you will want to `use llvm::prelude::*`.
pub mod prelude {
    pub type LLVMBool = ::libc::c_int;
    pub type LLVMMemoryBufferRef = *mut super::LLVMMemoryBuffer;
    pub type LLVMContextRef = *mut super::LLVMContext;
    pub type LLVMModuleRef = *mut super::LLVMModule;
    pub type LLVMTypeRef = *mut super::LLVMType;
    pub type LLVMValueRef = *mut super::LLVMValue;
    pub type LLVMBasicBlockRef = *mut super::LLVMBasicBlock;
    pub type LLVMBuilderRef = *mut super::LLVMBuilder;
    pub type LLVMModuleProviderRef = *mut super::LLVMModuleProvider;
    pub type LLVMPassManagerRef = *mut super::LLVMPassManager;
    pub type LLVMPassRegistryRef = *mut super::LLVMPassRegistry;
    pub type LLVMUseRef = *mut super::LLVMUse;
    pub type LLVMDiagnosticInfoRef = *mut super::LLVMDiagnosticInfo;
    pub type LLVMAttributeRef = *mut super::LLVMOpaqueAttributeRef;
}

pub mod analysis;
pub mod bit_reader;
pub mod bit_writer;
pub mod core;
pub mod disassembler;
pub mod error_handling;
pub mod execution_engine;
pub mod initialization;
pub mod ir_reader;
pub mod link_time_optimizer;
pub mod linker;
pub mod lto;
pub mod object;
pub mod orc;
pub mod target;
pub mod support;
pub mod target_machine;

pub mod transforms {
    pub mod ipo;
    pub mod pass_manager_builder;
    pub mod scalar;
    pub mod vectorize;
}

#[repr(C)]
pub enum LLVMOpcode {
    LLVMRet = 1,
    LLVMBr = 2,
    LLVMSwitch = 3,
    LLVMIndirectBr = 4,
    LLVMInvoke = 5,
    LLVMUnreachable = 7,
    LLVMAdd = 8,
    LLVMFAdd = 9,
    LLVMSub = 10,
    LLVMFSub = 11,
    LLVMMul = 12,
    LLVMFMul = 13,
    LLVMUDiv = 14,
    LLVMSDiv = 15,
    LLVMFDiv = 16,
    LLVMURem = 17,
    LLVMSRem = 18,
    LLVMFRem = 19,
    LLVMShl = 20,
    LLVMLShr = 21,
    LLVMAShr = 22,
    LLVMAnd = 23,
    LLVMOr = 24,
    LLVMXor = 25,
    LLVMAlloca = 26,
    LLVMLoad = 27,
    LLVMStore = 28,
    LLVMGetElementPtr = 29,
    LLVMTrunc = 30,
    LLVMZExt = 31,
    LLVMSExt = 32,
    LLVMFPToUI = 33,
    LLVMFPToSI = 34,
    LLVMUIToFP = 35,
    LLVMSIToFP = 36,
    LLVMFPTrunc = 37,
    LLVMFPExt = 38,
    LLVMPtrToInt = 39,
    LLVMIntToPtr = 40,
    LLVMBitCast = 41,
    LLVMAddrSpaceCast = 60,
    LLVMICmp = 42,
    LLVMFCmp = 43,
    LLVMPHI = 44,
    LLVMCall = 45,
    LLVMSelect = 46,
    LLVMUserOp1 = 47,
    LLVMUserOp2 = 48,
    LLVMVAArg = 49,
    LLVMExtractElement = 50,
    LLVMInsertElement = 51,
    LLVMShuffleVector = 52,
    LLVMExtractValue = 53,
    LLVMInsertValue = 54,
    LLVMFence = 55,
    LLVMAtomicCmpXchg = 56,
    LLVMAtomicRMW = 57,
    LLVMResume = 58,
    LLVMLandingPad = 59,
    LLVMCleanupRet = 61,
    LLVMCatchRet = 62,
    LLVMCatchPad = 63,
    LLVMCleanupPad = 64,
    LLVMCatchSwitch = 65,
}

#[repr(C)]
pub enum LLVMTypeKind {
    LLVMVoidTypeKind = 0,
    LLVMHalfTypeKind = 1,
    LLVMFloatTypeKind = 2,
    LLVMDoubleTypeKind = 3,
    LLVMX86_FP80TypeKind = 4,
    LLVMFP128TypeKind = 5,
    LLVMPPC_FP128TypeKind = 6,
    LLVMLabelTypeKind = 7,
    LLVMIntegerTypeKind = 8,
    LLVMFunctionTypeKind = 9,
    LLVMStructTypeKind = 10,
    LLVMArrayTypeKind = 11,
    LLVMPointerTypeKind = 12,
    LLVMVectorTypeKind = 13,
    LLVMMetadataTypeKind = 14,
    LLVMX86_MMXTypeKind = 15,
    LLVMTokenTypeKind = 16,
}

#[repr(C)]
pub enum LLVMLinkage {
    LLVMExternalLinkage = 0,
    LLVMAvailableExternallyLinkage = 1,
    LLVMLinkOnceAnyLinkage = 2,
    LLVMLinkOnceODRLinkage = 3,
    LLVMLinkOnceODRAutoHideLinkage = 4,
    LLVMWeakAnyLinkage = 5,
    LLVMWeakODRLinkage = 6,
    LLVMAppendingLinkage = 7,
    LLVMInternalLinkage = 8,
    LLVMPrivateLinkage = 9,
    LLVMDLLImportLinkage = 10,
    LLVMDLLExportLinkage = 11,
    LLVMExternalWeakLinkage = 12,
    LLVMGhostLinkage = 13,
    LLVMCommonLinkage = 14,
    LLVMLinkerPrivateLinkage = 15,
    LLVMLinkerPrivateWeakLinkage = 16,
}

#[repr(C)]
pub enum LLVMVisibility {
    LLVMDefaultVisibility = 0,
    LLVMHiddenVisibility = 1,
    LLVMProtectedVisibility = 2,
}

#[repr(C)]
pub enum LLVMDLLStorageClass {
    LLVMDefaultStorageClass = 0,
    LLVMDLLImportStorageClass = 1,
    LLVMDLLExportStorageClass = 2,
}

#[repr(C)]
pub enum LLVMCallConv {
    LLVMCCallConv = 0,
    LLVMFastCallConv = 8,
    LLVMColdCallConv = 9,
    LLVMWebKitJSCallConv = 12,
    LLVMAnyRegCallConv = 13,
    LLVMX86StdcallCallConv = 64,
    LLVMX86FastcallCallConv = 65,
}

#[repr(C)]
pub enum LLVMValueKind {
    LLVMArgumentValueKind,
    LLVMBasicBlockValueKind,
    LLVMMemoryUseValueKind,
    LLVMMemoryDefValueKind,
    LLVMMemoryPhiValueKind,

    LLVMFunctionValueKind,
    LLVMGlobalAliasValueKind,
    LLVMGlobalIFuncValueKind,
    LLVMGlobalVariableValueKind,
    LLVMBlockAddressValueKind,
    LLVMConstantExprValueKind,
    LLVMConstantArrayValueKind,
    LLVMConstantStructValueKind,
    LLVMConstantVectorValueKind,
    LLVMUndefValueValueKind,
    LLVMConstantAggregateZeroValueKind,
    LLVMConstantDataArrayValueKind,
    LLVMConstantDataVectorValueKind,
    LLVMConstantIntValueKind,
    LLVMConstantFPValueKind,
    LLVMConstantPointerNullValueKind,
    LLVMConstantTokenNoneValueKind,

    LLVMMetadataAsValueValueKind,
    LLVMInlineAsmValueKind,

    LLVMInstructionValueKind,
}

#[repr(C)]
pub enum LLVMIntPredicate {
    LLVMIntEQ = 32,
    LLVMIntNE = 33,
    LLVMIntUGT = 34,
    LLVMIntUGE = 35,
    LLVMIntULT = 36,
    LLVMIntULE = 37,
    LLVMIntSGT = 38,
    LLVMIntSGE = 39,
    LLVMIntSLT = 40,
    LLVMIntSLE = 41,
}

#[repr(C)]
pub enum LLVMRealPredicate {
    LLVMRealPredicateFalse = 0,
    LLVMRealOEQ = 1,
    LLVMRealOGT = 2,
    LLVMRealOGE = 3,
    LLVMRealOLT = 4,
    LLVMRealOLE = 5,
    LLVMRealONE = 6,
    LLVMRealORD = 7,
    LLVMRealUNO = 8,
    LLVMRealUEQ = 9,
    LLVMRealUGT = 10,
    LLVMRealUGE = 11,
    LLVMRealULT = 12,
    LLVMRealULE = 13,
    LLVMRealUNE = 14,
    LLVMRealPredicateTrue = 15,
}

#[repr(C)]
pub enum LLVMLandingPadClauseTy {
    LLVMLandingPadCatch = 0,
    LLVMLandingPadFilter = 1,
}

#[repr(C)]
pub enum LLVMThreadLocalMode {
    LLVMNotThreadLocal = 0,
    LLVMGeneralDynamicTLSModel = 1,
    LLVMLocalDynamicTLSModel = 2,
    LLVMInitialExecTLSModel = 3,
    LLVMLocalExecTLSModel = 4,
}

#[repr(C)]
pub enum LLVMAtomicOrdering {
    LLVMAtomicOrderingNotAtomic = 0,
    LLVMAtomicOrderingUnordered = 1,
    LLVMAtomicOrderingMonotonic = 2,
    LLVMAtomicOrderingAcquire = 4,
    LLVMAtomicOrderingRelease = 5,
    LLVMAtomicOrderingAcquireRelease = 6,
    LLVMAtomicOrderingSequentiallyConsistent = 7,
}

#[repr(C)]
pub enum LLVMAtomicRMWBinOp {
    LLVMAtomicRMWBinOpXchg = 0,
    LLVMAtomicRMWBinOpAdd = 1,
    LLVMAtomicRMWBinOpSub = 2,
    LLVMAtomicRMWBinOpAnd = 3,
    LLVMAtomicRMWBinOpNand = 4,
    LLVMAtomicRMWBinOpOr = 5,
    LLVMAtomicRMWBinOpXor = 6,
    LLVMAtomicRMWBinOpMax = 7,
    LLVMAtomicRMWBinOpMin = 8,
    LLVMAtomicRMWBinOpUMax = 9,
    LLVMAtomicRMWBinOpUMin = 10,
}

#[repr(C)]
pub enum LLVMDiagnosticSeverity {
    LLVMDSError = 0,
    LLVMDSWarning = 1,
    LLVMDSRemark = 2,
    LLVMDSNote = 3,
}

pub const LLVMAttributeReturnIndex: ::libc::c_uint = 0;
pub const LLVMAttributeFunctionIndex: ::libc::c_uint = !0; // -1
/// Either LLVMAttributeReturnIndex, LLVMAttributeFunctionIndex, or a parameter
/// number from 1 to N.
pub type LLVMAttributeIndex = ::libc::c_uint;

pub type LLVMDiagnosticHandler = extern "C" fn(arg1: LLVMDiagnosticInfoRef,
                                               arg2: *mut ::libc::c_void);
pub type LLVMYieldCallback = extern "C" fn(arg1: LLVMContextRef, arg2: *mut ::libc::c_void);
<|endoftext|>


<commit_before>//! Bindings to [libFuzzer](http://llvm.org/docs/LibFuzzer.html): a runtime for
//! coverage-guided fuzzing.
//!
//! See [the `cargo-fuzz`
//! guide](https://rust-fuzz.github.io/book/cargo-fuzz.html) for a usage
//! tutorial.
//!
//! The main export of this crate is [the `fuzz_target!`
//! macro](./macro.fuzz_target.html), which allows you to define targets for
//! libFuzzer to exercise.

#![deny(missing_docs, missing_debug_implementations)]

pub use arbitrary;

extern "C" {
    // We do not actually cross the FFI bound here.
    #[allow(improper_ctypes)]
    fn rust_fuzzer_test_input(input: &[u8]);
}

#[doc(hidden)]
#[export_name = "LLVMFuzzerTestOneInput"]
pub fn test_input_wrap(data: *const u8, size: usize) -> i32 {
    let test_input = ::std::panic::catch_unwind(|| unsafe {
        let data_slice = ::std::slice::from_raw_parts(data, size);
        rust_fuzzer_test_input(data_slice);
    });
    if test_input.err().is_some() {
        // hopefully the custom panic hook will be called before and abort the
        // process before the stack frames are unwinded.
        ::std::process::abort();
    }
    0
}

#[doc(hidden)]
#[export_name = "LLVMFuzzerInitialize"]
pub fn initialize(_argc: *const isize, _argv: *const *const *const u8) -> isize {
    // Registers a panic hook that aborts the process before unwinding.
    // It is useful to abort before unwinding so that the fuzzer will then be
    // able to analyse the process stack frames to tell different bugs appart.
    //
    // HACK / FIXME: it would be better to use `-C panic=abort` but it's currently
    // impossible to build code using compiler plugins with this flag.
    // We will be able to remove this code when
    // https://github.com/rust-lang/cargo/issues/5423 is fixed.
    let default_hook = ::std::panic::take_hook();
    ::std::panic::set_hook(Box::new(move |panic_info| {
        default_hook(panic_info);
        ::std::process::abort();
    }));
    0
}

/// Define a fuzz target.
///
/// ## Example
///
/// This example takes a `&[u8]` slice and attempts to parse it. The parsing
/// might fail and return an `Err`, but it shouldn't ever panic or segfault.
///
/// ```no_run
/// #![no_main]
///
/// use libfuzzer_sys::fuzz_target;
///
/// // Note: `|input|` is short for `|input: &[u8]|`.
/// fuzz_target!(|input| {
///     let _result: Result<_, _> = my_crate::parse(input);
/// });
/// # mod my_crate { pub fn parse(_: &[u8]) -> Result<(), ()> { unimplemented!() } }
/// ```
///
/// ## Arbitrary Input Types
///
/// The input is a `&[u8]` slice by default, but you can take arbitrary input
/// types, as long as the type implements [the `arbitrary` crate's `Arbitrary`
/// trait](https://docs.rs/arbitrary/*/arbitrary/trait.Arbitrary.html) (which is
/// also re-exported as `libfuzzer_sys::arbitrary::Arbitrary` for convenience).
///
/// For example, if you wanted to take an arbitrary RGB color, you could do the
/// following:
///
/// ```no_run
/// #![no_main]
///
/// use libfuzzer_sys::{arbitrary::{Arbitrary, Unstructured}, fuzz_target};
///
/// #[derive(Debug)]
/// pub struct Rgb {
///     r: u8,
///     g: u8,
///     b: u8,
/// }
///
/// impl Arbitrary for Rgb {
///     fn arbitrary<U>(raw: &mut U) -> Result<Self, U::Error>
///     where
///         U: Unstructured + ?Sized
///     {
///         let mut buf = [0; 3];
///         raw.fill_buffer(&mut buf)?;
///         let r = buf[0];
///         let g = buf[1];
///         let b = buf[2];
///         Ok(Rgb { r, g, b })
///     }
/// }
///
/// // Write a fuzz target that works with RGB colors instead of raw bytes.
/// fuzz_target!(|color: Rgb| {
///     my_crate::convert_color(color);
/// });
/// # mod my_crate { fn convert_color(_: super::Rgb) {} }
#[macro_export]
macro_rules! fuzz_target {
    (|$bytes:ident| $body:block) => {
        /// Auto-generated function
        #[no_mangle]
        pub extern "C" fn rust_fuzzer_test_input($bytes: &[u8]) {
            // When `RUST_LIBFUZZER_DEBUG_PATH` is set, write the debug
            // formatting of the input to that file. This is only intended for
            // `cargo fuzz`'s use!
            if let Ok(path) = std::env::var("RUST_LIBFUZZER_DEBUG_PATH") {
                use std::io::Write;
                let mut file = std::fs::File::create(path)
                    .expect("failed to create `RUST_LIBFUZZER_DEBUG_PATH` file");
                writeln!(&mut file, "{:?}", $bytes)
                    .expect("failed to write to `RUST_LIBFUZZER_DEBUG_PATH` file");
                return;
            }

            $body
        }
    };

    (|$data:ident: &[u8]| $body:block) => {
        fuzz_target!(|$data| $body);
    };

    (|$data:ident: $dty: ty| $body:block) => {
        /// Auto-generated function
        #[no_mangle]
        pub extern "C" fn rust_fuzzer_test_input(bytes: &[u8]) {
            use libfuzzer_sys::arbitrary::{Arbitrary, Unstructured};

            // Early exit if we don't have enough bytes for the `Arbitrary`
            // implementation. This helps the fuzzer avoid exploring all the
            // different not-enough-input-bytes paths inside the `Arbitrary`
            // implementation. Additionally, it exits faster, letting the fuzzer
            // get to longer inputs that actually lead to interesting executions
            // quicker.
            if bytes.len() < <$dty as Arbitrary>::size_hint(0).0 {
                return;
            }

            let mut u = Unstructured::new(bytes);
            let data = <$dty as Arbitrary>::arbitrary_take_rest(u);

            // When `RUST_LIBFUZZER_DEBUG_PATH` is set, write the debug
            // formatting of the input to that file. This is only intended for
            // `cargo fuzz`'s use!
            if let Ok(path) = std::env::var("RUST_LIBFUZZER_DEBUG_PATH") {
                use std::io::Write;
                let mut file = std::fs::File::create(path)
                    .expect("failed to create `RUST_LIBFUZZER_DEBUG_PATH` file");
                (match data {
                    Ok(data) => writeln!(&mut file, "{:#?}", data),
                    Err(err) => writeln!(&mut file, "Arbitrary Error: {}", err),
                })
                .expect("failed to write to `RUST_LIBFUZZER_DEBUG_PATH` file");
                return;
            }

            let $data = match data {
                Ok(d) => d,
                Err(_) => return,
            };

            $body
        }
    };
}
<commit_msg>Update the `arbitrary` example in the docs for `arbitrary` 1.0<commit_after>//! Bindings to [libFuzzer](http://llvm.org/docs/LibFuzzer.html): a runtime for
//! coverage-guided fuzzing.
//!
//! See [the `cargo-fuzz`
//! guide](https://rust-fuzz.github.io/book/cargo-fuzz.html) for a usage
//! tutorial.
//!
//! The main export of this crate is [the `fuzz_target!`
//! macro](./macro.fuzz_target.html), which allows you to define targets for
//! libFuzzer to exercise.

#![deny(missing_docs, missing_debug_implementations)]

pub use arbitrary;

extern "C" {
    // We do not actually cross the FFI bound here.
    #[allow(improper_ctypes)]
    fn rust_fuzzer_test_input(input: &[u8]);
}

#[doc(hidden)]
#[export_name = "LLVMFuzzerTestOneInput"]
pub fn test_input_wrap(data: *const u8, size: usize) -> i32 {
    let test_input = ::std::panic::catch_unwind(|| unsafe {
        let data_slice = ::std::slice::from_raw_parts(data, size);
        rust_fuzzer_test_input(data_slice);
    });
    if test_input.err().is_some() {
        // hopefully the custom panic hook will be called before and abort the
        // process before the stack frames are unwinded.
        ::std::process::abort();
    }
    0
}

#[doc(hidden)]
#[export_name = "LLVMFuzzerInitialize"]
pub fn initialize(_argc: *const isize, _argv: *const *const *const u8) -> isize {
    // Registers a panic hook that aborts the process before unwinding.
    // It is useful to abort before unwinding so that the fuzzer will then be
    // able to analyse the process stack frames to tell different bugs appart.
    //
    // HACK / FIXME: it would be better to use `-C panic=abort` but it's currently
    // impossible to build code using compiler plugins with this flag.
    // We will be able to remove this code when
    // https://github.com/rust-lang/cargo/issues/5423 is fixed.
    let default_hook = ::std::panic::take_hook();
    ::std::panic::set_hook(Box::new(move |panic_info| {
        default_hook(panic_info);
        ::std::process::abort();
    }));
    0
}

/// Define a fuzz target.
///
/// ## Example
///
/// This example takes a `&[u8]` slice and attempts to parse it. The parsing
/// might fail and return an `Err`, but it shouldn't ever panic or segfault.
///
/// ```no_run
/// #![no_main]
///
/// use libfuzzer_sys::fuzz_target;
///
/// // Note: `|input|` is short for `|input: &[u8]|`.
/// fuzz_target!(|input| {
///     let _result: Result<_, _> = my_crate::parse(input);
/// });
/// # mod my_crate { pub fn parse(_: &[u8]) -> Result<(), ()> { unimplemented!() } }
/// ```
///
/// ## Arbitrary Input Types
///
/// The input is a `&[u8]` slice by default, but you can take arbitrary input
/// types, as long as the type implements [the `arbitrary` crate's `Arbitrary`
/// trait](https://docs.rs/arbitrary/*/arbitrary/trait.Arbitrary.html) (which is
/// also re-exported as `libfuzzer_sys::arbitrary::Arbitrary` for convenience).
///
/// For example, if you wanted to take an arbitrary RGB color, you could do the
/// following:
///
/// ```no_run
/// #![no_main]
/// # mod foo {
///
/// use libfuzzer_sys::{arbitrary::{Arbitrary, Error, Unstructured}, fuzz_target};
///
/// #[derive(Debug)]
/// pub struct Rgb {
///     r: u8,
///     g: u8,
///     b: u8,
/// }
///
/// impl<'a> Arbitrary<'a> for Rgb {
///     fn arbitrary(raw: &mut Unstructured<'a>) -> Result<Self, Error> {
///         let mut buf = [0; 3];
///         raw.fill_buffer(&mut buf)?;
///         let r = buf[0];
///         let g = buf[1];
///         let b = buf[2];
///         Ok(Rgb { r, g, b })
///     }
/// }
///
/// // Write a fuzz target that works with RGB colors instead of raw bytes.
/// fuzz_target!(|color: Rgb| {
///     my_crate::convert_color(color);
/// });
/// # mod my_crate {
/// #     use super::Rgb;
/// #     pub fn convert_color(_: Rgb) {}
/// # }
/// # }
/// ```
#[macro_export]
macro_rules! fuzz_target {
    (|$bytes:ident| $body:block) => {
        /// Auto-generated function
        #[no_mangle]
        pub extern "C" fn rust_fuzzer_test_input($bytes: &[u8]) {
            // When `RUST_LIBFUZZER_DEBUG_PATH` is set, write the debug
            // formatting of the input to that file. This is only intended for
            // `cargo fuzz`'s use!
            if let Ok(path) = std::env::var("RUST_LIBFUZZER_DEBUG_PATH") {
                use std::io::Write;
                let mut file = std::fs::File::create(path)
                    .expect("failed to create `RUST_LIBFUZZER_DEBUG_PATH` file");
                writeln!(&mut file, "{:?}", $bytes)
                    .expect("failed to write to `RUST_LIBFUZZER_DEBUG_PATH` file");
                return;
            }

            $body
        }
    };

    (|$data:ident: &[u8]| $body:block) => {
        fuzz_target!(|$data| $body);
    };

    (|$data:ident: $dty: ty| $body:block) => {
        /// Auto-generated function
        #[no_mangle]
        pub extern "C" fn rust_fuzzer_test_input(bytes: &[u8]) {
            use libfuzzer_sys::arbitrary::{Arbitrary, Unstructured};

            // Early exit if we don't have enough bytes for the `Arbitrary`
            // implementation. This helps the fuzzer avoid exploring all the
            // different not-enough-input-bytes paths inside the `Arbitrary`
            // implementation. Additionally, it exits faster, letting the fuzzer
            // get to longer inputs that actually lead to interesting executions
            // quicker.
            if bytes.len() < <$dty as Arbitrary>::size_hint(0).0 {
                return;
            }

            let mut u = Unstructured::new(bytes);
            let data = <$dty as Arbitrary>::arbitrary_take_rest(u);

            // When `RUST_LIBFUZZER_DEBUG_PATH` is set, write the debug
            // formatting of the input to that file. This is only intended for
            // `cargo fuzz`'s use!
            if let Ok(path) = std::env::var("RUST_LIBFUZZER_DEBUG_PATH") {
                use std::io::Write;
                let mut file = std::fs::File::create(path)
                    .expect("failed to create `RUST_LIBFUZZER_DEBUG_PATH` file");
                (match data {
                    Ok(data) => writeln!(&mut file, "{:#?}", data),
                    Err(err) => writeln!(&mut file, "Arbitrary Error: {}", err),
                })
                .expect("failed to write to `RUST_LIBFUZZER_DEBUG_PATH` file");
                return;
            }

            let $data = match data {
                Ok(d) => d,
                Err(_) => return,
            };

            $body
        }
    };
}
<|endoftext|>


<commit_before>//! This crate parses and processes `hyper::server::Request` data
//! that contains `multipart/form-data` content.
//!
//! The main entry point is `parse_multipart`

extern crate hyper;
#[macro_use] extern crate mime;
extern crate httparse;
extern crate libc;
extern crate tempdir;
extern crate textnonce;

#[cfg(test)]
mod mock;
pub mod buf;
mod content_disposition;
pub mod error;

pub use error::Error;
pub use buf::BufReadPlus;

use std::path::PathBuf;
use std::fs::File;
use std::io::{BufReader,BufRead};

use hyper::server::Request;
use hyper::header::{Headers,ContentType};
use mime::{Mime,TopLevel,SubLevel,Attr,Value};
use tempdir::TempDir;
use textnonce::TextNonce;

pub use content_disposition::ContentDispositionFormData;

#[cfg(test)]
use std::net::SocketAddr;

/// This structure represents uploaded files which were received as
/// part of the `multipart/form-data` parsing.  They are streamed to
/// disk because they may not fit in memory.
pub struct UploadedFile {
    /// This is the temporary file where the data was saved.
    pub path: PathBuf,
    /// This is the filename that was specified in the data, unfiltered.  It may or may not
    /// be legal on the local filesystem.
    pub filename: Option<String>,
    /// This is the content-type that was specified in the data, unvalidated.
    pub content_type: Mime,
    /// This is the actual size of the file received
    pub size: usize,
}

/// This function parses and processes `hyper::server::Request` data
/// that contains `multipart/form-data` content.  It does this in a streaming
/// fashion, saving embedded uploaded files to disk as they are encountered by
/// the parser.
///
/// It returns two sets of data.  The first, `Vec<(String,String)>`, are the
/// variable-value pairs from the POST-ed form.  The second
/// `Vec<(String,UploadedFile)>` are the variable-file pairs from the POST-ed
/// form, where `UploadedFile` structures describe the uploaded file.
pub fn parse_multipart<'a,'b>(
    request: &mut Request<'a,'b>)
    -> Result< (Vec<(String,String)>, Vec<(String,UploadedFile)>), Error >
{
    let mut parameters: Vec<(String,String)> = Vec::new();
    let mut files: Vec<(String,UploadedFile)> = Vec::new();

    let string_boundary = try!( get_boundary(request) );
    println!("Boundary is {}", string_boundary);
    let boundary: Vec<u8> = string_boundary.into_bytes();
    let mut crlf_boundary: Vec<u8> = Vec::with_capacity(2 + boundary.len());
    crlf_boundary.extend(b"\r\n");
    crlf_boundary.extend(boundary.clone());

    // Request implements Read.  Internally it's a BufReader, but it's not
    // exposed that way.  We need to wrap it into a BufReader to get that
    // functionality ourselves.
    let mut r = BufReader::with_capacity(4096,request);

    enum State {
        Discarding, // Discard until after initial boundary and CRLF
        CapturingValue, // Capture value until boundary, then discard past CRLF
        CapturingFile, // Capture file until boundary, then discard past CRLF
        ReadingHeaders, // Capture headers to blank line
    }

    let mut state = State::Discarding;
    let mut headers = Headers::new();

    loop {
        let mut buf: Vec<u8> = Vec::new();

        match state {
            State::Discarding => {
                // Read up to and including the boundary
                let read = try!( r.stream_until_token( &*boundary, &mut buf ) );
                if read==0 { return Err(Error::Eof); }

                state = State::ReadingHeaders;
            },
            State::ReadingHeaders => {
                {
                    // IF the next two characters are '--' (look ahead), then we are finished
                    let peeker = try!( r.fill_buf() );
                    if peeker.len() >= 2 && &peeker[..2]==b"--" {
                        return Ok((parameters, files))
                    }
                } // drop peeker

                // Read up to and including the CRLF after the boundary
                let read = try!( r.stream_until_token( b"\r\n", &mut buf ) );
                if read==0 { return Err(Error::Eof); }

                buf.truncate(0);
                let read = try!( r.stream_until_token( b"\r\n\r\n", &mut buf ) );
                if read==0 { return Err(Error::Eof); }
                buf.extend(b"\r\n\r\n"); // parse_headers() needs this token at the end

                // Parse the headers
                let mut header_memory = [httparse::EMPTY_HEADER; 4];
                match httparse::parse_headers( &*buf, &mut header_memory) {
                    Ok(httparse::Status::Complete((_,raw_headers))) => {
                        // Turn raw headers into hyper headers
                        headers = try!( Headers::from_raw(raw_headers) );

                        let cd: &ContentDispositionFormData = match headers.get() {
                            Some(x) => x,
                            None => return Err(Error::MissingDisposition),
                        };
                        let ct: Option<&ContentType> = headers.get();

                        state = if ct.is_some() || cd.filename.is_some() {
                            State::CapturingFile
                        } else {
                            State::CapturingValue
                        };
                    },
                    Ok(httparse::Status::Partial) => {
                        println!("Header Parsing was Partial");
                        return Err(Error::ParseError);
                    },
                    Err(e) => return Err( From::from(e) ),
                }
            },
            State::CapturingValue => {
                buf.truncate(0);
                let _ = try!( r.stream_until_token( &*crlf_boundary, &mut buf ) );

                let cd: &ContentDispositionFormData = headers.get().unwrap();

                let key = match cd.name {
                    None => return Err(Error::NoName),
                    Some(ref name) => name.clone()
                };
                let value = try!( String::from_utf8(buf) );

                parameters.push( (key,value) );

                state = State::ReadingHeaders;
            },
            State::CapturingFile => {
                // Setup a file to capture the contents
                let mut temp = try!( TempDir::new("formdata") ).into_path();
                temp.push( TextNonce::sized_urlsafe(32).unwrap().into_string() );
                let mut tmpfile = try!( File::create(temp.clone()) );

                // Stream out the file
                let read = try!( r.stream_until_token( &*crlf_boundary, &mut tmpfile ) );

                let cd: &ContentDispositionFormData = headers.get().unwrap();
                let key = match cd.name {
                    None => return Err(Error::NoName),
                    Some(ref name) => name.clone()
                };
                // FIXME: handle content-type header, and default to text/plain if
                //        no content-type header.
                // FIXME: handle content-type: multipart/mixed as multiple files
                // FIXME: handle content-transfer-encoding

                let ufile = UploadedFile {
                    path: temp,
                    filename: cd.filename.clone(),
                    content_type: mime!(Text/Plain; Charset=Utf8), // FIXME
                    size: read - crlf_boundary.len()
                };
                files.push( (key,ufile) );

                state = State::ReadingHeaders;
            },
        }
    }
}

fn get_boundary<'a,'b>(request: &Request<'a,'b>) -> Result<String,Error>
{
    // Verify that the request is 'content-type: multipart/form-data'
    let content_type: &ContentType = match request.headers.get() {
        Some(h) => h,
        None => return Err(Error::NoRequestContentType),
    };
    let ContentType(ref mime) = *content_type;
    let Mime(ref top_level, ref sub_level, ref params) = *mime;
    if *top_level != TopLevel::Multipart {
        return Err(Error::NotMultipart);
    }
    if *sub_level != SubLevel::FormData {
        return Err(Error::NotFormData);
    }

    // Get the boundary token
    for &(ref attr,ref value) in params.iter() {
        match (attr,value) {
            (&Attr::Ext(ref k), &Value::Ext(ref v)) => {
                if *k=="boundary" {
                    return Ok( format!("--{}",v.clone()) )
                }
            }
            _ => {}
        }
    }
    Err(Error::BoundaryNotSpecified)

}

#[test]
fn test1() {
    use mock::MockStream;
    use hyper::net::NetworkStream;
    use hyper::buffer::BufReader;

    let input=b"POST / HTTP/1.1\r\n\
                Host: example.domain\r\n\
                Content-Type: multipart/form-data; boundary=\"abcdefg\"\r\n\
                Content-Length: 217\r\n\
                \r\n\
                --abcdefg\r\n\
                Content-Disposition: form-data; name=\"field1\"\r\n\
                \r\n\
                data1\r\n\
                --abcdefg\r\n\
                Content-Disposition: form-data; name=\"field2\"; filename=\"file.txt\"\r\n\
                Content-Type: text/plain\r\n\
                \r\n\
                This is a file\r\n\
                with two lines\r\n\
                --abcdefg--";
    let mut mock = MockStream::with_input(input);

    let mock: &mut NetworkStream = &mut mock;
    let mut stream = BufReader::new(mock);
    let sock: SocketAddr = "127.0.0.1:80".parse().unwrap();

    let mut req = Request::new(&mut stream, sock).unwrap();

    match parse_multipart(&mut req) {
        Ok((fields,files)) => {
            assert_eq!(fields.len(),1);
            for (k,v) in fields {
                if &k=="field1" { assert_eq!( &v, "data1" ) }
            }
            assert_eq!(files.len(),1);
            for (k,file) in files {
                if &k=="field2" {
                    assert_eq!(file.size, 30);
                    assert!(file.filename.is_some());
                    assert_eq!(&file.filename.unwrap(), "file.txt");
                    // FIXME add content_type check after impl
                }
            }

        }
        Err(e) => {
            println!("FAILED ON: {:?}",e);
            assert!(false);
        }
    }
}
<commit_msg>explitly call iter() on static byte strings inside extend() calls<commit_after>//! This crate parses and processes `hyper::server::Request` data
//! that contains `multipart/form-data` content.
//!
//! The main entry point is `parse_multipart`

extern crate hyper;
#[macro_use] extern crate mime;
extern crate httparse;
extern crate libc;
extern crate tempdir;
extern crate textnonce;

#[cfg(test)]
mod mock;
pub mod buf;
mod content_disposition;
pub mod error;

pub use error::Error;
pub use buf::BufReadPlus;

use std::path::PathBuf;
use std::fs::File;
use std::io::{BufReader,BufRead};

use hyper::server::Request;
use hyper::header::{Headers,ContentType};
use mime::{Mime,TopLevel,SubLevel,Attr,Value};
use tempdir::TempDir;
use textnonce::TextNonce;

pub use content_disposition::ContentDispositionFormData;

#[cfg(test)]
use std::net::SocketAddr;

/// This structure represents uploaded files which were received as
/// part of the `multipart/form-data` parsing.  They are streamed to
/// disk because they may not fit in memory.
pub struct UploadedFile {
    /// This is the temporary file where the data was saved.
    pub path: PathBuf,
    /// This is the filename that was specified in the data, unfiltered.  It may or may not
    /// be legal on the local filesystem.
    pub filename: Option<String>,
    /// This is the content-type that was specified in the data, unvalidated.
    pub content_type: Mime,
    /// This is the actual size of the file received
    pub size: usize,
}

/// This function parses and processes `hyper::server::Request` data
/// that contains `multipart/form-data` content.  It does this in a streaming
/// fashion, saving embedded uploaded files to disk as they are encountered by
/// the parser.
///
/// It returns two sets of data.  The first, `Vec<(String,String)>`, are the
/// variable-value pairs from the POST-ed form.  The second
/// `Vec<(String,UploadedFile)>` are the variable-file pairs from the POST-ed
/// form, where `UploadedFile` structures describe the uploaded file.
pub fn parse_multipart<'a,'b>(
    request: &mut Request<'a,'b>)
    -> Result< (Vec<(String,String)>, Vec<(String,UploadedFile)>), Error >
{
    let mut parameters: Vec<(String,String)> = Vec::new();
    let mut files: Vec<(String,UploadedFile)> = Vec::new();

    let string_boundary = try!( get_boundary(request) );
    println!("Boundary is {}", string_boundary);
    let boundary: Vec<u8> = string_boundary.into_bytes();
    let mut crlf_boundary: Vec<u8> = Vec::with_capacity(2 + boundary.len());
    crlf_boundary.extend(b"\r\n".iter());
    crlf_boundary.extend(boundary.clone());

    // Request implements Read.  Internally it's a BufReader, but it's not
    // exposed that way.  We need to wrap it into a BufReader to get that
    // functionality ourselves.
    let mut r = BufReader::with_capacity(4096,request);

    enum State {
        Discarding, // Discard until after initial boundary and CRLF
        CapturingValue, // Capture value until boundary, then discard past CRLF
        CapturingFile, // Capture file until boundary, then discard past CRLF
        ReadingHeaders, // Capture headers to blank line
    }

    let mut state = State::Discarding;
    let mut headers = Headers::new();

    loop {
        let mut buf: Vec<u8> = Vec::new();

        match state {
            State::Discarding => {
                // Read up to and including the boundary
                let read = try!( r.stream_until_token( &*boundary, &mut buf ) );
                if read==0 { return Err(Error::Eof); }

                state = State::ReadingHeaders;
            },
            State::ReadingHeaders => {
                {
                    // IF the next two characters are '--' (look ahead), then we are finished
                    let peeker = try!( r.fill_buf() );
                    if peeker.len() >= 2 && &peeker[..2]==b"--" {
                        return Ok((parameters, files))
                    }
                } // drop peeker

                // Read up to and including the CRLF after the boundary
                let read = try!( r.stream_until_token( b"\r\n", &mut buf ) );
                if read==0 { return Err(Error::Eof); }

                buf.truncate(0);
                let read = try!( r.stream_until_token( b"\r\n\r\n", &mut buf ) );
                if read==0 { return Err(Error::Eof); }
                buf.extend(b"\r\n\r\n".iter()); // parse_headers() needs this token at the end

                // Parse the headers
                let mut header_memory = [httparse::EMPTY_HEADER; 4];
                match httparse::parse_headers( &*buf, &mut header_memory) {
                    Ok(httparse::Status::Complete((_,raw_headers))) => {
                        // Turn raw headers into hyper headers
                        headers = try!( Headers::from_raw(raw_headers) );

                        let cd: &ContentDispositionFormData = match headers.get() {
                            Some(x) => x,
                            None => return Err(Error::MissingDisposition),
                        };
                        let ct: Option<&ContentType> = headers.get();

                        state = if ct.is_some() || cd.filename.is_some() {
                            State::CapturingFile
                        } else {
                            State::CapturingValue
                        };
                    },
                    Ok(httparse::Status::Partial) => {
                        println!("Header Parsing was Partial");
                        return Err(Error::ParseError);
                    },
                    Err(e) => return Err( From::from(e) ),
                }
            },
            State::CapturingValue => {
                buf.truncate(0);
                let _ = try!( r.stream_until_token( &*crlf_boundary, &mut buf ) );

                let cd: &ContentDispositionFormData = headers.get().unwrap();

                let key = match cd.name {
                    None => return Err(Error::NoName),
                    Some(ref name) => name.clone()
                };
                let value = try!( String::from_utf8(buf) );

                parameters.push( (key,value) );

                state = State::ReadingHeaders;
            },
            State::CapturingFile => {
                // Setup a file to capture the contents
                let mut temp = try!( TempDir::new("formdata") ).into_path();
                temp.push( TextNonce::sized_urlsafe(32).unwrap().into_string() );
                let mut tmpfile = try!( File::create(temp.clone()) );

                // Stream out the file
                let read = try!( r.stream_until_token( &*crlf_boundary, &mut tmpfile ) );

                let cd: &ContentDispositionFormData = headers.get().unwrap();
                let key = match cd.name {
                    None => return Err(Error::NoName),
                    Some(ref name) => name.clone()
                };
                // FIXME: handle content-type header, and default to text/plain if
                //        no content-type header.
                // FIXME: handle content-type: multipart/mixed as multiple files
                // FIXME: handle content-transfer-encoding

                let ufile = UploadedFile {
                    path: temp,
                    filename: cd.filename.clone(),
                    content_type: mime!(Text/Plain; Charset=Utf8), // FIXME
                    size: read - crlf_boundary.len()
                };
                files.push( (key,ufile) );

                state = State::ReadingHeaders;
            },
        }
    }
}

fn get_boundary<'a,'b>(request: &Request<'a,'b>) -> Result<String,Error>
{
    // Verify that the request is 'content-type: multipart/form-data'
    let content_type: &ContentType = match request.headers.get() {
        Some(h) => h,
        None => return Err(Error::NoRequestContentType),
    };
    let ContentType(ref mime) = *content_type;
    let Mime(ref top_level, ref sub_level, ref params) = *mime;
    if *top_level != TopLevel::Multipart {
        return Err(Error::NotMultipart);
    }
    if *sub_level != SubLevel::FormData {
        return Err(Error::NotFormData);
    }

    // Get the boundary token
    for &(ref attr,ref value) in params.iter() {
        match (attr,value) {
            (&Attr::Ext(ref k), &Value::Ext(ref v)) => {
                if *k=="boundary" {
                    return Ok( format!("--{}",v.clone()) )
                }
            }
            _ => {}
        }
    }
    Err(Error::BoundaryNotSpecified)

}

#[test]
fn test1() {
    use mock::MockStream;
    use hyper::net::NetworkStream;
    use hyper::buffer::BufReader;

    let input=b"POST / HTTP/1.1\r\n\
                Host: example.domain\r\n\
                Content-Type: multipart/form-data; boundary=\"abcdefg\"\r\n\
                Content-Length: 217\r\n\
                \r\n\
                --abcdefg\r\n\
                Content-Disposition: form-data; name=\"field1\"\r\n\
                \r\n\
                data1\r\n\
                --abcdefg\r\n\
                Content-Disposition: form-data; name=\"field2\"; filename=\"file.txt\"\r\n\
                Content-Type: text/plain\r\n\
                \r\n\
                This is a file\r\n\
                with two lines\r\n\
                --abcdefg--";
    let mut mock = MockStream::with_input(input);

    let mock: &mut NetworkStream = &mut mock;
    let mut stream = BufReader::new(mock);
    let sock: SocketAddr = "127.0.0.1:80".parse().unwrap();

    let mut req = Request::new(&mut stream, sock).unwrap();

    match parse_multipart(&mut req) {
        Ok((fields,files)) => {
            assert_eq!(fields.len(),1);
            for (k,v) in fields {
                if &k=="field1" { assert_eq!( &v, "data1" ) }
            }
            assert_eq!(files.len(),1);
            for (k,file) in files {
                if &k=="field2" {
                    assert_eq!(file.size, 30);
                    assert!(file.filename.is_some());
                    assert_eq!(&file.filename.unwrap(), "file.txt");
                    // FIXME add content_type check after impl
                }
            }

        }
        Err(e) => {
            println!("FAILED ON: {:?}",e);
            assert!(false);
        }
    }
}
<|endoftext|>


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

use std::env;
use std::io;

use kernel32;
use winapi;

const MAX_LEN: usize = 15;

pub fn hostname() -> io::Result<String> {
    let mut buf = [0 as winapi::CHAR; MAX_LEN + 1];
    let mut len = buf.len() as u32;
    unsafe {
        if kernel32::GetComputerNameA(buf.as_mut_ptr(), &mut len) == 0 {
            return Err(io::Error::last_os_error());
        }
    }
    let bytes = buf[0..len as usize]
        .iter()
        .map(|&byte| byte as u8)
        .collect::<Vec<u8>>();
    Ok(String::from_utf8_lossy(&bytes).into_owned())
}
<commit_msg>appease windows compile warning<commit_after>// Copyright (c) 2017 Chef Software Inc. and/or applicable contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::io;

use kernel32;
use winapi;

const MAX_LEN: usize = 15;

pub fn hostname() -> io::Result<String> {
    let mut buf = [0 as winapi::CHAR; MAX_LEN + 1];
    let mut len = buf.len() as u32;
    unsafe {
        if kernel32::GetComputerNameA(buf.as_mut_ptr(), &mut len) == 0 {
            return Err(io::Error::last_os_error());
        }
    }
    let bytes = buf[0..len as usize]
        .iter()
        .map(|&byte| byte as u8)
        .collect::<Vec<u8>>();
    Ok(String::from_utf8_lossy(&bytes).into_owned())
}
<|endoftext|>


<commit_before>extern crate hyper;
extern crate libc;
#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate log;
extern crate log4rs;
extern crate serde;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;

use hyper::Client;
use hyper::client::response::Response;
use hyper::header::{ Headers, ContentType };
use hyper::mime::{ Mime, TopLevel, SubLevel };
use hyper::status::StatusCode;
use libc::{ uint8_t, size_t };
use log::LogLevelFilter;
use log4rs::append::file::FileAppender;
use log4rs::encode::pattern::PatternEncoder;
use log4rs::config::{ Appender, Config, Logger, Root };
use serde::Serialize;
use std::collections::HashMap;
use std::env::var;
use std::io::Read;
use std::slice;
use std::sync::{ Once, ONCE_INIT, Mutex };

const BASE_URL: &'static str = "http://localhost:8080/k2/ctapi/";

static OK: i8 = 0;
static ERR_INVALID: i8 = -1;
static ERR_HOST: i8 = -127;

static INIT: Once = ONCE_INIT;

lazy_static! {
    static ref MAP: Mutex<HashMap<u16, u16>> = Mutex::new(HashMap::new());
}

#[derive(Serialize)]
struct Empty();

#[derive(Serialize)]
struct RequestData {
    dad: u8,
    sad: u8,
    lenc: usize,
    command: Vec<u8>,
    lenr: usize
}

#[allow(non_snake_case)]
#[derive(Deserialize)]
struct ResponseData {
    dad: u8,
    sad: u8,
    lenr: usize,
    response: Vec<u8>,
    responseCode: i8
}

macro_rules! post_request {
    ($path:expr) => (post_request($path, &Empty{}));
    ($path:expr, $data:expr) => (post_request($path, $data));
}

#[no_mangle]
#[allow(non_snake_case, unused_must_use)]
pub extern fn CT_init(ctn: u16, pn: u16) -> i8 {
    init_logging();

    debug!("CT_init( ctn: {}; pn: {} )", ctn, pn);

    // Do we know this CTN?
    if MAP.lock().unwrap().contains_key(&ctn) {
        return ERR_INVALID
    }

    // Build the request URL
    let endpoint = "ct_init".to_string();
    let path = endpoint + "/" + &ctn.to_string() + "/" + &pn.to_string();

    debug!("Request path: {}", path);

    // Perform the request
    let mut response = post_request!(&path);

    match response.status {
        StatusCode::Ok => {
            debug!("Response received!");

            // Cast server response
            let mut body = String::new();
            response.read_to_string(&mut body).unwrap();

            let status = body.parse::<i8>().unwrap();
            if status == OK {
                // Store CTN
                MAP.lock().unwrap().insert(ctn, pn);
            }

            debug!("Return status: {}", status);
            status
        },
        _ => {
            error!("Response not OK! Returning {}", ERR_HOST);
            debug!("Response: {:?}", response);
            ERR_HOST
        }
    }
}

#[no_mangle]
#[allow(non_snake_case)]
pub extern fn CT_data(ctn: u16, dad: *mut uint8_t, sad: *mut uint8_t, lenc: size_t, command: *const uint8_t, lenr: *mut size_t, response: *mut uint8_t) -> i8 {
    init_logging();

    debug!("CT_data for ctn: {}", ctn);
    unsafe {
        if !MAP.lock().unwrap().contains_key(&ctn) {
            return ERR_INVALID
        }

        let dad: &mut u8 = &mut *dad;

        let sad: &mut u8 = &mut *sad;

        let lenr: &mut size_t = &mut *lenr;

        let command = slice::from_raw_parts(command, lenc as usize);

        let response = slice::from_raw_parts_mut(response, *lenr);

        let requestData = RequestData {
            dad: *dad,
            sad: *sad,
            lenc: lenc,
            command: command.to_vec(),
            lenr: *lenr
        };

        debug!("Additonal request parameter: dad: {}, sad: {}, lenc: {}, command: {:?}, lenr: {}, response.len(): {}", dad, sad, lenc, command, lenr, response.len());

        let pn = MAP.lock().unwrap();
        let pn = pn.get(&ctn).unwrap();
        let endpoint = "ct_data".to_string();
        let path = endpoint + "/" + &ctn.to_string() + "/" + &pn.to_string();

        debug!("Request path: {}", path);

        let mut http_response = post_request(&path, &requestData);

        match http_response.status {
            StatusCode::Ok => {
                debug!("Response received!");

                // decode server response
                let mut body = String::new();
                http_response.read_to_string(&mut body).unwrap();
                let responseData: ResponseData = serde_json::from_str(&body).unwrap();

                if responseData.responseCode == OK {

                    debug!("Body data from response: dad: {}, sad: {}, lenr: {}", responseData.dad, responseData.sad, responseData.lenr);

                    *dad = responseData.dad;
                    *sad = responseData.sad;
                    *lenr = responseData.lenr;

                    debug!("Content of response array");
                    for (place, element) in response.iter_mut().zip(responseData.response.iter()) {
                        debug!("[{}] {}", place, element);
                        *place = *element;
                    }
                }
                debug!("Return status: {}", responseData.responseCode);
                return responseData.responseCode;
            },
            _ => {
                error!("Response not OK! Returning {}", ERR_HOST);
                debug!("Response: {:?}", http_response);
                ERR_HOST
            }
        }
    }
}

#[no_mangle]
#[allow(non_snake_case)]
pub extern fn CT_close(ctn: u16) -> i8 {
    init_logging();

    debug!("CT_close( ctn: {} )", ctn);

    // Do we know this CTN?
    if !MAP.lock().unwrap().contains_key(&ctn) {
        return ERR_INVALID
    }

    // Build the request URL
    let pn = MAP.lock().unwrap().get(&ctn).unwrap().clone();
    let endpoint = "ct_close".to_string();
    let path = endpoint + "/" + &ctn.to_string() + "/" + &pn.to_string();

    debug!("Request path: {}", path);

    // Perform the request
    let mut response = post_request!(&path);

    match response.status {
        StatusCode::Ok => {
            debug!("Response received!");

            // Cast server response
            let mut body = String::new();
            response.read_to_string(&mut body).unwrap();

            let status = body.parse::<i8>().unwrap();
            if status == OK {
                // Remove CTN
                MAP.lock().unwrap().remove(&ctn);
            }

            debug!("Return status: {}", status);
            status
        },
        _ => {
            error!("Response not OK! Returning {}", ERR_HOST);
            debug!("Response: {:?}", response);
            ERR_HOST
        }
    }
}

fn init_logging() {
   match var("K2_LOG_PATH") {
        Ok(path) => {
            INIT.call_once(|| {
                let file = FileAppender::builder()
                    .encoder(Box::new(PatternEncoder::new("{d} {l} {M}: {m}{n}")))
                    .build(path + "/" + &"k2_basecamp.log".to_string())
                    .unwrap();

                let config = Config::builder()
                    .appender(Appender::builder().build("file", Box::new(file)))
                    .logger(Logger::builder()
                        .appender("file")
                        .additive(false)
                        .build("k2_basecamp", LogLevelFilter::Debug))
                    .build(Root::builder().appender("file").build(LogLevelFilter::Error))
                    .unwrap();

                log4rs::init_config(config).unwrap();
            })
        },
        _ => ()
    }
}

fn post_request<T>(path: &str, payload: &T) -> Response
    where T: Serialize
{
    let base_url = var("K2_BASE_URL").unwrap_or(BASE_URL.to_string());
    let url = base_url + path;

    let client = Client::new();

    let mut headers = Headers::new();
    headers.set(
        ContentType(Mime(TopLevel::Application, SubLevel::Json, vec![]))
    );

    let body = serde_json::to_string(&payload).unwrap();

    return client.post(&url)
        .headers(headers)
        .body(&body[..])
        .send()
        .unwrap();
}
<commit_msg>refined debug logging for CT_data<commit_after>extern crate hyper;
extern crate libc;
#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate log;
extern crate log4rs;
extern crate serde;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;

use hyper::Client;
use hyper::client::response::Response;
use hyper::header::{ Headers, ContentType };
use hyper::mime::{ Mime, TopLevel, SubLevel };
use hyper::status::StatusCode;
use libc::{ uint8_t, size_t };
use log::LogLevelFilter;
use log4rs::append::file::FileAppender;
use log4rs::encode::pattern::PatternEncoder;
use log4rs::config::{ Appender, Config, Logger, Root };
use serde::Serialize;
use std::collections::HashMap;
use std::env::var;
use std::io::Read;
use std::slice;
use std::sync::{ Once, ONCE_INIT, Mutex };

const BASE_URL: &'static str = "http://localhost:8080/k2/ctapi/";

static OK: i8 = 0;
static ERR_INVALID: i8 = -1;
static ERR_HOST: i8 = -127;

static INIT: Once = ONCE_INIT;

lazy_static! {
    static ref MAP: Mutex<HashMap<u16, u16>> = Mutex::new(HashMap::new());
}

#[derive(Serialize)]
struct Empty();

#[derive(Serialize)]
struct RequestData {
    dad: u8,
    sad: u8,
    lenc: usize,
    command: Vec<u8>,
    lenr: usize
}

#[allow(non_snake_case)]
#[derive(Deserialize)]
struct ResponseData {
    dad: u8,
    sad: u8,
    lenr: usize,
    response: Vec<u8>,
    responseCode: i8
}

macro_rules! post_request {
    ($path:expr) => (post_request($path, &Empty{}));
    ($path:expr, $data:expr) => (post_request($path, $data));
}

#[no_mangle]
#[allow(non_snake_case, unused_must_use)]
pub extern fn CT_init(ctn: u16, pn: u16) -> i8 {
    init_logging();

    debug!("CT_init( ctn: {}; pn: {} )", ctn, pn);

    // Do we know this CTN?
    if MAP.lock().unwrap().contains_key(&ctn) {
        return ERR_INVALID
    }

    // Build the request URL
    let endpoint = "ct_init".to_string();
    let path = endpoint + "/" + &ctn.to_string() + "/" + &pn.to_string();

    debug!("Request path: {}", path);

    // Perform the request
    let mut response = post_request!(&path);

    match response.status {
        StatusCode::Ok => {
            debug!("Response received!");

            // Cast server response
            let mut body = String::new();
            response.read_to_string(&mut body).unwrap();

            let status = body.parse::<i8>().unwrap();
            if status == OK {
                // Store CTN
                MAP.lock().unwrap().insert(ctn, pn);
            }

            debug!("Return status: {}", status);
            status
        },
        _ => {
            error!("Response not OK! Returning {}", ERR_HOST);
            debug!("Response: {:?}", response);
            ERR_HOST
        }
    }
}

#[no_mangle]
#[allow(non_snake_case)]
pub extern fn CT_data(ctn: u16, dad: *mut uint8_t, sad: *mut uint8_t, lenc: size_t, command: *const uint8_t, lenr: *mut size_t, response: *mut uint8_t) -> i8 {
    init_logging();

    debug!("CT_data for ctn: {}", ctn);
    unsafe {
        if !MAP.lock().unwrap().contains_key(&ctn) {
            return ERR_INVALID
        }

        let dad: &mut u8 = &mut *dad;

        let sad: &mut u8 = &mut *sad;

        let lenr: &mut size_t = &mut *lenr;

        let command = slice::from_raw_parts(command, lenc as usize);

        let response = slice::from_raw_parts_mut(response, *lenr);

        let requestData = RequestData {
            dad: *dad,
            sad: *sad,
            lenc: lenc,
            command: command.to_vec(),
            lenr: *lenr
        };

        debug!("Additonal request parameter: dad: {}, sad: {}, lenc: {}, command: {:?}, lenr: {}, response.len(): {}", dad, sad, lenc, command, lenr, response.len());

        let pn = MAP.lock().unwrap();
        let pn = pn.get(&ctn).unwrap();
        let endpoint = "ct_data".to_string();
        let path = endpoint + "/" + &ctn.to_string() + "/" + &pn.to_string();

        debug!("Request path: {}", path);

        let mut http_response = post_request(&path, &requestData);

        match http_response.status {
            StatusCode::Ok => {
                debug!("Response received!");

                // decode server response
                let mut body = String::new();
                http_response.read_to_string(&mut body).unwrap();
                debug!("Reponse body: {}", body);

                let responseData: ResponseData = serde_json::from_str(&body).unwrap();

                if responseData.responseCode == OK {
                    debug!("dad: {}", responseData.dad);
                    *dad = responseData.dad;

                    debug!("sad: {}", responseData.sad);
                    *sad = responseData.sad;

                    debug!("lenr: {}", responseData.lenr);
                    *lenr = responseData.lenr;

                    debug!("Content of response array");
                    for (place, element) in response.iter_mut().zip(responseData.response.iter()) {
                        debug!("[{}] {}", place, element);
                        *place = *element;
                    }
                }
                debug!("Return status: {}", responseData.responseCode);
                return responseData.responseCode;
            },
            _ => {
                error!("Response not OK! Returning {}", ERR_HOST);
                debug!("Response: {:?}", http_response);
                ERR_HOST
            }
        }
    }
}

#[no_mangle]
#[allow(non_snake_case)]
pub extern fn CT_close(ctn: u16) -> i8 {
    init_logging();

    debug!("CT_close( ctn: {} )", ctn);

    // Do we know this CTN?
    if !MAP.lock().unwrap().contains_key(&ctn) {
        return ERR_INVALID
    }

    // Build the request URL
    let pn = MAP.lock().unwrap().get(&ctn).unwrap().clone();
    let endpoint = "ct_close".to_string();
    let path = endpoint + "/" + &ctn.to_string() + "/" + &pn.to_string();

    debug!("Request path: {}", path);

    // Perform the request
    let mut response = post_request!(&path);

    match response.status {
        StatusCode::Ok => {
            debug!("Response received!");

            // Cast server response
            let mut body = String::new();
            response.read_to_string(&mut body).unwrap();

            let status = body.parse::<i8>().unwrap();
            if status == OK {
                // Remove CTN
                MAP.lock().unwrap().remove(&ctn);
            }

            debug!("Return status: {}", status);
            status
        },
        _ => {
            error!("Response not OK! Returning {}", ERR_HOST);
            debug!("Response: {:?}", response);
            ERR_HOST
        }
    }
}

fn init_logging() {
   match var("K2_LOG_PATH") {
        Ok(path) => {
            INIT.call_once(|| {
                let file = FileAppender::builder()
                    .encoder(Box::new(PatternEncoder::new("{d} {l} {M}: {m}{n}")))
                    .build(path + "/" + &"k2_basecamp.log".to_string())
                    .unwrap();

                let config = Config::builder()
                    .appender(Appender::builder().build("file", Box::new(file)))
                    .logger(Logger::builder()
                        .appender("file")
                        .additive(false)
                        .build("k2_basecamp", LogLevelFilter::Debug))
                    .build(Root::builder().appender("file").build(LogLevelFilter::Error))
                    .unwrap();

                log4rs::init_config(config).unwrap();
            })
        },
        _ => ()
    }
}

fn post_request<T>(path: &str, payload: &T) -> Response
    where T: Serialize
{
    let base_url = var("K2_BASE_URL").unwrap_or(BASE_URL.to_string());
    let url = base_url + path;

    let client = Client::new();

    let mut headers = Headers::new();
    headers.set(
        ContentType(Mime(TopLevel::Application, SubLevel::Json, vec![]))
    );

    let body = serde_json::to_string(&payload).unwrap();

    return client.post(&url)
        .headers(headers)
        .body(&body[..])
        .send()
        .unwrap();
}
<|endoftext|>