<commit_before>use seed::prelude::*;
use crate::Msg;

pub mod perfects_utils {
	use num_bigint::{BigUint,ToBigUint};
	use num_traits::{ToPrimitive};
    use seed::prelude::*;
    use crate::Msg;

	#[derive(Clone)]
	pub struct Perfect {
		pub n: u64,
		pub p: u64,
		pub digits: u64,
		pub discovery: String,
	}

	#[derive(Clone)]
	pub struct PerfectDownload {
		pub n: u64,
		pub p: u64,
	}

	pub fn perfects() -> Vec<Perfect> {
        vec![
			Perfect {n: 51, p: 82589934, digits: 49724095, discovery: String::from("2018 Laroche, Woltman, Kurowski, Blosser, et al.")},
			Perfect {n: 50, p: 77232917, digits: 46498850, discovery: String::from("2017 Jonathan Pace, George Woltman, Scott Kurowski, Aaron Blosser, et al..") },
			Perfect {n: 49, p: 74207281, digits: 44677235, discovery: String::from("2016 Cooper, Woltman, Kurowski, Blosser et al.") },
			Perfect {n: 48, p: 57885161, digits: 34850340, discovery: String::from("2013 Cooper, Woltman, Kurowski, et al.") },
			Perfect {n: 47, p: 43112609, digits: 25956377, discovery: String::from("2008 Smith, Woltman, Kurowski, et al.") },
			Perfect {n: 46, p: 42643801, digits: 42643801, discovery: String::from("2009 Strindmo, Woltman, Kurowski, et al.") },
			Perfect {n: 45, p: 37156667, digits: 22370543, discovery: String::from("2008 Elvenich, Woltman, Kurowski, et al.") },
			Perfect {n: 44, p: 32582657, digits: 19616714, discovery: String::from("2006 Cooper, Boone, Woltman, Kurowski, et al.") },
			Perfect {n: 43, p: 30402457, digits: 18304103, discovery: String::from("2005 Cooper, Boone, Woltman, Kurowski, et al.") },
			Perfect {n: 42, p: 25964951, digits: 15632458, discovery: String::from("2005 Nowak, Woltman, Kurowski, et. al.") },
			Perfect {n: 41, p: 24036583, digits: 14471465, discovery: String::from("2004 Findley, Woltman, Kurowski, et. al.") },
			Perfect {n: 40, p: 20996011, digits: 12640858, discovery: String::from("2003 Shafer, Woltman, Kurowski, et. al.") },
			Perfect {n: 39, p: 13466917, digits: 8107892, discovery: String::from("2001 Cameron, Woltman, Kurowski, et. al.") },
			Perfect {n: 38, p: 6972593, digits: 4197919, discovery: String::from("1999 Hajratwala, Woltman, Kurowski, et. al.") },
			Perfect {n: 37, p: 3021377, digits: 1819050, discovery: String::from("1998 Clarkson, Woltman, Kurowski, et. al.") },
			Perfect {n: 36, p: 2976221, digits: 1791864, discovery: String::from("1997 Spence, Woltman, et. al.") },
			Perfect {n: 35, p: 1398269, digits: 841842, discovery: String::from("1996 Armengaud, Woltman, et. al.") },
			Perfect {n: 34, p: 1257787, digits: 757263, discovery: String::from("1996 Slowinski&Gage") },
			Perfect {n: 33, p: 859433, digits: 517430, discovery: String::from("1994 Slowinski&Gage") },
			Perfect {n: 32, p: 756839, digits: 455663, discovery: String::from("1992 Slowinski&Gage") },
			Perfect {n: 31, p: 216091, digits: 130100, discovery: String::from("1985 Slowinski") },
			Perfect {n: 30, p: 132049, digits: 79502, discovery: String::from("1983 Slowinski") },
			Perfect {n: 29, p: 110503, digits: 66530, discovery: String::from("1988 Colquitt&Welsh") },
			Perfect {n: 28, p: 86243, digits: 51924, discovery: String::from("1982 Slowinski") },
			Perfect {n: 27, p: 44497, digits: 26790, discovery: String::from("1979 Nelson&Slowinski") },
			Perfect {n: 26, p: 23209, digits: 13973, discovery: String::from("1979 Noll") },
			Perfect {n: 25, p: 21701, digits: 13066, discovery: String::from("1978 Noll&Nickel") },
			Perfect {n: 24, p: 19937, digits: 12003, discovery: String::from("1971 Tuckerman") },
			Perfect {n: 23, p: 11213, digits: 6751, discovery: String::from("1963 Gillies") },
			Perfect {n: 22, p: 9941, digits: 5985, discovery: String::from("1963 Gillies") },
			Perfect {n: 21, p: 9689, digits: 5834, discovery: String::from("1963 Gillies") },
			Perfect {n: 20, p: 4423, digits: 2663, discovery: String::from("1961 Hurwitz") },
			Perfect {n: 19, p: 4253, digits: 2561, discovery: String::from("1961 Hurwitz") },
			Perfect {n: 18, p: 3217, digits: 1937, discovery: String::from("1957 Riesel") },
			Perfect {n: 17, p: 2281, digits: 1373, discovery: String::from("1952 Robinson") },
			Perfect {n: 16, p: 2203, digits: 1327, discovery: String::from("1952 Robinson") },
			Perfect {n: 14, p: 607, digits: 366, discovery: String::from("1952 Robinson") },
			Perfect {n: 13, p: 521, digits: 314, discovery: String::from("1952 Robinson") },
			Perfect {n: 12, p: 127, digits: 77, discovery: String::from("1876 Lucas") },
			Perfect {n: 11, p: 107, digits: 65, discovery: String::from("1914 Powers") },
			Perfect {n: 10, p: 89, digits: 54, discovery: String::from("1911 Powers") },
			Perfect {n: 9, p: 61, digits: 37, discovery: String::from("1883 Pervushin") },
			Perfect {n: 8, p: 31, digits: 19, discovery: String::from("1772 Euler") },
			Perfect {n: 7, p: 19, digits: 12, discovery: String::from("1588 Cataldi") },
			Perfect {n: 6, p: 17, digits: 10, discovery: String::from("1588 Cataldi") },
			Perfect {n: 5, p: 13, digits: 8, discovery: String::from("1456 ?") },
			Perfect {n: 4, p: 7, digits: 4, discovery: String::from("?") },
			Perfect {n: 3, p: 5, digits: 3, discovery: String::from("?") },
			Perfect {n: 2, p: 3, digits: 2, discovery: String::from("?") },
			Perfect {n: 1, p: 2, digits: 1, discovery: String::from("?") },
		]
	}

	pub fn save_as_file(filename:String, filecontent:String) -> seed::dom_types::Node<Msg> {
        let href:String = vec!["data:text/plain,",&filecontent].into_iter().collect();
        a![attrs!{At::Download => &filename, At::Href => &href}, "TXT"]
    }

	pub fn generate_file(n:u64, p:u64) -> seed::dom_types::Node<Msg> {
		let download_filename:String = format!("P{}.txt",&n.to_string());
		let two:BigUint = 2.to_biguint().unwrap();
		let power:BigUint = two.clone() << (p.to_usize().unwrap()-1-1);
        let perfect_value:BigUint = power.clone() * ((power.clone()*two.clone()) -1.to_biguint().unwrap());
        save_as_file(download_filename, perfect_value.to_string())
    }
}

pub fn render(model: &crate::Model) -> seed::dom_types::Node<Msg> {
    let mut html = vec![];
    let perfects = perfects_utils::perfects().to_owned();

    for perfect in perfects.iter() {
		let perfectp:u64 = perfect.p;
		let perfect_download = perfects_utils::PerfectDownload {n: perfect.n, p: perfect.p};
		let equation:String = format!("2{} × (2{}-1)",&(perfectp-1).to_string(),&(perfectp).to_string());

        html.push(
            tr![
                td![perfect.n.to_string()],//rank
				td![El::from_html(&equation)],//perfect number as a formula

                td![perfect.digits.to_string()],//digits in length
                td![perfect.discovery],//discovery
				if model.download.n == perfect.n {
					td![perfects_utils::generate_file(model.download.n,model.download.p)]
				} else {
					td![button!["Generate",mouse_ev("mouseup", move |event| Msg::GenerateDownload(event, perfect_download))]]
				},
            ]
        );
    }

    div![
        h1!["The Perfect Numbers"],
        br![],
        br![],
        br![],
        table![
            attrs!{At::Class => "perfecttable text"},
            tbody![
                tr![
                    td![
                        b!["No."]
                    ],
                    td![
                        b!["Prime"]
                    ],
                    td![
                        b!["Digits"]
                    ],
                    td![
                        b!["Discovered"]
                    ],
                    td![
                        b!["Download"]
                    ]
                ],
                html
            ]
        ]
    ]
}

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

    #[test]
    fn perfect_test<'a>() {
        let mut perfects:Vec<perfects_utils::Perfect> = perfects_utils::perfects();
		perfects.reverse();
        assert_eq!(perfects[49].digits, 46498850);
        assert_eq!(perfects[39].digits, 12640858);
    }
}<commit_msg>Added 15th perfect back in! Unit test caught this!<commit_after>use seed::prelude::*;
use crate::Msg;

pub mod perfects_utils {
	use num_bigint::{BigUint,ToBigUint};
	use num_traits::{ToPrimitive};
    use seed::prelude::*;
    use crate::Msg;

	#[derive(Clone)]
	pub struct Perfect {
		pub n: u64,
		pub p: u64,
		pub digits: u64,
		pub discovery: String,
	}

	#[derive(Clone)]
	pub struct PerfectDownload {
		pub n: u64,
		pub p: u64,
	}

	pub fn perfects() -> Vec<Perfect> {
        vec![
			Perfect {n: 51, p: 82589934, digits: 49724095, discovery: String::from("2018 Laroche, Woltman, Kurowski, Blosser, et al.")},
			Perfect {n: 50, p: 77232917, digits: 46498850, discovery: String::from("2017 Jonathan Pace, George Woltman, Scott Kurowski, Aaron Blosser, et al..") },
			Perfect {n: 49, p: 74207281, digits: 44677235, discovery: String::from("2016 Cooper, Woltman, Kurowski, Blosser et al.") },
			Perfect {n: 48, p: 57885161, digits: 34850340, discovery: String::from("2013 Cooper, Woltman, Kurowski, et al.") },
			Perfect {n: 47, p: 43112609, digits: 25956377, discovery: String::from("2008 Smith, Woltman, Kurowski, et al.") },
			Perfect {n: 46, p: 42643801, digits: 42643801, discovery: String::from("2009 Strindmo, Woltman, Kurowski, et al.") },
			Perfect {n: 45, p: 37156667, digits: 22370543, discovery: String::from("2008 Elvenich, Woltman, Kurowski, et al.") },
			Perfect {n: 44, p: 32582657, digits: 19616714, discovery: String::from("2006 Cooper, Boone, Woltman, Kurowski, et al.") },
			Perfect {n: 43, p: 30402457, digits: 18304103, discovery: String::from("2005 Cooper, Boone, Woltman, Kurowski, et al.") },
			Perfect {n: 42, p: 25964951, digits: 15632458, discovery: String::from("2005 Nowak, Woltman, Kurowski, et. al.") },
			Perfect {n: 41, p: 24036583, digits: 14471465, discovery: String::from("2004 Findley, Woltman, Kurowski, et. al.") },
			Perfect {n: 40, p: 20996011, digits: 12640858, discovery: String::from("2003 Shafer, Woltman, Kurowski, et. al.") },
			Perfect {n: 39, p: 13466917, digits: 8107892, discovery: String::from("2001 Cameron, Woltman, Kurowski, et. al.") },
			Perfect {n: 38, p: 6972593, digits: 4197919, discovery: String::from("1999 Hajratwala, Woltman, Kurowski, et. al.") },
			Perfect {n: 37, p: 3021377, digits: 1819050, discovery: String::from("1998 Clarkson, Woltman, Kurowski, et. al.") },
			Perfect {n: 36, p: 2976221, digits: 1791864, discovery: String::from("1997 Spence, Woltman, et. al.") },
			Perfect {n: 35, p: 1398269, digits: 841842, discovery: String::from("1996 Armengaud, Woltman, et. al.") },
			Perfect {n: 34, p: 1257787, digits: 757263, discovery: String::from("1996 Slowinski&Gage") },
			Perfect {n: 33, p: 859433, digits: 517430, discovery: String::from("1994 Slowinski&Gage") },
			Perfect {n: 32, p: 756839, digits: 455663, discovery: String::from("1992 Slowinski&Gage") },
			Perfect {n: 31, p: 216091, digits: 130100, discovery: String::from("1985 Slowinski") },
			Perfect {n: 30, p: 132049, digits: 79502, discovery: String::from("1983 Slowinski") },
			Perfect {n: 29, p: 110503, digits: 66530, discovery: String::from("1988 Colquitt&Welsh") },
			Perfect {n: 28, p: 86243, digits: 51924, discovery: String::from("1982 Slowinski") },
			Perfect {n: 27, p: 44497, digits: 26790, discovery: String::from("1979 Nelson&Slowinski") },
			Perfect {n: 26, p: 23209, digits: 13973, discovery: String::from("1979 Noll") },
			Perfect {n: 25, p: 21701, digits: 13066, discovery: String::from("1978 Noll&Nickel") },
			Perfect {n: 24, p: 19937, digits: 12003, discovery: String::from("1971 Tuckerman") },
			Perfect {n: 23, p: 11213, digits: 6751, discovery: String::from("1963 Gillies") },
			Perfect {n: 22, p: 9941, digits: 5985, discovery: String::from("1963 Gillies") },
			Perfect {n: 21, p: 9689, digits: 5834, discovery: String::from("1963 Gillies") },
			Perfect {n: 20, p: 4423, digits: 2663, discovery: String::from("1961 Hurwitz") },
			Perfect {n: 19, p: 4253, digits: 2561, discovery: String::from("1961 Hurwitz") },
			Perfect {n: 18, p: 3217, digits: 1937, discovery: String::from("1957 Riesel") },
			Perfect {n: 17, p: 2281, digits: 1373, discovery: String::from("1952 Robinson") },
			Perfect {n: 16, p: 2203, digits: 1327, discovery: String::from("1952 Robinson") },
			Perfect {n: 15, p: 1279, digits: 770, discovery: String::from("1952 Robinson") },
			Perfect {n: 14, p: 607, digits: 366, discovery: String::from("1952 Robinson") },
			Perfect {n: 13, p: 521, digits: 314, discovery: String::from("1952 Robinson") },
			Perfect {n: 12, p: 127, digits: 77, discovery: String::from("1876 Lucas") },
			Perfect {n: 11, p: 107, digits: 65, discovery: String::from("1914 Powers") },
			Perfect {n: 10, p: 89, digits: 54, discovery: String::from("1911 Powers") },
			Perfect {n: 9, p: 61, digits: 37, discovery: String::from("1883 Pervushin") },
			Perfect {n: 8, p: 31, digits: 19, discovery: String::from("1772 Euler") },
			Perfect {n: 7, p: 19, digits: 12, discovery: String::from("1588 Cataldi") },
			Perfect {n: 6, p: 17, digits: 10, discovery: String::from("1588 Cataldi") },
			Perfect {n: 5, p: 13, digits: 8, discovery: String::from("1456 ?") },
			Perfect {n: 4, p: 7, digits: 4, discovery: String::from("?") },
			Perfect {n: 3, p: 5, digits: 3, discovery: String::from("?") },
			Perfect {n: 2, p: 3, digits: 2, discovery: String::from("?") },
			Perfect {n: 1, p: 2, digits: 1, discovery: String::from("?") },
		]
	}

	pub fn save_as_file(filename:String, filecontent:String) -> seed::dom_types::Node<Msg> {
        let href:String = vec!["data:text/plain,",&filecontent].into_iter().collect();
        a![attrs!{At::Download => &filename, At::Href => &href}, "TXT"]
    }

	pub fn generate_file(n:u64, p:u64) -> seed::dom_types::Node<Msg> {
		let download_filename:String = format!("P{}.txt",&n.to_string());
		let two:BigUint = 2.to_biguint().unwrap();
		let power:BigUint = two.clone() << (p.to_usize().unwrap()-1-1);
        let perfect_value:BigUint = power.clone() * ((power.clone()*two.clone()) -1.to_biguint().unwrap());
        save_as_file(download_filename, perfect_value.to_string())
    }
}

pub fn render(model: &crate::Model) -> seed::dom_types::Node<Msg> {
    let mut html = vec![];
    let perfects = perfects_utils::perfects().to_owned();

    for perfect in perfects.iter() {
		let perfectp:u64 = perfect.p;
		let perfect_download = perfects_utils::PerfectDownload {n: perfect.n, p: perfect.p};
		let equation:String = format!("2{} × (2{}-1)",&(perfectp-1).to_string(),&(perfectp).to_string());

        html.push(
            tr![
                td![perfect.n.to_string()],//rank
				td![El::from_html(&equation)],//perfect number as a formula

                td![perfect.digits.to_string()],//digits in length
                td![perfect.discovery],//discovery
				if model.download.n == perfect.n {
					td![perfects_utils::generate_file(model.download.n,model.download.p)]
				} else {
					td![button!["Generate",mouse_ev("mouseup", move |event| Msg::GenerateDownload(event, perfect_download))]]
				},
            ]
        );
    }

    div![
        h1!["The Perfect Numbers"],
        br![],
        br![],
        br![],
        table![
            attrs!{At::Class => "perfecttable text"},
            tbody![
                tr![
                    td![
                        b!["No."]
                    ],
                    td![
                        b!["Prime"]
                    ],
                    td![
                        b!["Digits"]
                    ],
                    td![
                        b!["Discovered"]
                    ],
                    td![
                        b!["Download"]
                    ]
                ],
                html
            ]
        ]
    ]
}

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

    #[test]
    fn perfect_test<'a>() {
        let mut perfects:Vec<perfects_utils::Perfect> = perfects_utils::perfects();
		perfects.reverse();
        assert_eq!(perfects[49].digits, 46498850);
        assert_eq!(perfects[39].digits, 12640858);
    }
}<|endoftext|>


<commit_before>use buffer::{Buffer, Direction, Mark};
use input::Input;
use uibuf::{UIBuffer, CharColor};
use frontends::Frontend;
use overlay::{Overlay, OverlayType};

/// A View is an abstract Window (into a Buffer).
///
/// It draws a portion of a Buffer to a UIBuffer which in turn is drawn to the
/// screen. It maintains the status bar for the current view, the "dirty status"
/// which is whether the buffer has been modified or not and a number of other
/// pieces of information.
pub struct View<'v> {
    pub buffer: Buffer,     // Text buffer
    pub uibuf: UIBuffer,    // UIBuffer
    pub overlay: Overlay,

    top_line: Mark,         // First character of the top line to be displayed.
    left_col: uint,         // Index into the top line to set the left column to.
    cursor: Mark,           // Cursor displayed by this buffer.
}

impl<'v> View<'v> {

    //----- CONSTRUCTORS ---------------------------------------------------------------------------

    pub fn new(source: Input, width: uint, height: uint) -> View<'v> {
        let mut buffer = match source {
            Input::Filename(path) => {
                match path {
                    Some(s) => Buffer::new_from_file(Path::new(s)),
                    None    => Buffer::new(),
                }
            },
            Input::Stdin(reader) => {
                Buffer::new_from_reader(reader)
            },
        };

        // NOTE(greg): this may not play well with resizing
        let uibuf = UIBuffer::new(width, height);

        let cursor = Mark::Cursor(0);
        buffer.set_mark(cursor, 0);
        let top_line = Mark::DisplayMark(0);
        buffer.set_mark(top_line, 0);

        View {
            buffer: buffer,
            top_line: top_line,
            left_col: 0,
            cursor: cursor,
            uibuf: uibuf,
            overlay: Overlay::None,
        }
    }

    pub fn get_height(&self) -> uint {
        // NOTE(greg): when the status bar needs to move up, this value should be changed
        self.uibuf.get_height() -1
    }

    pub fn get_width(&self) -> uint {
        self.uibuf.get_width()
    }

    //----- DRAWING METHODS ------------------------------------------------------------------------
    /// Clear the buffer
    ///
    /// Fills every cell in the UIBuffer with the space (' ') char.
    pub fn clear<T: Frontend>(&mut self, frontend: &mut T) {
        self.uibuf.fill(' ');
        self.uibuf.draw_everything(frontend);
    }

    pub fn draw<T: Frontend>(&mut self, frontend: &mut T) {
        for (index,line) in self.buffer
                                .lines_from(self.top_line)
                                .unwrap()
                                .take(self.get_height())
                                .enumerate() {
            draw_line(&mut self.uibuf, line, index, self.left_col);
            if index == self.get_height() { break; }
        }

        match self.overlay {
            Overlay::None => self.draw_cursor(frontend),
            _             => {
                self.overlay.draw(frontend, &mut self.uibuf);
                self.overlay.draw_cursor(frontend);
            }
        }

        self.uibuf.draw_everything(frontend);
    }

    pub fn draw_status<T: Frontend>(&mut self, frontend: &mut T) {
        let buffer_status = self.buffer.status_text();
        let mut cursor_status = self.buffer.get_mark_coords(self.cursor).unwrap_or((0,0));
        cursor_status = (cursor_status.0 + 1, cursor_status.1 + 1);
        let status_text = format!("{} {}", buffer_status, cursor_status).into_bytes();
        let status_text_len = status_text.len();
        let width = self.get_width();
        let height = self.get_height() - 1;

        for index in range(0, width) {
            let mut ch: char = ' ';
            if index < status_text_len {
                ch = status_text[index] as char;
            }
            self.uibuf.update_cell(index, height, ch, CharColor::Black, CharColor::Blue);
        }

        self.uibuf.draw_range(frontend, height, height+1);
    }

    pub fn draw_cursor<T: Frontend>(&mut self, frontend: &mut T) {
        if let Some(top_line) = self.buffer.get_mark_coords(self.top_line) {
            if let Some((x, y)) = self.buffer.get_mark_coords(self.cursor) {
                frontend.draw_cursor(x as int, y as int - top_line.1 as int);
            }
        }
    }

    pub fn set_overlay(&mut self, overlay_type: OverlayType) {
        match overlay_type {
            OverlayType::Prompt => {
                self.overlay = Overlay::Prompt {
                    cursor_x: 1,
                    prefix: ":",
                    data: String::new(),
                };
            }
        }
    }

    pub fn move_cursor(&mut self, direction: Direction, amount: uint) {
        self.buffer.shift_mark(self.cursor, direction, amount);
        self.move_screen();
    }

    pub fn move_cursor_to_line_end(&mut self) {
        self.buffer.shift_mark(self.cursor, Direction::LineEnd, 0);
        self.move_screen();
    }

    pub fn move_cursor_to_line_start(&mut self) {
        self.buffer.shift_mark(self.cursor, Direction::LineStart, 0);
        self.move_screen();
    }

    //Update the top_line mark if necessary to keep the cursor on the screen.
    fn move_screen(&mut self) {
        if let (Some(cursor), Some(top_line)) = (self.buffer.get_mark_coords(self.cursor),
                                                 self.buffer.get_mark_coords(self.top_line)) {
            // FIXME
            // match cursor.0 as int - self.left_col as int {
            //     x if x < 0 => { self.left_col -= x as uint; }
            //     x if x > self.get_width() as int => { self.left_col += x as uint - self.get_width()}
            //     _ => { }
            // }

            let cursor_linenum = cursor.1 as int;
            let cursor_offset = cursor_linenum - top_line.1 as int;
            let height = self.get_height() as int;

            if cursor_offset >= (height - 5) {
                // moving down
                let times = cursor_offset - (height - 5) + 1;
                self.buffer.shift_mark(self.top_line, Direction::Down, times as uint);
            } else if cursor_offset < 5 {
                // moving up
                self.buffer.shift_mark(self.top_line, Direction::Up, 1);
            }
        }
    }

    pub fn delete_chars(&mut self, direction: Direction, num_chars: uint) {
        let chars = self.buffer.remove_chars(self.cursor, direction, num_chars);
        match (chars, direction) {
            (Some(chars), Direction::Left) => {
                self.move_cursor(Direction::Left, chars.len());
            }
            (Some(chars), Direction::LeftWord(..)) => {
                self.move_cursor(Direction::Left, chars.len());
            }
            _ => {}
        }
    }

    pub fn insert_tab(&mut self) {
        // A tab is just 4 spaces
        for _ in range(0i, 4) {
            self.insert_char(' ');
        }
    }

    pub fn insert_char(&mut self, ch: char) {
        self.buffer.insert_char(self.cursor, ch as u8);
        self.move_cursor(Direction::Right, 1)
    }

    pub fn undo(&mut self) {
        let point = if let Some(transaction) = self.buffer.undo() { transaction.end_point }
                    else { return; };
        self.buffer.set_mark(self.cursor, point);
        self.move_screen();
    }

    pub fn redo(&mut self) {
        let point = if let Some(transaction) = self.buffer.redo() { transaction.end_point }
                    else { return; };
        self.buffer.set_mark(self.cursor, point);
        self.move_screen();
    }

}

pub fn draw_line(buf: &mut UIBuffer, line: &[u8], idx: uint, left: uint) {
    let width = buf.get_width() - 1;
    let mut wide_chars = 0;
    for line_idx in range(left, left + width) {
        if line_idx < line.len() {
            match line[line_idx] {
                b'\t'   => {
                    let w = 4 - line_idx % 4;
                    for _ in range(0, w) {
                        buf.update_cell_content(line_idx + wide_chars - left, idx, ' ');
                    }
                }
                b'\n'   => buf.update_cell_content(line_idx + wide_chars - left, idx, ' '),
                _       => buf.update_cell_content(line_idx + wide_chars - left, idx,
                                                   line[line_idx] as char),
            }
            wide_chars += (line[line_idx] as char).width(false).unwrap_or(1) - 1;
        } else { buf.update_cell_content(line_idx + wide_chars - left, idx, ' '); }
    }
    if line.len() >= width {
        buf.update_cell_content(width + wide_chars, idx, '→');
    }

}

#[cfg(test)]
mod tests {

    use buffer::Direction;
    use view::View;
    use input::Input;

    fn setup_view<'v>(testcase: &'static str) -> View<'v> {
        let mut view = View::new(Input::Filename(None), 50, 50);
        for ch in testcase.chars() {
            view.insert_char(ch);
        }
        view.buffer.set_mark(view.cursor, 0);
        view
    }

    #[test]
    fn test_move_cursor_down() {
        let mut view = setup_view("test\nsecond");
        view.move_cursor(Direction::Down, 1);
        assert_eq!(view.buffer.get_mark_coords(view.cursor).unwrap().1, 1);
        assert_eq!(view.buffer.lines_from(view.cursor).unwrap().next().unwrap(), b"second"[]);
    }

    #[test]
    fn test_move_cursor_up() {
        let mut view = setup_view("test\nsecond");
        view.move_cursor(Direction::Down, 1);
        view.move_cursor(Direction::Up, 1);
        assert_eq!(view.buffer.get_mark_coords(view.cursor).unwrap().1, 0);
        assert_eq!(view.buffer.lines_from(view.cursor).unwrap().next().unwrap(), b"test\n"[]);
    }

    #[test]
    fn test_insert_line() {
        let mut view = setup_view("test\nsecond");
        view.move_cursor(Direction::Right, 1);
        view.insert_char('\n');

        assert_eq!(view.buffer.get_mark_coords(view.cursor).unwrap(), (0, 1))
    }

    #[test]
    fn test_insert_char() {
        let mut view = setup_view("test\nsecond");
        view.insert_char('t');

        assert_eq!(view.buffer.lines().next().unwrap(), b"ttest\n"[]);
    }

    #[test]
    fn test_delete_char_to_right() {
        let mut view = setup_view("test\nsecond");
        view.delete_chars(Direction::Right, 1);

        assert_eq!(view.buffer.lines().next().unwrap(), b"est\n"[]);
    }

    #[test]
    fn test_delete_char_to_left() {
        let mut view = setup_view("test\nsecond");
        view.move_cursor(Direction::Right, 1);
        view.delete_chars(Direction::Left, 1);

        assert_eq!(view.buffer.lines().next().unwrap(), b"est\n"[]);
    }

    #[test]
    fn test_delete_char_at_start_of_line() {
        let mut view = setup_view("test\nsecond");
        view.move_cursor(Direction::Down, 1);
        view.delete_chars(Direction::Left, 1);

        assert_eq!(view.buffer.lines().next().unwrap(), b"testsecond"[]);
    }

    #[test]
    fn test_delete_char_at_end_of_line() {
        let mut view = setup_view("test\nsecond");
        view.move_cursor(Direction::Right, 4);
        view.delete_chars(Direction::Right, 1);

        assert_eq!(view.buffer.lines().next().unwrap(), b"testsecond"[]);
    }

    #[test]
    fn deleting_backward_at_start_of_first_line_does_nothing() {
        let mut view = setup_view("test\nsecond");
        view.delete_chars(Direction::Left, 1);

        let lines: Vec<&[u8]> = view.buffer.lines().collect();

        assert_eq!(lines.len(), 2);
        assert_eq!(view.buffer.lines().next().unwrap(), b"test\n");
    }
}
<commit_msg>Fixed some scrolling bugs.<commit_after>use buffer::{Buffer, Direction, Mark};
use input::Input;
use uibuf::{UIBuffer, CharColor};
use frontends::Frontend;
use overlay::{Overlay, OverlayType};

/// A View is an abstract Window (into a Buffer).
///
/// It draws a portion of a Buffer to a UIBuffer which in turn is drawn to the
/// screen. It maintains the status bar for the current view, the "dirty status"
/// which is whether the buffer has been modified or not and a number of other
/// pieces of information.
pub struct View<'v> {
    pub buffer: Buffer,     //Text buffer
    top_line: Mark,         //First character of the top line to be displayed.
    left_col: uint,         //Index into the top line to set the left column to.
    cursor: Mark,           //Cursor displayed by this buffer.
    uibuf: UIBuffer,        //UIBuffer
    threshold: uint,
}

impl<'v> View<'v> {

    //----- CONSTRUCTORS ---------------------------------------------------------------------------

    pub fn new(source: Input, width: uint, height: uint) -> View<'v> {
        let mut buffer = match source {
            Input::Filename(path) => {
                match path {
                    Some(s) => Buffer::new_from_file(Path::new(s)),
                    None    => Buffer::new(),
                }
            },
            Input::Stdin(reader) => {
                Buffer::new_from_reader(reader)
            },
        };

        // NOTE(greg): this may not play well with resizing
        let uibuf = UIBuffer::new(width, height);

        let cursor = Mark::Cursor(0);
        buffer.set_mark(cursor, 0);
        let top_line = Mark::DisplayMark(0);
        buffer.set_mark(top_line, 0);

        View {
            buffer: buffer,
            top_line: top_line,
            left_col: 0,
            cursor: cursor,
            uibuf: uibuf,
            threshold: 5,
        }
    }

    pub fn get_height(&self) -> uint {
        // NOTE(greg): when the status bar needs to move up, this value should be changed
        self.uibuf.get_height() -1
    }

    pub fn get_width(&self) -> uint {
        self.uibuf.get_width()
    }

    //----- DRAWING METHODS ------------------------------------------------------------------------
    /// Clear the buffer
    ///
    /// Fills every cell in the UIBuffer with the space (' ') char.
    pub fn clear<T: Frontend>(&mut self, frontend: &mut T) {
        self.uibuf.fill(' ');
        self.uibuf.draw_everything(frontend);
    }

    pub fn draw<T: Frontend>(&mut self, frontend: &mut T) {
        for (index,line) in self.buffer
                                .lines_from(self.top_line)
                                .unwrap()
                                .take(self.get_height())
                                .enumerate() {
            draw_line(&mut self.uibuf, line, index, self.left_col);
            if index == self.get_height() { break; }
        }

        match self.overlay {
            Overlay::None => self.draw_cursor(frontend),
            _             => {
                self.overlay.draw(frontend, &mut self.uibuf);
                self.overlay.draw_cursor(frontend);
            }
        }

        self.uibuf.draw_everything(frontend);
    }

    pub fn draw_status<T: Frontend>(&mut self, frontend: &mut T) {
        let buffer_status = self.buffer.status_text();
        let mut cursor_status = self.buffer.get_mark_coords(self.cursor).unwrap_or((0,0));
        cursor_status = (cursor_status.0 + 1, cursor_status.1 + 1);
        let status_text = format!("{} {}", buffer_status, cursor_status).into_bytes();
        let status_text_len = status_text.len();
        let width = self.get_width();
        let height = self.get_height() - 1;

        for index in range(0, width) {
            let mut ch: char = ' ';
            if index < status_text_len {
                ch = status_text[index] as char;
            }
            self.uibuf.update_cell(index, height, ch, CharColor::Black, CharColor::Blue);
        }

        self.uibuf.draw_range(frontend, height, height+1);
    }

    pub fn draw_cursor<T: Frontend>(&mut self, frontend: &mut T) {
        if let Some(top_line) = self.buffer.get_mark_coords(self.top_line) {
            if let Some((x, y)) = self.buffer.get_mark_coords(self.cursor) {
                frontend.draw_cursor(x as int, y as int - top_line.1 as int);
            }
        }
    }

    pub fn set_overlay(&mut self, overlay_type: OverlayType) {
        match overlay_type {
            OverlayType::Prompt => {
                self.overlay = Overlay::Prompt {
                    cursor_x: 1,
                    prefix: ":",
                    data: String::new(),
                };
            }
        }
    }

    pub fn move_cursor(&mut self, direction: Direction, amount: uint) {
        self.buffer.shift_mark(self.cursor, direction, amount);
        self.move_screen();
    }

    pub fn move_cursor_to_line_end(&mut self) {
        self.buffer.shift_mark(self.cursor, Direction::LineEnd, 0);
        self.move_screen();
    }

    pub fn move_cursor_to_line_start(&mut self) {
        self.buffer.shift_mark(self.cursor, Direction::LineStart, 0);
        self.move_screen();
    }

    //Update the top_line mark if necessary to keep the cursor on the screen.
    fn move_screen(&mut self) {
        if let (Some(cursor), Some((_, top_line))) = (self.buffer.get_mark_coords(self.cursor),
                                                      self.buffer.get_mark_coords(self.top_line)) {

            let width  = (self.get_width()  - self.threshold) as int;
            let height = (self.get_height() - self.threshold) as int;

            //left-right shifting
            match cursor.0 as int - self.left_col as int {
                x_offset if x_offset < self.threshold as int && self.left_col > 0 => {
                    self.left_col -= (self.threshold as int - x_offset) as uint;
                }
                x_offset if x_offset >= width => {
                    self.left_col += (x_offset - width + 1) as uint;
                }
                _ => { }
            }

            //up-down shifting
            match cursor.1 as int - top_line as int {
                y_offset if y_offset < self.threshold as int && top_line > 0 => {
                    self.buffer.shift_mark(self.top_line,
                                           Direction::Up((self.threshold as int - y_offset) as uint));
                }
                y_offset if y_offset >= height => {
                    self.buffer.shift_mark(self.top_line,
                                           Direction::Down((y_offset - height + 1) as uint));
                }
                _ => { }
            }
        }
    }

    pub fn delete_chars(&mut self, direction: Direction, num_chars: uint) {
        let chars = self.buffer.remove_chars(self.cursor, direction, num_chars);
        match (chars, direction) {
            (Some(chars), Direction::Left) => {
                self.move_cursor(Direction::Left, chars.len());
            }
            (Some(chars), Direction::LeftWord(..)) => {
                self.move_cursor(Direction::Left, chars.len());
            }
            _ => {}
        }
    }

    pub fn insert_tab(&mut self) {
        // A tab is just 4 spaces
        for _ in range(0i, 4) {
            self.insert_char(' ');
        }
    }

    pub fn insert_char(&mut self, ch: char) {
        self.buffer.insert_char(self.cursor, ch as u8);
        self.move_cursor(Direction::Right, 1)
    }

    pub fn undo(&mut self) {
        let point = if let Some(transaction) = self.buffer.undo() { transaction.end_point }
                    else { return; };
        self.buffer.set_mark(self.cursor, point);
        self.move_screen();
    }

    pub fn redo(&mut self) {
        let point = if let Some(transaction) = self.buffer.redo() { transaction.end_point }
                    else { return; };
        self.buffer.set_mark(self.cursor, point);
        self.move_screen();
    }

}

pub fn draw_line(buf: &mut UIBuffer, line: &[u8], idx: uint, left: uint) {
    let width = buf.get_width() - 1;
    let mut wide_chars = 0;
    for line_idx in range(left, left + width) {
        if line_idx < line.len() {
            match line[line_idx] {
                b'\t'   => {
                    let w = 4 - line_idx % 4;
                    for _ in range(0, w) {
                        buf.update_cell_content(line_idx + wide_chars - left, idx, ' ');
                    }
                }
                b'\n'   => buf.update_cell_content(line_idx + wide_chars - left, idx, ' '),
                _       => buf.update_cell_content(line_idx + wide_chars - left, idx,
                                                   line[line_idx] as char),
            }
            wide_chars += (line[line_idx] as char).width(false).unwrap_or(1) - 1;
        } else { buf.update_cell_content(line_idx + wide_chars - left, idx, ' '); }
    }
    if line.len() >= width {
        buf.update_cell_content(width + wide_chars, idx, '→');
    }

}

#[cfg(test)]
mod tests {

    use buffer::Direction;
    use view::View;
    use input::Input;

    fn setup_view<'v>(testcase: &'static str) -> View<'v> {
        let mut view = View::new(Input::Filename(None), 50, 50);
        for ch in testcase.chars() {
            view.insert_char(ch);
        }
        view.buffer.set_mark(view.cursor, 0);
        view
    }

    #[test]
    fn test_move_cursor_down() {
        let mut view = setup_view("test\nsecond");
        view.move_cursor(Direction::Down, 1);
        assert_eq!(view.buffer.get_mark_coords(view.cursor).unwrap().1, 1);
        assert_eq!(view.buffer.lines_from(view.cursor).unwrap().next().unwrap(), b"second"[]);
    }

    #[test]
    fn test_move_cursor_up() {
        let mut view = setup_view("test\nsecond");
        view.move_cursor(Direction::Down, 1);
        view.move_cursor(Direction::Up, 1);
        assert_eq!(view.buffer.get_mark_coords(view.cursor).unwrap().1, 0);
        assert_eq!(view.buffer.lines_from(view.cursor).unwrap().next().unwrap(), b"test\n"[]);
    }

    #[test]
    fn test_insert_line() {
        let mut view = setup_view("test\nsecond");
        view.move_cursor(Direction::Right, 1);
        view.insert_char('\n');

        assert_eq!(view.buffer.get_mark_coords(view.cursor).unwrap(), (0, 1))
    }

    #[test]
    fn test_insert_char() {
        let mut view = setup_view("test\nsecond");
        view.insert_char('t');

        assert_eq!(view.buffer.lines().next().unwrap(), b"ttest\n"[]);
    }

    #[test]
    fn test_delete_char_to_right() {
        let mut view = setup_view("test\nsecond");
        view.delete_chars(Direction::Right, 1);

        assert_eq!(view.buffer.lines().next().unwrap(), b"est\n"[]);
    }

    #[test]
    fn test_delete_char_to_left() {
        let mut view = setup_view("test\nsecond");
        view.move_cursor(Direction::Right, 1);
        view.delete_chars(Direction::Left, 1);

        assert_eq!(view.buffer.lines().next().unwrap(), b"est\n"[]);
    }

    #[test]
    fn test_delete_char_at_start_of_line() {
        let mut view = setup_view("test\nsecond");
        view.move_cursor(Direction::Down, 1);
        view.delete_chars(Direction::Left, 1);

        assert_eq!(view.buffer.lines().next().unwrap(), b"testsecond"[]);
    }

    #[test]
    fn test_delete_char_at_end_of_line() {
        let mut view = setup_view("test\nsecond");
        view.move_cursor(Direction::Right, 4);
        view.delete_chars(Direction::Right, 1);

        assert_eq!(view.buffer.lines().next().unwrap(), b"testsecond"[]);
    }

    #[test]
    fn deleting_backward_at_start_of_first_line_does_nothing() {
        let mut view = setup_view("test\nsecond");
        view.delete_chars(Direction::Left, 1);

        let lines: Vec<&[u8]> = view.buffer.lines().collect();

        assert_eq!(lines.len(), 2);
        assert_eq!(view.buffer.lines().next().unwrap(), b"test\n");
    }
}
<|endoftext|>


<commit_before>use std::path::Path;
use std::process::Command;
use std::env;

fn main() {
    let out_dir = env::var("OUT_DIR").unwrap();
    let debug = env::var("DEBUG").unwrap();

    let mut ispc_args = vec!["src/say_hello.ispc", "--pic"];
    if debug == "true" {
        ispc_args.push("-g");
    }
    // Invoke ISPC to compile our code
    let ispc_status = Command::new("ispc").args(&ispc_args[..])
        .args(&["-o", &format!("{}/say_hello.o", out_dir)])
        .status().unwrap();
    if !ispc_status.success() {
        panic!("ISPC compilation failed");
    }
    // Place our code into a static library we can link against
    let link_status = Command::new("ar").args(&["crus", "libsay_hello.a", "say_hello.o"])
        .current_dir(&Path::new(&out_dir))
        .status().unwrap();
    if !link_status.success() {
        panic!("Linking ISPC code into archive failed");
    }
    println!("cargo:rustc-flags=-L native={}", out_dir);
    println!("cargo:rerun-if-changed=src/say_hello.ispc");
}

<commit_msg>Always print the re-run text even if compilation fails, handle opt-level<commit_after>use std::path::Path;
use std::process::Command;
use std::env;

fn main() {
    println!("cargo:rerun-if-changed=src/say_hello.ispc");

    let out_dir = env::var("OUT_DIR").unwrap();
    let debug = env::var("DEBUG").unwrap();
    let opt_level = env::var("OPT_LEVEL").unwrap();

    let mut ispc_args = vec!["src/say_hello.ispc", "--pic"];
    if debug == "true" {
        ispc_args.push("-g");
    }
    if opt_level == "0" {
        ispc_args.push("-O0");
    } else if opt_level == "1" {
        ispc_args.push("-O1");
    } else if opt_level == "2" {
        ispc_args.push("-O2");
    } else if opt_level == "3" {
        ispc_args.push("-O3");
    }

    // Invoke ISPC to compile our code
    let ispc_status = Command::new("ispc").args(&ispc_args[..])
        .args(&["-o", &format!("{}/say_hello.o", out_dir)])
        .status().unwrap();
    if !ispc_status.success() {
        panic!("ISPC compilation failed");
    }
    // Place our code into a static library we can link against
    let link_status = Command::new("ar").args(&["crus", "libsay_hello.a", "say_hello.o"])
        .current_dir(&Path::new(&out_dir))
        .status().unwrap();
    if !link_status.success() {
        panic!("Linking ISPC code into archive failed");
    }
    println!("cargo:rustc-flags=-L native={}", out_dir);
}

<|endoftext|>


<commit_before>extern crate bindgen;

fn sdk_path(target: &str) -> Result<String, std::io::Error> {
    // Use environment variable if set
    println!("cargo:rerun-if-env-changed=COREAUDIO_SDK_PATH");
    if let Ok(path) = std::env::var("COREAUDIO_SDK_PATH") {
        return Ok(path);
    }

    use std::process::Command;

    let sdk = if target.contains("apple-darwin") {
        "macosx"
    } else if target.contains("apple-ios") {
        "iphoneos"
    } else {
        unreachable!();
    };
    let output = Command::new("xcrun")
        .args(&["--sdk", sdk, "--show-sdk-path"])
        .output()?
        .stdout;
    let prefix_str = std::str::from_utf8(&output).expect("invalid output from `xcrun`");
    Ok(prefix_str.trim_end().to_string())
}

fn build(sdk_path: Option<&str>, target: &str) {
    // Generate one large set of bindings for all frameworks.
    //
    // We do this rather than generating a module per framework as some frameworks depend on other
    // frameworks and in turn share types. To ensure all types are compatible across each
    // framework, we feed all headers to bindgen at once.
    //
    // Only link to each framework and include their headers if their features are enabled and they
    // are available on the target os.

    use std::env;
    use std::path::PathBuf;

    let mut headers = vec![];

    #[cfg(feature = "audio_toolbox")]
    {
        println!("cargo:rustc-link-lib=framework=AudioToolbox");
        headers.push("AudioToolbox/AudioToolbox.h");
    }

    #[cfg(feature = "audio_unit")]
    {
        println!("cargo:rustc-link-lib=framework=AudioUnit");
        headers.push("AudioUnit/AudioUnit.h");
    }

    #[cfg(feature = "core_audio")]
    {
        println!("cargo:rustc-link-lib=framework=CoreAudio");
        headers.push("CoreAudio/CoreAudio.h");
    }

    #[cfg(feature = "open_al")]
    {
        println!("cargo:rustc-link-lib=framework=OpenAL");
        headers.push("OpenAL/al.h");
        headers.push("OpenAL/alc.h");
    }

    #[cfg(all(feature = "core_midi"))]
    {
        if target.contains("apple-darwin") {
            println!("cargo:rustc-link-lib=framework=CoreMIDI");
            headers.push("CoreMIDI/CoreMIDI.h");
        }
    }

    println!("cargo:rerun-if-env-changed=BINDGEN_EXTRA_CLANG_ARGS");
    // Get the cargo out directory.
    let out_dir = PathBuf::from(env::var("OUT_DIR").expect("env variable OUT_DIR not found"));

    // Begin building the bindgen params.
    let mut builder = bindgen::Builder::default();

    builder = builder.size_t_is_usize(true);

    builder = builder.clang_args(&[&format!("--target={}", target)]);

    if let Some(sdk_path) = sdk_path {
        builder = builder.clang_args(&["-isysroot", sdk_path]);
    }

    let meta_header: Vec<_> = headers
        .iter()
        .map(|h| format!("#include <{}>\n", h))
        .collect();

    builder = builder.header_contents("coreaudio.h", &meta_header.concat());

    // Generate the bindings.
    builder = builder
        .trust_clang_mangling(false)
        .derive_default(true);

    let bindings = builder.generate().expect("unable to generate bindings");

    // Write them to the crate root.
    bindings
        .write_to_file(out_dir.join("coreaudio.rs"))
        .expect("could not write bindings");
}

fn main() {
    let target = std::env::var("TARGET").unwrap();
    if !(target.contains("apple-darwin") || target.contains("apple-ios")) {
        panic!("coreaudio-sys requires macos or ios target");
    }

    let directory = sdk_path(&target).ok();
    build(directory.as_ref().map(String::as_ref), &target);
}
<commit_msg>Fix no-default-features build<commit_after>extern crate bindgen;

fn sdk_path(target: &str) -> Result<String, std::io::Error> {
    // Use environment variable if set
    println!("cargo:rerun-if-env-changed=COREAUDIO_SDK_PATH");
    if let Ok(path) = std::env::var("COREAUDIO_SDK_PATH") {
        return Ok(path);
    }

    use std::process::Command;

    let sdk = if target.contains("apple-darwin") {
        "macosx"
    } else if target.contains("apple-ios") {
        "iphoneos"
    } else {
        unreachable!();
    };
    let output = Command::new("xcrun")
        .args(&["--sdk", sdk, "--show-sdk-path"])
        .output()?
        .stdout;
    let prefix_str = std::str::from_utf8(&output).expect("invalid output from `xcrun`");
    Ok(prefix_str.trim_end().to_string())
}

fn build(sdk_path: Option<&str>, target: &str) {
    // Generate one large set of bindings for all frameworks.
    //
    // We do this rather than generating a module per framework as some frameworks depend on other
    // frameworks and in turn share types. To ensure all types are compatible across each
    // framework, we feed all headers to bindgen at once.
    //
    // Only link to each framework and include their headers if their features are enabled and they
    // are available on the target os.

    use std::env;
    use std::path::PathBuf;

    let mut headers: Vec<&'static str> = vec![];

    #[cfg(feature = "audio_toolbox")]
    {
        println!("cargo:rustc-link-lib=framework=AudioToolbox");
        headers.push("AudioToolbox/AudioToolbox.h");
    }

    #[cfg(feature = "audio_unit")]
    {
        println!("cargo:rustc-link-lib=framework=AudioUnit");
        headers.push("AudioUnit/AudioUnit.h");
    }

    #[cfg(feature = "core_audio")]
    {
        println!("cargo:rustc-link-lib=framework=CoreAudio");
        headers.push("CoreAudio/CoreAudio.h");
    }

    #[cfg(feature = "open_al")]
    {
        println!("cargo:rustc-link-lib=framework=OpenAL");
        headers.push("OpenAL/al.h");
        headers.push("OpenAL/alc.h");
    }

    #[cfg(all(feature = "core_midi"))]
    {
        if target.contains("apple-darwin") {
            println!("cargo:rustc-link-lib=framework=CoreMIDI");
            headers.push("CoreMIDI/CoreMIDI.h");
        }
    }

    println!("cargo:rerun-if-env-changed=BINDGEN_EXTRA_CLANG_ARGS");
    // Get the cargo out directory.
    let out_dir = PathBuf::from(env::var("OUT_DIR").expect("env variable OUT_DIR not found"));

    // Begin building the bindgen params.
    let mut builder = bindgen::Builder::default();

    builder = builder.size_t_is_usize(true);

    builder = builder.clang_args(&[&format!("--target={}", target)]);

    if let Some(sdk_path) = sdk_path {
        builder = builder.clang_args(&["-isysroot", sdk_path]);
    }

    let meta_header: Vec<_> = headers
        .iter()
        .map(|h| format!("#include <{}>\n", h))
        .collect();

    builder = builder.header_contents("coreaudio.h", &meta_header.concat());

    // Generate the bindings.
    builder = builder
        .trust_clang_mangling(false)
        .derive_default(true);

    let bindings = builder.generate().expect("unable to generate bindings");

    // Write them to the crate root.
    bindings
        .write_to_file(out_dir.join("coreaudio.rs"))
        .expect("could not write bindings");
}

fn main() {
    let target = std::env::var("TARGET").unwrap();
    if !(target.contains("apple-darwin") || target.contains("apple-ios")) {
        panic!("coreaudio-sys requires macos or ios target");
    }

    let directory = sdk_path(&target).ok();
    build(directory.as_ref().map(String::as_ref), &target);
}
<|endoftext|>


<commit_before>extern crate target_build_utils;
use target_build_utils::TargetInfo;

fn main(){
    let target = TargetInfo::new().expect("could not get target info");
    match target.target_os() {
        "linux" | "android" => println!("cargo:rustc-link-lib=dl"),
        "freebsd" | "dragonfly" => println!("cargo:rustc-link-lib=c"),

        // netbsd claims dl* will be available to any dynamically linked binary, but I haven’t
        // found any libraries that have to be linked to on other platforms.
        // "openbsd" | "bitrig" | "netbsd" | "macos" | "ios" => {}
        //
        // dependencies come with winapi
        // "windows" => {}
        _ => {}
    }

}
<commit_msg>Make build.rs exhaustive<commit_after>extern crate target_build_utils;
use target_build_utils::TargetInfo;
use std::io::Write;

fn main(){
    let target = TargetInfo::new().expect("could not get target info");
    match target.target_os() {
        "linux" | "android" => println!("cargo:rustc-link-lib=dl"),
        "freebsd" | "dragonfly" => println!("cargo:rustc-link-lib=c"),
        // netbsd claims dl* will be available to any dynamically linked binary, but I haven’t
        // found any libraries that have to be linked to on other platforms.
        // What happens if the executable is not linked up dynamically?
        "openbsd" | "bitrig" | "netbsd" | "macos" | "ios" => {}
        // dependencies come with winapi
        "windows" => {}
        tos => {
            writeln!(::std::io::stderr(),
                     "Building for an unknown target_os=`{}`!\nPlease report an issue ",
                     tos).expect("could not report the error");
            ::std::process::exit(0xfc);
        }
    }

}
<|endoftext|>


<commit_before>extern crate gcc;
use std::default::Default;

fn main() {
    gcc::compile_library("liblzf.a",
                         &Default::default(),
                         &["lzf/lzf_c.c", "lzf/lzf_d.c"]);
}
<commit_msg>Use latest gcc crate<commit_after>extern crate gcc;

fn main() {
    gcc::compile_library("liblzf.a", &["lzf/lzf_c.c", "lzf/lzf_d.c"]);
}
<|endoftext|>


<commit_before>use std::env;
use std::path::Path;
use std::process::{Stdio, Command};

fn main() {
    let out_dir = env::var("OUT_DIR").unwrap();
    let dst = Path::new(&out_dir);

    setup();
    configure();
    build();
    install(&dst);
    clean();
    println!("cargo:rustc-flags=-L {} -l static=termbox", dst.join("lib").display());
}

fn setup() {
    let mut cmd = Command::new("git");
    cmd.arg("clone");
    cmd.arg("https://github.com/nsf/termbox");
    cmd.arg(".termbox");
    let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
    let cargo_dir = Path::new(&manifest_dir);
    cmd.current_dir(&cargo_dir);

    run(&mut cmd);
}

fn clean() {
    let mut cmd = Command::new("rm");
    cmd.arg("-rf");
    cmd.arg(".termbox");
    let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
    let cargo_dir = Path::new(&manifest_dir);
    cmd.current_dir(&cargo_dir);
    run(&mut cmd);
}

fn configure() {
    let mut cmd = waf();
    cmd.arg("configure");
    cmd.arg("--prefix=/");

    let target = env::var("TARGET").unwrap();
    let mut cflags;
    if target.contains("i686") {
        cflags = "-m32"
    } else if target.contains("x86_64") {
        cflags = "-m64 -fPIC"
    } else {
        cflags = ""
    }
    println!("waf configure: setting CFLAGS to: `{}`", cflags);
    env::set_var("CFLAGS", cflags);

    run(&mut cmd);
    env::remove_var("CFLAGS");
}

fn build() {
    let mut cmd = waf();
    cmd.arg("build");
    cmd.arg("--targets=termbox_static");
    run(&mut cmd);
}

fn install(dst: &Path) {
    let mut cmd = waf();
    cmd.arg("install");
    cmd.arg("--targets=termbox_static");
    cmd.arg(format!("--destdir={}", dst.display()));
    run(&mut cmd);
}

fn waf() -> Command {
    let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
    let cargo_dir = Path::new(&manifest_dir);
    let termbox_dir = cargo_dir.join(".termbox");
    let mut cmd = Command::new("./waf");
    cmd.current_dir(&termbox_dir);
    cmd
}

fn run(cmd: &mut Command) {
    println!("running: {:?}", cmd);
    assert!(cmd.stdout(Stdio::inherit())
                .stderr(Stdio::inherit())
                .status()
                .unwrap()
                .success());
}
<commit_msg>Fix ARM<commit_after>use std::env;
use std::path::Path;
use std::process::{Stdio, Command};

fn main() {
    let out_dir = env::var("OUT_DIR").unwrap();
    let dst = Path::new(&out_dir);

    setup();
    configure();
    build();
    install(&dst);
    clean();
    println!("cargo:rustc-flags=-L {} -l static=termbox", dst.join("lib").display());
}

fn setup() {
    let mut cmd = Command::new("git");
    cmd.arg("clone");
    cmd.arg("https://github.com/nsf/termbox");
    cmd.arg(".termbox");
    let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
    let cargo_dir = Path::new(&manifest_dir);
    cmd.current_dir(&cargo_dir);

    run(&mut cmd);
}

fn clean() {
    let mut cmd = Command::new("rm");
    cmd.arg("-rf");
    cmd.arg(".termbox");
    let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
    let cargo_dir = Path::new(&manifest_dir);
    cmd.current_dir(&cargo_dir);
    run(&mut cmd);
}

fn configure() {
    let mut cmd = waf();
    cmd.arg("configure");
    cmd.arg("--prefix=/");

    let target = env::var("TARGET").unwrap();
    let mut cflags;
    if target.contains("i686") {
        cflags = "-m32"
    } else if target.contains("x86_64") {
        cflags = "-m64 -fPIC"
    } else {
        cflags = "-fPIC"
    }
    println!("waf configure: setting CFLAGS to: `{}`", cflags);
    env::set_var("CFLAGS", cflags);

    run(&mut cmd);
    env::remove_var("CFLAGS");
}

fn build() {
    let mut cmd = waf();
    cmd.arg("build");
    cmd.arg("--targets=termbox_static");
    run(&mut cmd);
}

fn install(dst: &Path) {
    let mut cmd = waf();
    cmd.arg("install");
    cmd.arg("--targets=termbox_static");
    cmd.arg(format!("--destdir={}", dst.display()));
    run(&mut cmd);
}

fn waf() -> Command {
    let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
    let cargo_dir = Path::new(&manifest_dir);
    let termbox_dir = cargo_dir.join(".termbox");
    let mut cmd = Command::new("./waf");
    cmd.current_dir(&termbox_dir);
    cmd
}

fn run(cmd: &mut Command) {
    println!("running: {:?}", cmd);
    assert!(cmd.stdout(Stdio::inherit())
                .stderr(Stdio::inherit())
                .status()
                .unwrap()
                .success());
}
<|endoftext|>


<commit_before>use std::collections::{HashMap,HashSet};

use std::iter::FromIterator;
use openssl::x509::X509;
use openssl::hash::MessageDigest;

use sozu::messages::{CertFingerprint,CertificateAndKey,Order,HttpFront,TlsFront,Instance};

pub type AppId = String;

#[derive(Debug,Clone,PartialEq,Eq, Serialize, Deserialize)]
pub struct HttpProxy {
  ip_address: String,
  port:       u16,
  fronts:     HashMap<AppId, Vec<HttpFront>>,
  instances:  HashMap<AppId, Vec<Instance>>,
}

#[derive(Debug,Clone,PartialEq,Eq,Serialize, Deserialize)]
pub struct TlsProxy {
  ip_address:   String,
  port:         u16,
  certificates: HashMap<CertFingerprint, CertificateAndKey>,
  fronts:       HashMap<AppId, Vec<TlsFront>>,
  instances:    HashMap<AppId, Vec<Instance>>,
}

#[derive(Debug,Default,Clone,PartialEq,Eq,Serialize,Deserialize)]
pub struct ConfigState {
  instances:       HashMap<AppId, Vec<Instance>>,
  http_fronts:     HashMap<AppId, Vec<HttpFront>>,
  https_fronts:    HashMap<AppId, Vec<TlsFront>>,
  certificates:    HashMap<CertFingerprint, CertificateAndKey>,
  //ip, port
  http_addresses:  Vec<(String, u16)>,
  https_addresses: Vec<(String, u16)>,
  //tcp:
}

impl ConfigState {
  pub fn new() -> ConfigState {
    ConfigState {
      instances:       HashMap::new(),
      http_fronts:     HashMap::new(),
      https_fronts:    HashMap::new(),
      certificates:    HashMap::new(),
      http_addresses:  Vec::new(),
      https_addresses: Vec::new(),
    }
  }

  pub fn add_http_address(&mut self, ip_address: String, port: u16) {
    self.http_addresses.push((ip_address, port))
  }

  pub fn add_https_address(&mut self, ip_address: String, port: u16) {
    self.https_addresses.push((ip_address, port))
  }

  pub fn handle_order(&mut self, order: &Order) {
    match order {
      &Order::AddHttpFront(ref front) => {
        let f = HttpFront {
          app_id:     front.app_id.clone(),
          hostname:   front.hostname.clone(),
          path_begin: front.path_begin.clone(),
        };
        if self.http_fronts.contains_key(&front.app_id) {
          self.http_fronts.get_mut(&front.app_id).map(|front| {
            if !front.contains(&f) {
              front.push(f);
            }
          });
        } else {
          self.http_fronts.insert(front.app_id.clone(), vec!(f));
        }
      },
      &Order::RemoveHttpFront(ref front) => {
        if let Some(front_list) = self.http_fronts.get_mut(&front.app_id) {
          front_list.retain(|el| el.hostname != front.hostname || el.path_begin != front.path_begin);
        }
      },
      &Order::AddCertificate(ref certificate_and_key) => {
        let f = CertificateAndKey {
          certificate:       certificate_and_key.certificate.clone(),
          certificate_chain: certificate_and_key.certificate_chain.clone(),
          key:               certificate_and_key.key.clone(),
        };
        let fingerprint = match X509::from_pem(&certificate_and_key.certificate.as_bytes()[..]).and_then(|cert|
                                  cert.fingerprint(MessageDigest::sha256())) {
          Ok(f)  => f,
          Err(e) => {
            error!("cannot obtain the certificate's fingerprint: {:?}", e);
            return;
          }
        };

        if !self.certificates.contains_key(&fingerprint) {
          self.certificates.insert(fingerprint.clone(), f);
        }
      },
      &Order::RemoveCertificate(ref fingerprint) => {
        self.certificates.remove(fingerprint);
      },
      &Order::AddTlsFront(ref front) => {
        let f = TlsFront {
          app_id:      front.app_id.clone(),
          hostname:    front.hostname.clone(),
          path_begin:  front.path_begin.clone(),
          fingerprint: front.fingerprint.clone(),
        };
        if self.https_fronts.contains_key(&front.app_id) {
          self.https_fronts.get_mut(&front.app_id).map(|front| {
            if !front.contains(&f) {
              front.push(f);
            }
          });
        } else {
          self.https_fronts.insert(front.app_id.clone(), vec!(f));
        }
      },
      &Order::RemoveTlsFront(ref front) => {
        self.https_fronts.remove(&front.app_id);
      },
      &Order::AddInstance(ref instance)  => {
        if self.instances.contains_key(&instance.app_id) {
          self.instances.get_mut(&instance.app_id).map(|instance_vec| {
            if !instance_vec.contains(&instance) {
              instance_vec.push(instance.clone());
            }
          });
        } else {
          self.instances.insert(instance.app_id.clone(), vec!(instance.clone()));
        }
      },
      &Order::RemoveInstance(ref instance) => {
        if let Some(instance_list) = self.instances.get_mut(&instance.app_id) {
          instance_list.retain(|el| el.ip_address != instance.ip_address || el.port != instance.port);
        }
      }
      _ => {}
    }
  }

  pub fn generate_orders(&self) -> Vec<Order> {
    let mut v = Vec::new();
    for (app_id, front_list) in self.http_fronts.iter() {
      for front in front_list {
        v.push(Order::AddHttpFront(HttpFront {
          app_id:     app_id.clone(),
          hostname:   front.hostname.clone(),
          path_begin: front.path_begin.clone(),
        }));
      }
    }

    for certificate_and_key in (&self.certificates).values() {
      v.push(Order::AddCertificate(certificate_and_key.clone()));
    }

    for (app_id, front_list) in &self.https_fronts {
      for front in front_list {
        v.push(Order::AddTlsFront(TlsFront {
          app_id:      app_id.clone(),
          hostname:    front.hostname.clone(),
          path_begin:  front.path_begin.clone(),
          fingerprint: front.fingerprint.clone(),
        }));
      }
    }

    for instance_list in self.instances.values() {
      for instance in instance_list {
        v.push(Order::AddInstance(instance.clone()));
      }
    }

    v
  }

  pub fn diff(&self, other:&ConfigState) -> Vec<Order> {
    let mut my_fronts: HashSet<(&AppId, &HttpFront)> = HashSet::new();
    for (ref app_id, ref front_list) in self.http_fronts.iter() {
      for ref front in front_list.iter() {
        my_fronts.insert((&app_id, &front));
      }
    }
    let mut their_fronts: HashSet<(&AppId, &HttpFront)> = HashSet::new();
    for (ref app_id, ref front_list) in other.http_fronts.iter() {
      for ref front in front_list.iter() {
        their_fronts.insert((&app_id, &front));
      }
    }

    let removed_http_fronts = my_fronts.difference(&their_fronts);
    let added_http_fronts   = their_fronts.difference(&my_fronts);

    let mut my_fronts: HashSet<(&AppId, &TlsFront)> = HashSet::new();
    for (ref app_id, ref front_list) in self.https_fronts.iter() {
      for ref front in front_list.iter() {
        my_fronts.insert((&app_id, &front));
      }
    }
    let mut their_fronts: HashSet<(&AppId, &TlsFront)> = HashSet::new();
    for (ref app_id, ref front_list) in other.https_fronts.iter() {
      for ref front in front_list.iter() {
        their_fronts.insert((&app_id, &front));
      }
    }

    let removed_https_fronts = my_fronts.difference(&their_fronts);
    let added_https_fronts   = their_fronts.difference(&my_fronts);

    let mut my_instances: HashSet<(&AppId, &Instance)> = HashSet::new();
    for (ref app_id, ref instance_list) in self.instances.iter() {
      for ref instance in instance_list.iter() {
        my_instances.insert((&app_id, &instance));
      }
    }
    let mut their_instances: HashSet<(&AppId, &Instance)> = HashSet::new();
    for (ref app_id, ref instance_list) in other.instances.iter() {
      for ref instance in instance_list.iter() {
        their_instances.insert((&app_id, &instance));
      }
    }

    let removed_instances = my_instances.difference(&their_instances);
    let added_instances   = their_instances.difference(&my_instances);

    let my_certificates:    HashSet<(&CertFingerprint, &CertificateAndKey)> = HashSet::from_iter(self.certificates.iter());
    let their_certificates: HashSet<(&CertFingerprint, &CertificateAndKey)> = HashSet::from_iter(other.certificates.iter());

    let removed_certificates = my_certificates.difference(&their_certificates);
    let added_certificates   = their_certificates.difference(&my_certificates);

    let mut v = vec!();

    for &(_, certificate_and_key) in added_certificates {
      v.push(Order::AddCertificate(certificate_and_key.clone()));
    }

    for &(app_id, front) in removed_http_fronts {
     v.push(Order::RemoveHttpFront(HttpFront {
       app_id:     app_id.clone(),
       hostname:   front.hostname.clone(),
       path_begin: front.path_begin.clone(),
      }));
    }

    for &(app_id, front) in removed_https_fronts {
     v.push(Order::RemoveTlsFront(TlsFront {
       app_id:      app_id.clone(),
       hostname:    front.hostname.clone(),
       path_begin:  front.path_begin.clone(),
       fingerprint: front.fingerprint.clone(),
      }));
    }

    for &(_, instance) in added_instances {
      v.push(Order::AddInstance(instance.clone()));
    }

    for &(_, instance) in removed_instances {
      v.push(Order::RemoveInstance(instance.clone()));
    }

    for &(app_id, front) in added_http_fronts {
      v.push(Order::AddHttpFront(HttpFront {
        app_id:     app_id.clone(),
        hostname:   front.hostname.clone(),
        path_begin: front.path_begin.clone(),
      }));
    }

    for &(app_id, front) in added_https_fronts {
      v.push(Order::AddTlsFront(TlsFront {
        app_id:      app_id.clone(),
        hostname:    front.hostname.clone(),
        path_begin:  front.path_begin.clone(),
        fingerprint: front.fingerprint.clone(),
      }));
    }

    for  &(fingerprint, _) in removed_certificates {
      v.push(Order::RemoveCertificate(fingerprint.clone()));
    }
    v
  }
}

#[cfg(test)]
mod tests {
  use super::*;
  use sozu::messages::{Order,HttpFront,Instance};

  #[test]
  fn serialize() {
    let mut state:ConfigState = Default::default();
    state.handle_order(&Order::AddHttpFront(HttpFront { app_id: String::from("app_1"), hostname: String::from("lolcatho.st:8080"), path_begin: String::from("/") }));
    state.handle_order(&Order::AddHttpFront(HttpFront { app_id: String::from("app_2"), hostname: String::from("test.local"), path_begin: String::from("/abc") }));
    state.handle_order(&Order::AddInstance(Instance { app_id: String::from("app_1"), ip_address: String::from("127.0.0.1"), port: 1026 }));
    state.handle_order(&Order::AddInstance(Instance { app_id: String::from("app_1"), ip_address: String::from("127.0.0.2"), port: 1027 }));
    state.handle_order(&Order::AddInstance(Instance { app_id: String::from("app_2"), ip_address: String::from("192.167.1.2"), port: 1026 }));
    state.handle_order(&Order::AddInstance(Instance { app_id: String::from("app_1"), ip_address: String::from("192.168.1.3"), port: 1027 }));
    state.handle_order(&Order::RemoveInstance(Instance { app_id: String::from("app_1"), ip_address: String::from("192.168.1.3"), port: 1027 }));

    /*
    let encoded = state.encode();
    println!("serialized:\n{}", encoded);

    let new_state: Option<HttpProxy> = decode_str(&encoded);
    println!("deserialized:\n{:?}", new_state);
    assert_eq!(new_state, Some(state));
    */
    //assert!(false);
  }

  #[test]
  fn diff() {
    let mut state:ConfigState = Default::default();
    state.handle_order(&Order::AddHttpFront(HttpFront { app_id: String::from("app_1"), hostname: String::from("lolcatho.st:8080"), path_begin: String::from("/") }));
    state.handle_order(&Order::AddHttpFront(HttpFront { app_id: String::from("app_2"), hostname: String::from("test.local"), path_begin: String::from("/abc") }));
    state.handle_order(&Order::AddInstance(Instance { app_id: String::from("app_1"), ip_address: String::from("127.0.0.1"), port: 1026 }));
    state.handle_order(&Order::AddInstance(Instance { app_id: String::from("app_1"), ip_address: String::from("127.0.0.2"), port: 1027 }));
    state.handle_order(&Order::AddInstance(Instance { app_id: String::from("app_2"), ip_address: String::from("192.167.1.2"), port: 1026 }));

    let mut state2:ConfigState = Default::default();
    state2.handle_order(&Order::AddHttpFront(HttpFront { app_id: String::from("app_1"), hostname: String::from("lolcatho.st:8080"), path_begin: String::from("/") }));
    state2.handle_order(&Order::AddInstance(Instance { app_id: String::from("app_1"), ip_address: String::from("127.0.0.1"), port: 1026 }));
    state2.handle_order(&Order::AddInstance(Instance { app_id: String::from("app_1"), ip_address: String::from("127.0.0.2"), port: 1027 }));
    state2.handle_order(&Order::AddInstance(Instance { app_id: String::from("app_1"), ip_address: String::from("127.0.0.2"), port: 1028 }));

   let e = vec!(
     Order::RemoveHttpFront(HttpFront { app_id: String::from("app_2"), hostname: String::from("test.local"), path_begin: String::from("/abc") }),
     Order::RemoveInstance(Instance { app_id: String::from("app_2"), ip_address: String::from("192.167.1.2"), port: 1026 }),
     Order::AddInstance(Instance { app_id: String::from("app_1"), ip_address: String::from("127.0.0.2"), port: 1028 }),
   );
   let expected_diff:HashSet<&Order> = HashSet::from_iter(e.iter());

   let d = state.diff(&state2);
   let diff = HashSet::from_iter(d.iter());
   println!("diff orders:\n{:?}\n", diff);
   println!("expected diff orders:\n{:?}\n", expected_diff);

   assert_eq!(diff, expected_diff);
  }
}
<commit_msg>use HashMap's Entry API<commit_after>use std::collections::{HashMap,HashSet};

use std::iter::FromIterator;
use openssl::x509::X509;
use openssl::hash::MessageDigest;

use sozu::messages::{CertFingerprint,CertificateAndKey,Order,HttpFront,TlsFront,Instance};

pub type AppId = String;

#[derive(Debug,Clone,PartialEq,Eq, Serialize, Deserialize)]
pub struct HttpProxy {
  ip_address: String,
  port:       u16,
  fronts:     HashMap<AppId, Vec<HttpFront>>,
  instances:  HashMap<AppId, Vec<Instance>>,
}

#[derive(Debug,Clone,PartialEq,Eq,Serialize, Deserialize)]
pub struct TlsProxy {
  ip_address:   String,
  port:         u16,
  certificates: HashMap<CertFingerprint, CertificateAndKey>,
  fronts:       HashMap<AppId, Vec<TlsFront>>,
  instances:    HashMap<AppId, Vec<Instance>>,
}

#[derive(Debug,Default,Clone,PartialEq,Eq,Serialize,Deserialize)]
pub struct ConfigState {
  instances:       HashMap<AppId, Vec<Instance>>,
  http_fronts:     HashMap<AppId, Vec<HttpFront>>,
  https_fronts:    HashMap<AppId, Vec<TlsFront>>,
  certificates:    HashMap<CertFingerprint, CertificateAndKey>,
  //ip, port
  http_addresses:  Vec<(String, u16)>,
  https_addresses: Vec<(String, u16)>,
  //tcp:
}

impl ConfigState {
  pub fn new() -> ConfigState {
    ConfigState {
      instances:       HashMap::new(),
      http_fronts:     HashMap::new(),
      https_fronts:    HashMap::new(),
      certificates:    HashMap::new(),
      http_addresses:  Vec::new(),
      https_addresses: Vec::new(),
    }
  }

  pub fn add_http_address(&mut self, ip_address: String, port: u16) {
    self.http_addresses.push((ip_address, port))
  }

  pub fn add_https_address(&mut self, ip_address: String, port: u16) {
    self.https_addresses.push((ip_address, port))
  }

  pub fn handle_order(&mut self, order: &Order) {
    match order {
      &Order::AddHttpFront(ref front) => {
        let f = HttpFront {
          app_id:     front.app_id.clone(),
          hostname:   front.hostname.clone(),
          path_begin: front.path_begin.clone(),
        };

        let front = self.http_fronts.entry(front.app_id.clone()).or_insert(vec!());
        if !front.contains(&f) {
          front.push(f);
        }
      },
      &Order::RemoveHttpFront(ref front) => {
        if let Some(front_list) = self.http_fronts.get_mut(&front.app_id) {
          front_list.retain(|el| el.hostname != front.hostname || el.path_begin != front.path_begin);
        }
      },
      &Order::AddCertificate(ref certificate_and_key) => {
        let f = CertificateAndKey {
          certificate:       certificate_and_key.certificate.clone(),
          certificate_chain: certificate_and_key.certificate_chain.clone(),
          key:               certificate_and_key.key.clone(),
        };
        let fingerprint = match X509::from_pem(&certificate_and_key.certificate.as_bytes()[..]).and_then(|cert|
                                  cert.fingerprint(MessageDigest::sha256())) {
          Ok(f)  => f,
          Err(e) => {
            error!("cannot obtain the certificate's fingerprint: {:?}", e);
            return;
          }
        };

        if !self.certificates.contains_key(&fingerprint) {
          self.certificates.insert(fingerprint.clone(), f);
        }
      },
      &Order::RemoveCertificate(ref fingerprint) => {
        self.certificates.remove(fingerprint);
      },
      &Order::AddTlsFront(ref front) => {
        let f = TlsFront {
          app_id:      front.app_id.clone(),
          hostname:    front.hostname.clone(),
          path_begin:  front.path_begin.clone(),
          fingerprint: front.fingerprint.clone(),
        };
        let front = self.https_fronts.entry(front.app_id.clone()).or_insert(vec!());
        if !front.contains(&f) {
          front.push(f);
        }
      },
      &Order::RemoveTlsFront(ref front) => {
        self.https_fronts.remove(&front.app_id);
      },
      &Order::AddInstance(ref instance)  => {
        let instance_vec = self.instances.entry(instance.app_id.clone()).or_insert(vec!());
        if !instance_vec.contains(&instance) {
          instance_vec.push(instance.clone());
        }
      },
      &Order::RemoveInstance(ref instance) => {
        if let Some(instance_list) = self.instances.get_mut(&instance.app_id) {
          instance_list.retain(|el| el.ip_address != instance.ip_address || el.port != instance.port);
        }
      }
      _ => {}
    }
  }

  pub fn generate_orders(&self) -> Vec<Order> {
    let mut v = Vec::new();
    for (app_id, front_list) in self.http_fronts.iter() {
      for front in front_list {
        v.push(Order::AddHttpFront(HttpFront {
          app_id:     app_id.clone(),
          hostname:   front.hostname.clone(),
          path_begin: front.path_begin.clone(),
        }));
      }
    }

    for certificate_and_key in (&self.certificates).values() {
      v.push(Order::AddCertificate(certificate_and_key.clone()));
    }

    for (app_id, front_list) in &self.https_fronts {
      for front in front_list {
        v.push(Order::AddTlsFront(TlsFront {
          app_id:      app_id.clone(),
          hostname:    front.hostname.clone(),
          path_begin:  front.path_begin.clone(),
          fingerprint: front.fingerprint.clone(),
        }));
      }
    }

    for instance_list in self.instances.values() {
      for instance in instance_list {
        v.push(Order::AddInstance(instance.clone()));
      }
    }

    v
  }

  pub fn diff(&self, other:&ConfigState) -> Vec<Order> {
    let mut my_fronts: HashSet<(&AppId, &HttpFront)> = HashSet::new();
    for (ref app_id, ref front_list) in self.http_fronts.iter() {
      for ref front in front_list.iter() {
        my_fronts.insert((&app_id, &front));
      }
    }
    let mut their_fronts: HashSet<(&AppId, &HttpFront)> = HashSet::new();
    for (ref app_id, ref front_list) in other.http_fronts.iter() {
      for ref front in front_list.iter() {
        their_fronts.insert((&app_id, &front));
      }
    }

    let removed_http_fronts = my_fronts.difference(&their_fronts);
    let added_http_fronts   = their_fronts.difference(&my_fronts);

    let mut my_fronts: HashSet<(&AppId, &TlsFront)> = HashSet::new();
    for (ref app_id, ref front_list) in self.https_fronts.iter() {
      for ref front in front_list.iter() {
        my_fronts.insert((&app_id, &front));
      }
    }
    let mut their_fronts: HashSet<(&AppId, &TlsFront)> = HashSet::new();
    for (ref app_id, ref front_list) in other.https_fronts.iter() {
      for ref front in front_list.iter() {
        their_fronts.insert((&app_id, &front));
      }
    }

    let removed_https_fronts = my_fronts.difference(&their_fronts);
    let added_https_fronts   = their_fronts.difference(&my_fronts);

    let mut my_instances: HashSet<(&AppId, &Instance)> = HashSet::new();
    for (ref app_id, ref instance_list) in self.instances.iter() {
      for ref instance in instance_list.iter() {
        my_instances.insert((&app_id, &instance));
      }
    }
    let mut their_instances: HashSet<(&AppId, &Instance)> = HashSet::new();
    for (ref app_id, ref instance_list) in other.instances.iter() {
      for ref instance in instance_list.iter() {
        their_instances.insert((&app_id, &instance));
      }
    }

    let removed_instances = my_instances.difference(&their_instances);
    let added_instances   = their_instances.difference(&my_instances);

    let my_certificates:    HashSet<(&CertFingerprint, &CertificateAndKey)> = HashSet::from_iter(self.certificates.iter());
    let their_certificates: HashSet<(&CertFingerprint, &CertificateAndKey)> = HashSet::from_iter(other.certificates.iter());

    let removed_certificates = my_certificates.difference(&their_certificates);
    let added_certificates   = their_certificates.difference(&my_certificates);

    let mut v = vec!();

    for &(_, certificate_and_key) in added_certificates {
      v.push(Order::AddCertificate(certificate_and_key.clone()));
    }

    for &(app_id, front) in removed_http_fronts {
     v.push(Order::RemoveHttpFront(HttpFront {
       app_id:     app_id.clone(),
       hostname:   front.hostname.clone(),
       path_begin: front.path_begin.clone(),
      }));
    }

    for &(app_id, front) in removed_https_fronts {
     v.push(Order::RemoveTlsFront(TlsFront {
       app_id:      app_id.clone(),
       hostname:    front.hostname.clone(),
       path_begin:  front.path_begin.clone(),
       fingerprint: front.fingerprint.clone(),
      }));
    }

    for &(_, instance) in added_instances {
      v.push(Order::AddInstance(instance.clone()));
    }

    for &(_, instance) in removed_instances {
      v.push(Order::RemoveInstance(instance.clone()));
    }

    for &(app_id, front) in added_http_fronts {
      v.push(Order::AddHttpFront(HttpFront {
        app_id:     app_id.clone(),
        hostname:   front.hostname.clone(),
        path_begin: front.path_begin.clone(),
      }));
    }

    for &(app_id, front) in added_https_fronts {
      v.push(Order::AddTlsFront(TlsFront {
        app_id:      app_id.clone(),
        hostname:    front.hostname.clone(),
        path_begin:  front.path_begin.clone(),
        fingerprint: front.fingerprint.clone(),
      }));
    }

    for  &(fingerprint, _) in removed_certificates {
      v.push(Order::RemoveCertificate(fingerprint.clone()));
    }
    v
  }
}

#[cfg(test)]
mod tests {
  use super::*;
  use sozu::messages::{Order,HttpFront,Instance};

  #[test]
  fn serialize() {
    let mut state:ConfigState = Default::default();
    state.handle_order(&Order::AddHttpFront(HttpFront { app_id: String::from("app_1"), hostname: String::from("lolcatho.st:8080"), path_begin: String::from("/") }));
    state.handle_order(&Order::AddHttpFront(HttpFront { app_id: String::from("app_2"), hostname: String::from("test.local"), path_begin: String::from("/abc") }));
    state.handle_order(&Order::AddInstance(Instance { app_id: String::from("app_1"), ip_address: String::from("127.0.0.1"), port: 1026 }));
    state.handle_order(&Order::AddInstance(Instance { app_id: String::from("app_1"), ip_address: String::from("127.0.0.2"), port: 1027 }));
    state.handle_order(&Order::AddInstance(Instance { app_id: String::from("app_2"), ip_address: String::from("192.167.1.2"), port: 1026 }));
    state.handle_order(&Order::AddInstance(Instance { app_id: String::from("app_1"), ip_address: String::from("192.168.1.3"), port: 1027 }));
    state.handle_order(&Order::RemoveInstance(Instance { app_id: String::from("app_1"), ip_address: String::from("192.168.1.3"), port: 1027 }));

    /*
    let encoded = state.encode();
    println!("serialized:\n{}", encoded);

    let new_state: Option<HttpProxy> = decode_str(&encoded);
    println!("deserialized:\n{:?}", new_state);
    assert_eq!(new_state, Some(state));
    */
    //assert!(false);
  }

  #[test]
  fn diff() {
    let mut state:ConfigState = Default::default();
    state.handle_order(&Order::AddHttpFront(HttpFront { app_id: String::from("app_1"), hostname: String::from("lolcatho.st:8080"), path_begin: String::from("/") }));
    state.handle_order(&Order::AddHttpFront(HttpFront { app_id: String::from("app_2"), hostname: String::from("test.local"), path_begin: String::from("/abc") }));
    state.handle_order(&Order::AddInstance(Instance { app_id: String::from("app_1"), ip_address: String::from("127.0.0.1"), port: 1026 }));
    state.handle_order(&Order::AddInstance(Instance { app_id: String::from("app_1"), ip_address: String::from("127.0.0.2"), port: 1027 }));
    state.handle_order(&Order::AddInstance(Instance { app_id: String::from("app_2"), ip_address: String::from("192.167.1.2"), port: 1026 }));

    let mut state2:ConfigState = Default::default();
    state2.handle_order(&Order::AddHttpFront(HttpFront { app_id: String::from("app_1"), hostname: String::from("lolcatho.st:8080"), path_begin: String::from("/") }));
    state2.handle_order(&Order::AddInstance(Instance { app_id: String::from("app_1"), ip_address: String::from("127.0.0.1"), port: 1026 }));
    state2.handle_order(&Order::AddInstance(Instance { app_id: String::from("app_1"), ip_address: String::from("127.0.0.2"), port: 1027 }));
    state2.handle_order(&Order::AddInstance(Instance { app_id: String::from("app_1"), ip_address: String::from("127.0.0.2"), port: 1028 }));

   let e = vec!(
     Order::RemoveHttpFront(HttpFront { app_id: String::from("app_2"), hostname: String::from("test.local"), path_begin: String::from("/abc") }),
     Order::RemoveInstance(Instance { app_id: String::from("app_2"), ip_address: String::from("192.167.1.2"), port: 1026 }),
     Order::AddInstance(Instance { app_id: String::from("app_1"), ip_address: String::from("127.0.0.2"), port: 1028 }),
   );
   let expected_diff:HashSet<&Order> = HashSet::from_iter(e.iter());

   let d = state.diff(&state2);
   let diff = HashSet::from_iter(d.iter());
   println!("diff orders:\n{:?}\n", diff);
   println!("expected diff orders:\n{:?}\n", expected_diff);

   assert_eq!(diff, expected_diff);
  }
}
<|endoftext|>