<commit_before>extern crate cursive;
extern crate rand;

mod game;

use cursive::Cursive;
use cursive::Printer;
use cursive::direction::Direction;
use cursive::event::{Event, EventResult, MouseButton, MouseEvent};
use cursive::theme::{BaseColor, Color, ColorStyle};
use cursive::vec::Vec2;
use cursive::views::{Button, Dialog, LinearLayout, SelectView, Panel};

fn main() {
    let mut siv = Cursive::new();

    siv.add_layer(
        Dialog::new()
            .title("Minesweeper")
            .padding((2, 2, 1, 1))
            .content(
                LinearLayout::vertical()
                    .child(Button::new_raw("  New game   ", show_options))
                    .child(Button::new_raw(" Best scores ", |s| {
                        s.add_layer(Dialog::info("Not yet!").title("Scores"))
                    }))
                    .child(Button::new_raw("    Exit     ", |s| s.quit())),
            ),
    );

    siv.run();
}

fn show_options(siv: &mut Cursive) {
    siv.add_layer(
        Dialog::new()
            .title("Select difficulty")
            .content(
                SelectView::new()
                    .item(
                        "Easy:      8x8,   10 mines",
                        game::Options {
                            size: Vec2::new(8, 8),
                            mines: 10,
                        },
                    )
                    .item(
                        "Medium:    16x16, 40 mines",
                        game::Options {
                            size: Vec2::new(16, 16),
                            mines: 40,
                        },
                    )
                    .item(
                        "Difficult: 24x24, 99 mines",
                        game::Options {
                            size: Vec2::new(24, 24),
                            mines: 99,
                        },
                    )
                    .on_submit(|s, option| {
                        s.pop_layer();
                        new_game(s, *option);
                    }),
            )
            .button("Back", |s| s.pop_layer()),
    );
}

#[derive(Clone, Copy, PartialEq)]
enum Cell {
    Visible(usize),
    Flag,
    Unknown,
}

struct BoardView {
    // Actual board, unknown to the player.
    board: game::Board,

    // Visible board
    overlay: Vec<Cell>,

    focused: Option<Vec2>,
    missing_mines: usize,
}

impl BoardView {
    pub fn new(options: game::Options) -> Self {
        let overlay = vec![Cell::Unknown; options.size.x * options.size.y];
        let board = game::Board::new(options);
        BoardView {
            board,
            overlay,
            focused: None,
            missing_mines: options.mines,
        }
    }

    fn get_cell(&self, mouse_pos: Vec2, offset: Vec2) -> Option<Vec2> {
        mouse_pos
            .checked_sub(offset)
            .map(|pos| pos.map_x(|x| x / 2))
            .and_then(|pos| {
                if pos.fits_in(self.board.size) {
                    Some(pos)
                } else {
                    None
                }
            })
    }

    fn flag(&mut self, pos: Vec2) {
        if let Some(i) = self.board.cell_id(pos) {
            let new_cell = match self.overlay[i] {
                Cell::Unknown => Cell::Flag,
                Cell::Flag => Cell::Unknown,
                other => other,
            };
            self.overlay[i] = new_cell;
        }
    }

    fn reveal(&mut self, pos: Vec2) -> EventResult {
        if let Some(i) = self.board.cell_id(pos) {
            if self.overlay[i] != Cell::Unknown {
                return EventResult::Consumed(None);
            }

            // Action!
            match self.board.cells[i] {
                game::Cell::Bomb => {
                    return EventResult::with_cb(|s| {
                        s.add_layer(Dialog::text("BOOOM").button("Ok", |s| {
                            s.pop_layer();
                            s.pop_layer();
                        }));
                    })
                }
                game::Cell::Free(n) => {
                    self.overlay[i] = Cell::Visible(n);
                    if n == 0 {
                        // Reveal all surrounding cells
                        for p in self.board.neighbours(pos) {
                            self.reveal(p);
                        }
                    }
                }
            }
        }
        return EventResult::Consumed(None);
    }

    fn auto_reveal(&mut self, pos: Vec2) -> EventResult {
        if let Some(i) = self.board.cell_id(pos) {
            if let Cell::Visible(n) = self.overlay[i] {
                // First: is every possible cell tagged?
                let neighbours = self.board.neighbours(pos);
                let tagged = neighbours
                    .iter()
                    .filter_map(|&pos| self.board.cell_id(pos))
                    .map(|i| self.overlay[i])
                    .filter(|&cell| cell == Cell::Flag)
                    .count();
                if tagged != n {
                    return EventResult::Consumed(None);
                }

                for p in neighbours {
                    let result = self.reveal(p);
                    if result.has_callback() {
                        return result;
                    }
                }
            }
        }

        EventResult::Consumed(None)
    }
}

impl cursive::view::View for BoardView {
    fn draw(&self, printer: &Printer) {
        for (i, cell) in self.overlay.iter().enumerate() {
            let x = (i % self.board.size.x) * 2;
            let y = i / self.board.size.x;

            let text = match *cell {
                Cell::Unknown => "[]",
                Cell::Flag => "()",
                Cell::Visible(n) => {
                    ["  ", " 1", " 2", " 3", " 4", " 5", " 6", " 7", " 8"][n]
                }
            };

            let color = match *cell {
                Cell::Unknown => Color::RgbLowRes(3, 3, 3),
                Cell::Flag => Color::RgbLowRes(4, 4, 2),
                Cell::Visible(1) => Color::RgbLowRes(3, 5, 3),
                Cell::Visible(2) => Color::RgbLowRes(5, 5, 3),
                Cell::Visible(3) => Color::RgbLowRes(5, 4, 3),
                Cell::Visible(4) => Color::RgbLowRes(5, 3, 3),
                Cell::Visible(5) => Color::RgbLowRes(5, 2, 2),
                Cell::Visible(6) => Color::RgbLowRes(5, 0, 1),
                Cell::Visible(7) => Color::RgbLowRes(5, 0, 2),
                Cell::Visible(8) => Color::RgbLowRes(5, 0, 3),
                _ => Color::Dark(BaseColor::White),
            };

            printer.with_color(
                ColorStyle::Custom {
                    back: color,
                    front: Color::Dark(BaseColor::Black),
                },
                |printer| printer.print((x, y), text),
            );
        }
    }

    fn take_focus(&mut self, _: Direction) -> bool {
        true
    }

    fn on_event(&mut self, event: Event) -> EventResult {
        match event {
            Event::Mouse {
                offset,
                position,
                event: MouseEvent::Press(btn),
            } => {
                // Get cell for position
                if let Some(pos) = self.get_cell(position, offset) {
                    self.focused = Some(pos);
                    return EventResult::Consumed(None);
                }
            }
            Event::Mouse {
                offset,
                position,
                event: MouseEvent::Release(btn),
            } => {
                // Get cell for position
                if let Some(pos) = self.get_cell(position, offset) {
                    if self.focused == Some(pos) {
                        // We got a click here!
                        match btn {
                            MouseButton::Left => return self.reveal(pos),
                            MouseButton::Right => {
                                self.flag(pos);
                                return EventResult::Consumed(None);
                            }
                            MouseButton::Middle => {
                                return self.auto_reveal(pos);
                            }
                            _ => (),
                        }
                    }

                    self.focused = None;
                }
            }
            _ => (),
        }

        EventResult::Ignored
    }

    fn required_size(&mut self, _: Vec2) -> Vec2 {
        self.board.size.map_x(|x| 2 * x)
    }
}

fn new_game(siv: &mut Cursive, options: game::Options) {
    let board = game::Board::new(options);

    siv.add_layer(
        Dialog::new()
            .title("Minesweeper")
            .content(
                LinearLayout::horizontal()
                    .child(Panel::new(BoardView::new(options))),
            )
            .button("Quit game", |s| {
                s.pop_layer();
            }),
    );
}
<commit_msg>Mines example: add help message<commit_after>extern crate cursive;
extern crate rand;

mod game;

use cursive::Cursive;
use cursive::Printer;
use cursive::direction::Direction;
use cursive::event::{Event, EventResult, MouseButton, MouseEvent};
use cursive::theme::{BaseColor, Color, ColorStyle};
use cursive::vec::Vec2;
use cursive::views::{Button, Dialog, LinearLayout, Panel, SelectView};

fn main() {
    let mut siv = Cursive::new();

    siv.add_layer(
        Dialog::new()
            .title("Minesweeper")
            .padding((2, 2, 1, 1))
            .content(
                LinearLayout::vertical()
                    .child(Button::new_raw("  New game   ", show_options))
                    .child(Button::new_raw(" Best scores ", |s| {
                        s.add_layer(Dialog::info("Not yet!").title("Scores"))
                    }))
                    .child(Button::new_raw("    Exit     ", |s| s.quit())),
            ),
    );

    siv.run();
}

fn show_options(siv: &mut Cursive) {
    siv.add_layer(
        Dialog::new()
            .title("Select difficulty")
            .content(
                SelectView::new()
                    .item(
                        "Easy:      8x8,   10 mines",
                        game::Options {
                            size: Vec2::new(8, 8),
                            mines: 10,
                        },
                    )
                    .item(
                        "Medium:    16x16, 40 mines",
                        game::Options {
                            size: Vec2::new(16, 16),
                            mines: 40,
                        },
                    )
                    .item(
                        "Difficult: 24x24, 99 mines",
                        game::Options {
                            size: Vec2::new(24, 24),
                            mines: 99,
                        },
                    )
                    .on_submit(|s, option| {
                        s.pop_layer();
                        new_game(s, *option);
                    }),
            )
            .button("Back", |s| s.pop_layer()),
    );
}

#[derive(Clone, Copy, PartialEq)]
enum Cell {
    Visible(usize),
    Flag,
    Unknown,
}

struct BoardView {
    // Actual board, unknown to the player.
    board: game::Board,

    // Visible board
    overlay: Vec<Cell>,

    focused: Option<Vec2>,
    missing_mines: usize,
}

impl BoardView {
    pub fn new(options: game::Options) -> Self {
        let overlay = vec![Cell::Unknown; options.size.x * options.size.y];
        let board = game::Board::new(options);
        BoardView {
            board,
            overlay,
            focused: None,
            missing_mines: options.mines,
        }
    }

    fn get_cell(&self, mouse_pos: Vec2, offset: Vec2) -> Option<Vec2> {
        mouse_pos
            .checked_sub(offset)
            .map(|pos| pos.map_x(|x| x / 2))
            .and_then(|pos| {
                if pos.fits_in(self.board.size) {
                    Some(pos)
                } else {
                    None
                }
            })
    }

    fn flag(&mut self, pos: Vec2) {
        if let Some(i) = self.board.cell_id(pos) {
            let new_cell = match self.overlay[i] {
                Cell::Unknown => Cell::Flag,
                Cell::Flag => Cell::Unknown,
                other => other,
            };
            self.overlay[i] = new_cell;
        }
    }

    fn reveal(&mut self, pos: Vec2) -> EventResult {
        if let Some(i) = self.board.cell_id(pos) {
            if self.overlay[i] != Cell::Unknown {
                return EventResult::Consumed(None);
            }

            // Action!
            match self.board.cells[i] {
                game::Cell::Bomb => {
                    return EventResult::with_cb(|s| {
                        s.add_layer(Dialog::text("BOOOM").button("Ok", |s| {
                            s.pop_layer();
                            s.pop_layer();
                        }));
                    })
                }
                game::Cell::Free(n) => {
                    self.overlay[i] = Cell::Visible(n);
                    if n == 0 {
                        // Reveal all surrounding cells
                        for p in self.board.neighbours(pos) {
                            self.reveal(p);
                        }
                    }
                }
            }
        }
        return EventResult::Consumed(None);
    }

    fn auto_reveal(&mut self, pos: Vec2) -> EventResult {
        if let Some(i) = self.board.cell_id(pos) {
            if let Cell::Visible(n) = self.overlay[i] {
                // First: is every possible cell tagged?
                let neighbours = self.board.neighbours(pos);
                let tagged = neighbours
                    .iter()
                    .filter_map(|&pos| self.board.cell_id(pos))
                    .map(|i| self.overlay[i])
                    .filter(|&cell| cell == Cell::Flag)
                    .count();
                if tagged != n {
                    return EventResult::Consumed(None);
                }

                for p in neighbours {
                    let result = self.reveal(p);
                    if result.has_callback() {
                        return result;
                    }
                }
            }
        }

        EventResult::Consumed(None)
    }
}

impl cursive::view::View for BoardView {
    fn draw(&self, printer: &Printer) {
        for (i, cell) in self.overlay.iter().enumerate() {
            let x = (i % self.board.size.x) * 2;
            let y = i / self.board.size.x;

            let text = match *cell {
                Cell::Unknown => "[]",
                Cell::Flag => "()",
                Cell::Visible(n) => {
                    ["  ", " 1", " 2", " 3", " 4", " 5", " 6", " 7", " 8"][n]
                }
            };

            let color = match *cell {
                Cell::Unknown => Color::RgbLowRes(3, 3, 3),
                Cell::Flag => Color::RgbLowRes(4, 4, 2),
                Cell::Visible(1) => Color::RgbLowRes(3, 5, 3),
                Cell::Visible(2) => Color::RgbLowRes(5, 5, 3),
                Cell::Visible(3) => Color::RgbLowRes(5, 4, 3),
                Cell::Visible(4) => Color::RgbLowRes(5, 3, 3),
                Cell::Visible(5) => Color::RgbLowRes(5, 2, 2),
                Cell::Visible(6) => Color::RgbLowRes(5, 0, 1),
                Cell::Visible(7) => Color::RgbLowRes(5, 0, 2),
                Cell::Visible(8) => Color::RgbLowRes(5, 0, 3),
                _ => Color::Dark(BaseColor::White),
            };

            printer.with_color(
                ColorStyle::Custom {
                    back: color,
                    front: Color::Dark(BaseColor::Black),
                },
                |printer| printer.print((x, y), text),
            );
        }
    }

    fn take_focus(&mut self, _: Direction) -> bool {
        true
    }

    fn on_event(&mut self, event: Event) -> EventResult {
        match event {
            Event::Mouse {
                offset,
                position,
                event: MouseEvent::Press(btn),
            } => {
                // Get cell for position
                if let Some(pos) = self.get_cell(position, offset) {
                    self.focused = Some(pos);
                    return EventResult::Consumed(None);
                }
            }
            Event::Mouse {
                offset,
                position,
                event: MouseEvent::Release(btn),
            } => {
                // Get cell for position
                if let Some(pos) = self.get_cell(position, offset) {
                    if self.focused == Some(pos) {
                        // We got a click here!
                        match btn {
                            MouseButton::Left => return self.reveal(pos),
                            MouseButton::Right => {
                                self.flag(pos);
                                return EventResult::Consumed(None);
                            }
                            MouseButton::Middle => {
                                return self.auto_reveal(pos);
                            }
                            _ => (),
                        }
                    }

                    self.focused = None;
                }
            }
            _ => (),
        }

        EventResult::Ignored
    }

    fn required_size(&mut self, _: Vec2) -> Vec2 {
        self.board.size.map_x(|x| 2 * x)
    }
}

fn new_game(siv: &mut Cursive, options: game::Options) {
    let board = game::Board::new(options);

    siv.add_layer(
        Dialog::new()
            .title("Minesweeper")
            .content(
                LinearLayout::horizontal()
                    .child(Panel::new(BoardView::new(options))),
            )
            .button("Quit game", |s| {
                s.pop_layer();
            }),
    );

    siv.add_layer(Dialog::info(
        "Controls:
Reveal cell:                  left click
Mark as mine:                 right-click
Reveal nearby unmarked cells: middle-click",
    ));
}
<|endoftext|>


<commit_before>extern crate mqtt;
#[macro_use]
extern crate log;
extern crate env_logger;
extern crate clap;
extern crate uuid;

use std::net::TcpStream;
use std::io::{self, Write};

use clap::{App, Arg};

use uuid::Uuid;

use mqtt::{Encodable, Decodable, QualityOfService};
use mqtt::packet::*;
use mqtt::control::variable_header::ConnectReturnCode;
use mqtt::TopicFilter;
use mqtt::control::variable_header::TopicName;

fn generate_client_id() -> String {
    format!("/MQTT/rust/{}", Uuid::new_v4().to_simple_string())
}

fn main() {
    env_logger::init().unwrap();

    let matches = App::new("sub-client")
                    .author("Y. T. Chung <zonyitoo@gmail.com>")
                    .arg(Arg::with_name("HOST").short("h").long("host").takes_value(true).required(true)
                            .help("MQTT host"))
                    .arg(Arg::with_name("SUBSCRIBE").short("s").long("subscribe").takes_value(true)
                            .multiple(true).required(true).help("Channel filter to subscribe"))
                    .arg(Arg::with_name("USER_NAME").short("u").long("username").takes_value(true)
                            .help("Login user name"))
                    .arg(Arg::with_name("PASSWORD").short("p").long("password").takes_value(true)
                            .help("Password"))
                    .arg(Arg::with_name("CLIENT_ID").short("i").long("client-identifier").takes_value(true)
                            .help("Client identifier"))
                    .get_matches();

    let server_addr = matches.value_of("HOST").unwrap();
    let client_id = matches.value_of("CLIENT_ID")
        .map(|x| x.to_owned())
        .unwrap_or_else(generate_client_id);
    let channel_filters: Vec<(TopicFilter, QualityOfService)>
        = matches.values_of("SUBSCRIBE").unwrap()
                 .iter()
                 .map(|c| (TopicFilter::new_checked(c.to_string()).unwrap(), QualityOfService::Level0))
                 .collect();

    print!("Connecting to {:?} ... ", server_addr);
    let mut stream = TcpStream::connect(server_addr).unwrap();
    println!("Connected!");

    println!("Client identifier {:?}", client_id);
    let mut conn = ConnectPacket::new(client_id.to_owned());
    conn.set_clean_session(true);
    let mut buf = Vec::new();
    conn.encode(&mut buf).unwrap();
    stream.write_all(&buf[..]).unwrap();

    let connack = ConnackPacket::decode(&mut stream).unwrap();
    trace!("CONNACK {:?}", connack);

    if connack.connect_return_code() != ConnectReturnCode::ConnectionAccepted {
        panic!("Failed to connect to server, return code {:?}", connack.connect_return_code());
    }

    println!("Applying channel filters {:?} ...", channel_filters);
    let sub = SubscribePacket::new(10, channel_filters);
    let mut buf = Vec::new();
    sub.encode(&mut buf).unwrap();
    stream.write_all(&buf[..]).unwrap();

    let channels: Vec<TopicName> = matches.values_of("SUBSCRIBE").unwrap()
                                          .iter().map(|c| TopicName::new(c.to_string()).unwrap())
                                          .collect();

    let user_name = matches.value_of("USER_NAME").unwrap_or("<anonym>");

    let mut stdin = io::stdin();
    loop {
        print!(">> ");
        io::stdout().flush().unwrap();

        let mut line = String::new();
        stdin.read_line(&mut line).unwrap();

        match line.trim() {
            "" => continue,
            _ => {},
        }

        let message = format!("{}: {}", user_name, line.trim());

        for chan in channels.iter() {
            let publish_packet = PublishPacket::new(chan.clone(), QoSWithPacketIdentifier::Level0,
                                                    message.as_bytes().to_vec());
            let mut buf = Vec::new();
            publish_packet.encode(&mut buf).unwrap();
            stream.write_all(&buf[..]).unwrap();
        }
    }
}
<commit_msg>make user name as line start<commit_after>extern crate mqtt;
#[macro_use]
extern crate log;
extern crate env_logger;
extern crate clap;
extern crate uuid;

use std::net::TcpStream;
use std::io::{self, Write};

use clap::{App, Arg};

use uuid::Uuid;

use mqtt::{Encodable, Decodable, QualityOfService};
use mqtt::packet::*;
use mqtt::control::variable_header::ConnectReturnCode;
use mqtt::TopicFilter;
use mqtt::control::variable_header::TopicName;

fn generate_client_id() -> String {
    format!("/MQTT/rust/{}", Uuid::new_v4().to_simple_string())
}

fn main() {
    env_logger::init().unwrap();

    let matches = App::new("sub-client")
                    .author("Y. T. Chung <zonyitoo@gmail.com>")
                    .arg(Arg::with_name("HOST").short("h").long("host").takes_value(true).required(true)
                            .help("MQTT host"))
                    .arg(Arg::with_name("SUBSCRIBE").short("s").long("subscribe").takes_value(true)
                            .multiple(true).required(true).help("Channel filter to subscribe"))
                    .arg(Arg::with_name("USER_NAME").short("u").long("username").takes_value(true)
                            .help("Login user name"))
                    .arg(Arg::with_name("PASSWORD").short("p").long("password").takes_value(true)
                            .help("Password"))
                    .arg(Arg::with_name("CLIENT_ID").short("i").long("client-identifier").takes_value(true)
                            .help("Client identifier"))
                    .get_matches();

    let server_addr = matches.value_of("HOST").unwrap();
    let client_id = matches.value_of("CLIENT_ID")
        .map(|x| x.to_owned())
        .unwrap_or_else(generate_client_id);
    let channel_filters: Vec<(TopicFilter, QualityOfService)>
        = matches.values_of("SUBSCRIBE").unwrap()
                 .iter()
                 .map(|c| (TopicFilter::new_checked(c.to_string()).unwrap(), QualityOfService::Level0))
                 .collect();

    print!("Connecting to {:?} ... ", server_addr);
    let mut stream = TcpStream::connect(server_addr).unwrap();
    println!("Connected!");

    println!("Client identifier {:?}", client_id);
    let mut conn = ConnectPacket::new(client_id.to_owned());
    conn.set_clean_session(true);
    let mut buf = Vec::new();
    conn.encode(&mut buf).unwrap();
    stream.write_all(&buf[..]).unwrap();

    let connack = ConnackPacket::decode(&mut stream).unwrap();
    trace!("CONNACK {:?}", connack);

    if connack.connect_return_code() != ConnectReturnCode::ConnectionAccepted {
        panic!("Failed to connect to server, return code {:?}", connack.connect_return_code());
    }

    println!("Applying channel filters {:?} ...", channel_filters);
    let sub = SubscribePacket::new(10, channel_filters);
    let mut buf = Vec::new();
    sub.encode(&mut buf).unwrap();
    stream.write_all(&buf[..]).unwrap();

    let channels: Vec<TopicName> = matches.values_of("SUBSCRIBE").unwrap()
                                          .iter().map(|c| TopicName::new(c.to_string()).unwrap())
                                          .collect();

    let user_name = matches.value_of("USER_NAME").unwrap_or("<anonym>");

    let mut stdin = io::stdin();
    loop {
        print!("{}: ", user_name);
        io::stdout().flush().unwrap();

        let mut line = String::new();
        stdin.read_line(&mut line).unwrap();

        match line.trim() {
            "" => continue,
            _ => {},
        }

        let message = format!("{}: {}", user_name, line.trim());

        for chan in channels.iter() {
            let publish_packet = PublishPacket::new(chan.clone(), QoSWithPacketIdentifier::Level0,
                                                    message.as_bytes().to_vec());
            let mut buf = Vec::new();
            publish_packet.encode(&mut buf).unwrap();
            stream.write_all(&buf[..]).unwrap();
        }
    }
}
<|endoftext|>


<commit_before>use std::sync::{Once, ONCE_INIT};
use std::ffi::CString;
use std::path::Path;
use std::ptr::null;
use libc::c_int;
use vector::{ogr, Layer};

static START: Once = ONCE_INIT;
static mut registered_drivers: bool = false;

fn register_drivers() {
    unsafe {
        START.call_once(|| {
            ogr::OGRRegisterAll();
            registered_drivers = true;
        });
    }
}

pub struct Dataset {
    c_dataset: *const (),
}

impl Dataset {
    pub fn open(path: &Path) -> Option<Dataset> {
        register_drivers();
        let filename = path.to_str().unwrap();
        let c_filename = CString::new(filename.as_bytes()).unwrap();
        let c_dataset = unsafe { ogr::OGROpen(c_filename.as_ptr(), 0, null()) };
        return match c_dataset.is_null() {
            true  => None,
            false => Some(Dataset{c_dataset: c_dataset}),
        };
    }

    pub fn count(&self) -> isize {
        return unsafe { ogr::OGR_DS_GetLayerCount(self.c_dataset) } as isize;
    }

    pub fn layer<'a>(&'a self, idx: isize) -> Option<Layer<'a>> {
        let c_layer = unsafe { ogr::OGR_DS_GetLayer(self.c_dataset, idx as c_int) };
        return match c_layer.is_null() {
            true  => None,
            false => Some(unsafe { Layer::_with_dataset(self, c_layer) }),
        };
    }
}

impl Drop for Dataset {
    fn drop(&mut self) {
        unsafe { ogr::OGR_DS_Destroy(self.c_dataset); }
    }
}
<commit_msg>doc for Dataset<commit_after>use std::sync::{Once, ONCE_INIT};
use std::ffi::CString;
use std::path::Path;
use std::ptr::null;
use libc::c_int;
use vector::{ogr, Layer};

static START: Once = ONCE_INIT;
static mut registered_drivers: bool = false;

fn register_drivers() {
    unsafe {
        START.call_once(|| {
            ogr::OGRRegisterAll();
            registered_drivers = true;
        });
    }
}

/// Vector dataset
///
/// ```
/// use std::path::Path;
/// use gdal::vector::Dataset;
///
/// let dataset = Dataset::open(Path::new("fixtures/roads.geojson")).unwrap();
/// println!("Dataset has {} layers", dataset.count());
/// ```
pub struct Dataset {
    c_dataset: *const (),
}

impl Dataset {
    /// Open the dataset at `path`.
    pub fn open(path: &Path) -> Option<Dataset> {
        register_drivers();
        let filename = path.to_str().unwrap();
        let c_filename = CString::new(filename.as_bytes()).unwrap();
        let c_dataset = unsafe { ogr::OGROpen(c_filename.as_ptr(), 0, null()) };
        return match c_dataset.is_null() {
            true  => None,
            false => Some(Dataset{c_dataset: c_dataset}),
        };
    }

    /// Get number of layers.
    pub fn count(&self) -> isize {
        return unsafe { ogr::OGR_DS_GetLayerCount(self.c_dataset) } as isize;
    }

    /// Get layer number `idx`.
    pub fn layer<'a>(&'a self, idx: isize) -> Option<Layer<'a>> {
        let c_layer = unsafe { ogr::OGR_DS_GetLayer(self.c_dataset, idx as c_int) };
        return match c_layer.is_null() {
            true  => None,
            false => Some(unsafe { Layer::_with_dataset(self, c_layer) }),
        };
    }
}

impl Drop for Dataset {
    fn drop(&mut self) {
        unsafe { ogr::OGR_DS_Destroy(self.c_dataset); }
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2016 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

//! Describes a graphical or compute operation.
//!
//! In Vulkan before you can add a draw or a compute command to a command buffer, you have to
//! create a *pipeline object* that describes it.
//!
//! There are two kinds of pipelines:
//!
//! - `ComputePipeline`s, for compute operations that reads/write raw pixels from images and
//!   buffers.
//! - `GraphicsPipeline`s, for graphical operations.
//!
//! All the sub-modules of this module (with the exception of `cache`) correspond to the various
//! steps of pipelines.

pub use self::compute_pipeline::ComputePipeline;
pub use self::graphics_pipeline::GraphicsPipeline;
pub use self::graphics_pipeline::GraphicsPipelineParams;
pub use self::graphics_pipeline::GraphicsPipelineCreationError;

mod compute_pipeline;
mod graphics_pipeline;

pub mod blend;
pub mod cache;
pub mod depth_stencil;
pub mod input_assembly;
pub mod multisample;
pub mod raster;
pub mod shader;
pub mod vertex;
pub mod viewport;
<commit_msg>Fix missing export<commit_after>// Copyright (c) 2016 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

//! Describes a graphical or compute operation.
//!
//! In Vulkan before you can add a draw or a compute command to a command buffer, you have to
//! create a *pipeline object* that describes it.
//!
//! There are two kinds of pipelines:
//!
//! - `ComputePipeline`s, for compute operations that reads/write raw pixels from images and
//!   buffers.
//! - `GraphicsPipeline`s, for graphical operations.
//!
//! All the sub-modules of this module (with the exception of `cache`) correspond to the various
//! steps of pipelines.

pub use self::compute_pipeline::ComputePipeline;
pub use self::graphics_pipeline::GraphicsPipeline;
pub use self::graphics_pipeline::GraphicsPipelineParams;
pub use self::graphics_pipeline::GraphicsPipelineParamsTess;
pub use self::graphics_pipeline::GraphicsPipelineCreationError;

mod compute_pipeline;
mod graphics_pipeline;

pub mod blend;
pub mod cache;
pub mod depth_stencil;
pub mod input_assembly;
pub mod multisample;
pub mod raster;
pub mod shader;
pub mod vertex;
pub mod viewport;
<|endoftext|>


<commit_before>use base64;
use std::borrow::Cow;
use std::io::Write;
use xml_rs::attribute::Attribute;
use xml_rs::escape::escape_str_pcdata;
use xml_rs::name::Name;
use xml_rs::namespace::Namespace;
use xml_rs::writer::events::XmlEvent as WriteXmlEvent;
use xml_rs::writer::{EmitterConfig, Error as XmlWriterError, EventWriter as XmlEventWriter};

use {Error, EventWriter as PlistEventWriter, PlistEvent, Result};

impl From<XmlWriterError> for Error {
    fn from(err: XmlWriterError) -> Error {
        match err {
            XmlWriterError::Io(err) => Error::Io(err),
            _ => Error::InvalidData,
        }
    }
}

enum Element {
    Dictionary(DictionaryState),
    Array,
    Root,
}

enum DictionaryState {
    ExpectKey,
    ExpectValue,
}

pub struct EventWriter<W: Write> {
    xml_writer: XmlEventWriter<W>,
    stack: Vec<Element>,
    // Not very nice
    empty_namespace: Namespace,
}

impl<W: Write> EventWriter<W> {
    pub fn new(writer: W) -> EventWriter<W> {
        let mut config = EmitterConfig::new()
            .line_separator("\n")
            .indent_string("\t")
            .perform_indent(true)
            .write_document_declaration(false)
            .normalize_empty_elements(true)
            .cdata_to_characters(true)
            .keep_element_names_stack(false)
            .autopad_comments(true);
        config.perform_escaping = false;

        EventWriter {
            xml_writer: XmlEventWriter::new_with_config(writer, config),
            stack: Vec::new(),
            empty_namespace: Namespace::empty(),
        }
    }

    fn write_element_and_value(&mut self, name: &str, value: &str) -> Result<()> {
        self.start_element(name)?;
        self.write_value(value)?;
        self.end_element(name)?;
        Ok(())
    }

    fn start_element(&mut self, name: &str) -> Result<()> {
        self.xml_writer.write(WriteXmlEvent::StartElement {
            name: Name::local(name),
            attributes: Cow::Borrowed(&[]),
            namespace: Cow::Borrowed(&self.empty_namespace),
        })?;
        Ok(())
    }

    fn end_element(&mut self, name: &str) -> Result<()> {
        self.xml_writer.write(WriteXmlEvent::EndElement {
            name: Some(Name::local(name)),
        })?;
        Ok(())
    }

    fn write_value(&mut self, value: &str) -> Result<()> {
        self.xml_writer
            .write(WriteXmlEvent::Characters(&escape_str_pcdata(value)))?;
        Ok(())
    }

    fn maybe_end_plist(&mut self) -> Result<()> {
        // If there are no more open tags then write the </plist> element
        if self.stack.len() == 1 {
            self.end_element("plist")?;
            if let Some(Element::Root) = self.stack.pop() {
            } else {
                return Err(Error::InvalidData);
            }
        }
        Ok(())
    }

    pub fn write(&mut self, event: &PlistEvent) -> Result<()> {
        <Self as PlistEventWriter>::write(self, event)
    }
}

impl<W: Write> PlistEventWriter for EventWriter<W> {
    fn write(&mut self, event: &PlistEvent) -> Result<()> {
        match self.stack.pop() {
            Some(Element::Dictionary(DictionaryState::ExpectKey)) => {
                match *event {
                    PlistEvent::StringValue(ref value) => {
                        self.write_element_and_value("key", &*value)?;
                        self.stack
                            .push(Element::Dictionary(DictionaryState::ExpectValue));
                    }
                    PlistEvent::EndDictionary => {
                        self.end_element("dict")?;
                        // We might be closing the last tag here as well
                        self.maybe_end_plist()?;
                    }
                    _ => return Err(Error::InvalidData),
                };
                return Ok(());
            }
            Some(Element::Dictionary(DictionaryState::ExpectValue)) => self.stack
                .push(Element::Dictionary(DictionaryState::ExpectKey)),
            Some(other) => self.stack.push(other),
            None => {
                // Write prologue
                let prologue = r#"<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
"#;
                self.xml_writer.write(WriteXmlEvent::Characters(prologue))?;

                let version_name = Name::local("version");
                let version_attr = Attribute::new(version_name, "1.0");

                self.xml_writer.write(WriteXmlEvent::StartElement {
                    name: Name::local("plist"),
                    attributes: Cow::Borrowed(&[version_attr]),
                    namespace: Cow::Borrowed(&self.empty_namespace),
                })?;

                self.stack.push(Element::Root);
            }
        }

        match *event {
            PlistEvent::StartArray(_) => {
                self.start_element("array")?;
                self.stack.push(Element::Array);
            }
            PlistEvent::EndArray => {
                self.end_element("array")?;
                if let Some(Element::Array) = self.stack.pop() {
                } else {
                    return Err(Error::InvalidData);
                }
            }

            PlistEvent::StartDictionary(_) => {
                self.start_element("dict")?;
                self.stack
                    .push(Element::Dictionary(DictionaryState::ExpectKey));
            }
            PlistEvent::EndDictionary => return Err(Error::InvalidData),

            PlistEvent::BooleanValue(true) => {
                self.start_element("true")?;
                self.end_element("true")?;
            }
            PlistEvent::BooleanValue(false) => {
                self.start_element("false")?;
                self.end_element("false")?;
            }
            PlistEvent::DataValue(ref value) => {
                let base64_data = base64::encode_config(&value, base64::MIME);
                self.write_element_and_value("data", &base64_data)?;
            }
            PlistEvent::DateValue(ref value) => {
                self.write_element_and_value("date", &value.to_rfc3339())?
            }
            PlistEvent::IntegerValue(ref value) => {
                self.write_element_and_value("integer", &value.to_string())?
            }
            PlistEvent::RealValue(ref value) => {
                self.write_element_and_value("real", &value.to_string())?
            }
            PlistEvent::StringValue(ref value) => self.write_element_and_value("string", &*value)?,
        };

        self.maybe_end_plist()?;

        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use humantime::parse_rfc3339_weak;
    use std::io::Cursor;

    use super::*;

    #[test]
    fn streaming_parser() {
        use PlistEvent::*;

        let plist = &[
            StartDictionary(None),
            StringValue("Author".to_owned()),
            StringValue("William Shakespeare".to_owned()),
            StringValue("Lines".to_owned()),
            StartArray(None),
            StringValue("It is a tale told by an idiot,".to_owned()),
            StringValue("Full of sound and fury, signifying nothing.".to_owned()),
            EndArray,
            StringValue("Death".to_owned()),
            IntegerValue(1564),
            StringValue("Height".to_owned()),
            RealValue(1.60),
            StringValue("Data".to_owned()),
            DataValue(vec![0, 0, 0, 190, 0, 0, 0, 3, 0, 0, 0, 30, 0, 0, 0]),
            StringValue("Birthdate".to_owned()),
            DateValue(parse_rfc3339_weak("1981-05-16 11:32:06").unwrap().into()),
            EndDictionary,
        ];

        let mut cursor = Cursor::new(Vec::new());

        {
            let mut plist_w = EventWriter::new(&mut cursor);

            for item in plist {
                plist_w.write(item).unwrap();
            }
        }

        let comparison = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">
<plist version=\"1.0\">
\t<dict>
\t\t<key>Author</key>
\t\t<string>William Shakespeare</string>
\t\t<key>Lines</key>
\t\t<array>
\t\t\t<string>It is a tale told by an idiot,</string>
\t\t\t<string>Full of sound and fury, signifying nothing.</string>
\t\t</array>
\t\t<key>Death</key>
\t\t<integer>1564</integer>
\t\t<key>Height</key>
\t\t<real>1.6</real>
\t\t<key>Data</key>
\t\t<data>AAAAvgAAAAMAAAAeAAAA</data>
\t\t<key>Birthdate</key>
\t\t<date>1981-05-16T11:32:06Z</date>
\t</dict>
</plist>";

        let s = String::from_utf8(cursor.into_inner()).unwrap();

        assert_eq!(s, comparison);
    }
}
<commit_msg>Stop indenting the child of the <plist> tag<commit_after>use base64;
use std::borrow::Cow;
use std::io::Write;
use xml_rs::escape::escape_str_pcdata;
use xml_rs::name::Name;
use xml_rs::namespace::Namespace;
use xml_rs::writer::events::XmlEvent as WriteXmlEvent;
use xml_rs::writer::{EmitterConfig, Error as XmlWriterError, EventWriter as XmlEventWriter};

use {Error, EventWriter as PlistEventWriter, PlistEvent, Result};

impl From<XmlWriterError> for Error {
    fn from(err: XmlWriterError) -> Error {
        match err {
            XmlWriterError::Io(err) => Error::Io(err),
            _ => Error::InvalidData,
        }
    }
}

enum Element {
    Dictionary(DictionaryState),
    Array,
    Root,
}

enum DictionaryState {
    ExpectKey,
    ExpectValue,
}

pub struct EventWriter<W: Write> {
    xml_writer: XmlEventWriter<W>,
    stack: Vec<Element>,
    // Not very nice
    empty_namespace: Namespace,
}

impl<W: Write> EventWriter<W> {
    pub fn new(writer: W) -> EventWriter<W> {
        let mut config = EmitterConfig::new()
            .line_separator("\n")
            .indent_string("\t")
            .perform_indent(true)
            .write_document_declaration(false)
            .normalize_empty_elements(true)
            .cdata_to_characters(true)
            .keep_element_names_stack(false)
            .autopad_comments(true);
        config.perform_escaping = false;

        EventWriter {
            xml_writer: XmlEventWriter::new_with_config(writer, config),
            stack: Vec::new(),
            empty_namespace: Namespace::empty(),
        }
    }

    fn write_element_and_value(&mut self, name: &str, value: &str) -> Result<()> {
        self.start_element(name)?;
        self.write_value(value)?;
        self.end_element(name)?;
        Ok(())
    }

    fn start_element(&mut self, name: &str) -> Result<()> {
        self.xml_writer.write(WriteXmlEvent::StartElement {
            name: Name::local(name),
            attributes: Cow::Borrowed(&[]),
            namespace: Cow::Borrowed(&self.empty_namespace),
        })?;
        Ok(())
    }

    fn end_element(&mut self, name: &str) -> Result<()> {
        self.xml_writer.write(WriteXmlEvent::EndElement {
            name: Some(Name::local(name)),
        })?;
        Ok(())
    }

    fn write_value(&mut self, value: &str) -> Result<()> {
        self.xml_writer
            .write(WriteXmlEvent::Characters(&escape_str_pcdata(value)))?;
        Ok(())
    }

    fn maybe_end_plist(&mut self) -> Result<()> {
        // If there are no more open tags then write the </plist> element
        if self.stack.len() == 1 {
            // We didn't tell the xml_writer about the <plist> tag so it thinks we're already at
            // the root. As such, it's not going to prettify our output, so we need to include
            // the newline ourselves.
            self.xml_writer.write(WriteXmlEvent::Characters("\n"))?;
            self.end_element("plist")?;
            if let Some(Element::Root) = self.stack.pop() {
            } else {
                return Err(Error::InvalidData);
            }
        }
        Ok(())
    }

    pub fn write(&mut self, event: &PlistEvent) -> Result<()> {
        <Self as PlistEventWriter>::write(self, event)
    }
}

impl<W: Write> PlistEventWriter for EventWriter<W> {
    fn write(&mut self, event: &PlistEvent) -> Result<()> {
        match self.stack.pop() {
            Some(Element::Dictionary(DictionaryState::ExpectKey)) => {
                match *event {
                    PlistEvent::StringValue(ref value) => {
                        self.write_element_and_value("key", &*value)?;
                        self.stack
                            .push(Element::Dictionary(DictionaryState::ExpectValue));
                    }
                    PlistEvent::EndDictionary => {
                        self.end_element("dict")?;
                        // We might be closing the last tag here as well
                        self.maybe_end_plist()?;
                    }
                    _ => return Err(Error::InvalidData),
                };
                return Ok(());
            }
            Some(Element::Dictionary(DictionaryState::ExpectValue)) => self.stack
                .push(Element::Dictionary(DictionaryState::ExpectKey)),
            Some(other) => self.stack.push(other),
            None => {
                // Write prologue
                let prologue = r#"<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
"#;
                self.xml_writer.write(WriteXmlEvent::Characters(prologue))?;

                self.stack.push(Element::Root);
            }
        }

        match *event {
            PlistEvent::StartArray(_) => {
                self.start_element("array")?;
                self.stack.push(Element::Array);
            }
            PlistEvent::EndArray => {
                self.end_element("array")?;
                if let Some(Element::Array) = self.stack.pop() {
                } else {
                    return Err(Error::InvalidData);
                }
            }

            PlistEvent::StartDictionary(_) => {
                self.start_element("dict")?;
                self.stack
                    .push(Element::Dictionary(DictionaryState::ExpectKey));
            }
            PlistEvent::EndDictionary => return Err(Error::InvalidData),

            PlistEvent::BooleanValue(true) => {
                self.start_element("true")?;
                self.end_element("true")?;
            }
            PlistEvent::BooleanValue(false) => {
                self.start_element("false")?;
                self.end_element("false")?;
            }
            PlistEvent::DataValue(ref value) => {
                let base64_data = base64::encode_config(&value, base64::MIME);
                self.write_element_and_value("data", &base64_data)?;
            }
            PlistEvent::DateValue(ref value) => {
                self.write_element_and_value("date", &value.to_rfc3339())?
            }
            PlistEvent::IntegerValue(ref value) => {
                self.write_element_and_value("integer", &value.to_string())?
            }
            PlistEvent::RealValue(ref value) => {
                self.write_element_and_value("real", &value.to_string())?
            }
            PlistEvent::StringValue(ref value) => self.write_element_and_value("string", &*value)?,
        };

        self.maybe_end_plist()?;

        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use humantime::parse_rfc3339_weak;
    use std::io::Cursor;

    use super::*;

    #[test]
    fn streaming_parser() {
        use PlistEvent::*;

        let plist = &[
            StartDictionary(None),
            StringValue("Author".to_owned()),
            StringValue("William Shakespeare".to_owned()),
            StringValue("Lines".to_owned()),
            StartArray(None),
            StringValue("It is a tale told by an idiot,".to_owned()),
            StringValue("Full of sound and fury, signifying nothing.".to_owned()),
            EndArray,
            StringValue("Death".to_owned()),
            IntegerValue(1564),
            StringValue("Height".to_owned()),
            RealValue(1.60),
            StringValue("Data".to_owned()),
            DataValue(vec![0, 0, 0, 190, 0, 0, 0, 3, 0, 0, 0, 30, 0, 0, 0]),
            StringValue("Birthdate".to_owned()),
            DateValue(parse_rfc3339_weak("1981-05-16 11:32:06").unwrap().into()),
            EndDictionary,
        ];

        let mut cursor = Cursor::new(Vec::new());

        {
            let mut plist_w = EventWriter::new(&mut cursor);

            for item in plist {
                plist_w.write(item).unwrap();
            }
        }

        let comparison = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">
<plist version=\"1.0\">
<dict>
\t<key>Author</key>
\t<string>William Shakespeare</string>
\t<key>Lines</key>
\t<array>
\t\t<string>It is a tale told by an idiot,</string>
\t\t<string>Full of sound and fury, signifying nothing.</string>
\t</array>
\t<key>Death</key>
\t<integer>1564</integer>
\t<key>Height</key>
\t<real>1.6</real>
\t<key>Data</key>
\t<data>AAAAvgAAAAMAAAAeAAAA</data>
\t<key>Birthdate</key>
\t<date>1981-05-16T11:32:06Z</date>
</dict>
</plist>";

        let s = String::from_utf8(cursor.into_inner()).unwrap();

        assert_eq!(s, comparison);
    }
}
<|endoftext|>


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

#![allow(uppercase_variables)]

extern crate freetype;
extern crate fontconfig;

use fontconfig::fontconfig::{FcChar8, FcResultMatch, FcSetSystem};
use fontconfig::fontconfig::{
    FcConfigGetCurrent, FcConfigGetFonts,
    FcConfigSubstitute, FcDefaultSubstitute,
    FcFontMatch,
    FcNameParse, FcPatternGetString,
    FcPatternDestroy, FcFontSetDestroy,
    FcMatchPattern,
    FcPatternCreate, FcPatternAddString,
    FcFontSetList, FcObjectSetCreate, FcObjectSetDestroy,
    FcObjectSetAdd, FcPatternGetInteger
};

use libc;
use libc::c_int;
use std::ptr;
use std::string;

pub fn get_available_families(callback: |String|) {
    unsafe {
        let config = FcConfigGetCurrent();
        let fontSet = FcConfigGetFonts(config, FcSetSystem);
        for i in range(0, (*fontSet).nfont as int) {
            let font = (*fontSet).fonts.offset(i);
            let mut family: *mut FcChar8 = ptr::mut_null();
            let mut v: c_int = 0;
            let mut FC_FAMILY_C = "family".to_c_str();
            let FC_FAMILY = FC_FAMILY_C.as_mut_ptr();
            while FcPatternGetString(*font, FC_FAMILY, v, &mut family) == FcResultMatch {
                let family_name = string::raw::from_buf(family as *const i8 as *const u8);
                callback(family_name);
                v += 1;
            }
        }
    }
}

pub fn get_variations_for_family(family_name: &str, callback: |String|) {
    debug!("getting variations for {}", family_name);
    unsafe {
        let config = FcConfigGetCurrent();
        let mut font_set = FcConfigGetFonts(config, FcSetSystem);
        let font_set_array_ptr = &mut font_set;
        let pattern = FcPatternCreate();
        assert!(pattern.is_not_null());
        let mut FC_FAMILY_C = "family".to_c_str();
        let FC_FAMILY = FC_FAMILY_C.as_mut_ptr();
        let mut family_name_c = family_name.to_c_str();
        let family_name = family_name_c.as_mut_ptr();
        let ok = FcPatternAddString(pattern, FC_FAMILY, family_name as *mut FcChar8);
        assert!(ok != 0);

        let object_set = FcObjectSetCreate();
        assert!(object_set.is_not_null());

        let mut FC_FILE_C = "file".to_c_str();
        let FC_FILE = FC_FILE_C.as_mut_ptr();
        FcObjectSetAdd(object_set, FC_FILE);
        let mut FC_INDEX_C = "index".to_c_str();
        let FC_INDEX = FC_INDEX_C.as_mut_ptr();
        FcObjectSetAdd(object_set, FC_INDEX);

        let matches = FcFontSetList(config, font_set_array_ptr, 1, pattern, object_set);

        debug!("found {} variations", (*matches).nfont);

        for i in range(0, (*matches).nfont as int) {
            let font = (*matches).fonts.offset(i);
            let mut FC_FILE_C = "file".to_c_str();
            let FC_FILE = FC_FILE_C.as_mut_ptr();
            let mut file: *mut FcChar8 = ptr::mut_null();
            let file = if FcPatternGetString(*font, FC_FILE, 0, &mut file) == FcResultMatch {
                string::raw::from_buf(file as *const i8 as *const u8)
            } else {
                fail!();
            };
            let mut FC_INDEX_C = "index".to_c_str();
            let FC_INDEX = FC_INDEX_C.as_mut_ptr();
            let mut index: libc::c_int = 0;
            let index = if FcPatternGetInteger(*font, FC_INDEX, 0, &mut index) == FcResultMatch {
                index
            } else {
                fail!();
            };

            debug!("variation file: {}", file);
            debug!("variation index: {}", index);

            callback(file);
        }

        FcFontSetDestroy(matches);
        FcPatternDestroy(pattern);
        FcObjectSetDestroy(object_set);
    }
}

pub fn get_system_default_family(generic_name: &str) -> Option<String> {
    let mut generic_name_c = generic_name.to_c_str();
    let generic_name_ptr = generic_name_c.as_mut_ptr();

    unsafe {
        let pattern = FcNameParse(generic_name_ptr as *mut FcChar8);

        FcConfigSubstitute(ptr::mut_null(), pattern, FcMatchPattern);
        FcDefaultSubstitute(pattern);

        let mut result = 0;
        let family_match = FcFontMatch(ptr::mut_null(), pattern, &mut result);

        let family_name = if result == FcResultMatch {
            let mut FC_FAMILY_C = "family".to_c_str();
            let FC_FAMILY = FC_FAMILY_C.as_mut_ptr();
            let mut match_string: *mut FcChar8 = ptr::mut_null();
            FcPatternGetString(family_match, FC_FAMILY, 0, &mut match_string);
            let result = string::raw::from_buf(match_string as *const i8 as *const u8);
            FcPatternDestroy(family_match);
            Some(result)
        } else {
            None
        };

        FcPatternDestroy(pattern);
        family_name
    }
}

#[cfg(target_os="linux")]
pub fn get_last_resort_font_families() -> Vec<String> {
    vec!(
        "Fira Sans".to_string(),
        "DejaVu Sans".to_string(),
        "Arial".to_string()
    )
}

#[cfg(target_os="android")]
pub fn get_last_resort_font_families() -> Vec<String> {
    vec!("Roboto".to_string())
}
<commit_msg>Define constant parameters strings as statics.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#![allow(uppercase_variables)]

extern crate freetype;
extern crate fontconfig;

use fontconfig::fontconfig::{FcChar8, FcResultMatch, FcSetSystem};
use fontconfig::fontconfig::{
    FcConfigGetCurrent, FcConfigGetFonts,
    FcConfigSubstitute, FcDefaultSubstitute,
    FcFontMatch,
    FcNameParse, FcPatternGetString,
    FcPatternDestroy, FcFontSetDestroy,
    FcMatchPattern,
    FcPatternCreate, FcPatternAddString,
    FcFontSetList, FcObjectSetCreate, FcObjectSetDestroy,
    FcObjectSetAdd, FcPatternGetInteger
};

use libc;
use libc::c_int;
use std::ptr;
use std::string;

static FC_FAMILY: &'static [u8] = b"family\0";
static FC_FILE: &'static [u8] = b"file\0";
static FC_INDEX: &'static [u8] = b"index\0";

pub fn get_available_families(callback: |String|) {
    unsafe {
        let config = FcConfigGetCurrent();
        let fontSet = FcConfigGetFonts(config, FcSetSystem);
        for i in range(0, (*fontSet).nfont as int) {
            let font = (*fontSet).fonts.offset(i);
            let mut family: *mut FcChar8 = ptr::mut_null();
            let mut v: c_int = 0;
            while FcPatternGetString(*font, FC_FAMILY.as_ptr() as *mut i8, v, &mut family) == FcResultMatch {
                let family_name = string::raw::from_buf(family as *const i8 as *const u8);
                callback(family_name);
                v += 1;
            }
        }
    }
}

pub fn get_variations_for_family(family_name: &str, callback: |String|) {
    debug!("getting variations for {}", family_name);
    unsafe {
        let config = FcConfigGetCurrent();
        let mut font_set = FcConfigGetFonts(config, FcSetSystem);
        let font_set_array_ptr = &mut font_set;
        let pattern = FcPatternCreate();
        assert!(pattern.is_not_null());
        let mut family_name_c = family_name.to_c_str();
        let family_name = family_name_c.as_mut_ptr();
        let ok = FcPatternAddString(pattern, FC_FAMILY.as_ptr() as *mut i8, family_name as *mut FcChar8);
        assert!(ok != 0);

        let object_set = FcObjectSetCreate();
        assert!(object_set.is_not_null());

        FcObjectSetAdd(object_set, FC_FILE.as_ptr() as *mut i8);
        FcObjectSetAdd(object_set, FC_INDEX.as_ptr() as *mut i8);

        let matches = FcFontSetList(config, font_set_array_ptr, 1, pattern, object_set);

        debug!("found {} variations", (*matches).nfont);

        for i in range(0, (*matches).nfont as int) {
            let font = (*matches).fonts.offset(i);
            let mut file: *mut FcChar8 = ptr::mut_null();
            let file = if FcPatternGetString(*font, FC_FILE.as_ptr() as *mut i8, 0, &mut file) == FcResultMatch {
                string::raw::from_buf(file as *const i8 as *const u8)
            } else {
                fail!();
            };
            let mut index: libc::c_int = 0;
            let index = if FcPatternGetInteger(*font, FC_INDEX.as_ptr() as *mut i8, 0, &mut index) == FcResultMatch {
                index
            } else {
                fail!();
            };

            debug!("variation file: {}", file);
            debug!("variation index: {}", index);

            callback(file);
        }

        FcFontSetDestroy(matches);
        FcPatternDestroy(pattern);
        FcObjectSetDestroy(object_set);
    }
}

pub fn get_system_default_family(generic_name: &str) -> Option<String> {
    let mut generic_name_c = generic_name.to_c_str();
    let generic_name_ptr = generic_name_c.as_mut_ptr();

    unsafe {
        let pattern = FcNameParse(generic_name_ptr as *mut FcChar8);

        FcConfigSubstitute(ptr::mut_null(), pattern, FcMatchPattern);
        FcDefaultSubstitute(pattern);

        let mut result = 0;
        let family_match = FcFontMatch(ptr::mut_null(), pattern, &mut result);

        let family_name = if result == FcResultMatch {
            let mut match_string: *mut FcChar8 = ptr::mut_null();
            FcPatternGetString(family_match, FC_FAMILY.as_ptr() as *mut i8, 0, &mut match_string);
            let result = string::raw::from_buf(match_string as *const i8 as *const u8);
            FcPatternDestroy(family_match);
            Some(result)
        } else {
            None
        };

        FcPatternDestroy(pattern);
        family_name
    }
}

#[cfg(target_os="linux")]
pub fn get_last_resort_font_families() -> Vec<String> {
    vec!(
        "Fira Sans".to_string(),
        "DejaVu Sans".to_string(),
        "Arial".to_string()
    )
}

#[cfg(target_os="android")]
pub fn get_last_resort_font_families() -> Vec<String> {
    vec!("Roboto".to_string())
}
<|endoftext|>


<commit_before>extern crate argparse;
extern crate env_logger;
extern crate ipnetwork;
extern crate libc;
extern crate lithos;
extern crate quire;
extern crate scan_dir;
#[macro_use] extern crate log;

use std::collections::BTreeMap;
use std::env;
use std::fs::{metadata};
use std::net::IpAddr;
use std::path::{Path, PathBuf};
use std::process::exit;
use std::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT};

use argparse::{ArgumentParser, Parse, ParseOption, StoreTrue, Print, Collect};
use ipnetwork::IpNetwork;
use quire::{parse_config, Options};

use lithos::utils::{in_mapping, check_mapping, relative};
use lithos::range::in_range;
use lithos::master_config::MasterConfig;
use lithos::sandbox_config::SandboxConfig;
use lithos::container_config::{ContainerConfig, Variables};
use lithos::child_config::{ChildConfig, ChildKind};
use lithos::network::{get_host_name, get_host_ip};
use lithos::id_map::{IdMapExt};

static EXIT_STATUS: AtomicUsize = ATOMIC_USIZE_INIT;

macro_rules! err {
    ( $( $x:expr ),* ) => {
        {
            error!($($x),*);
            EXIT_STATUS.store(1,  Ordering::SeqCst);
        }
    }
}

fn check_master_config(master: &MasterConfig, verbose: bool) {
    // TODO(tailhook) maybe check host only if we need it for hosts file
    match get_host_name() {
        Ok(hostname) => {
            if verbose {
                println!("Hostname is {}", hostname);
            }
        }
        Err(e) => {
            err!("Can't get hostname: {}", e);
        }
    }
    match get_host_ip() {
        Ok(ipaddr) => {
            if verbose {
                println!("IPAddr is {}", ipaddr);
            }
        }
        Err(e) => {
            err!("Can't get IPAddress: {}", e);
        }
    }

    if metadata(&master.devfs_dir).is_err() {
        err!("Devfs dir ({:?}) must exist and contain device nodes",
            master.devfs_dir);
    }
}

fn check_sandbox_config(sandbox: &SandboxConfig) {
    if sandbox.allow_users.len() == 0 {
        err!("No allowed users range. Please add `allow-users: [1-1000]`");
    }
    if sandbox.allow_groups.len() == 0 {
        err!("No allowed groups range. Please add `allow-groups: [1-1000]`");
    }
    // TODO(tailhook) check allow_users/allow_groups against uid_map/gid_map
}

fn check_container(config_file: &Path,
    sandbox: Option<&SandboxConfig>,)
    -> Result<ContainerConfig, ()>
{
    // Only checks things that can be checked without other configs
    let config: ContainerConfig = match parse_config(config_file,
        &ContainerConfig::validator(), &Options::default())
    {
        Ok(cfg) => cfg,
        Err(e) => {
            err!("Can't read container config {:?}: {}", config_file, e);
            return Err(());
        }
    };
    if let Some(sandbox) = sandbox {
        if config.uid_map.len() > 0 {
            let user_id = config.user_id.or(sandbox.default_user);
            if let Some(user_id) = user_id {
                if !in_mapping(&config.uid_map, user_id) {
                    err!("User is not in mapped range (uid: {})",
                        user_id);
                }
            } else {
                err!("Neither user id is specified nor default is found");
            }
        }
        if config.gid_map.len() > 0 {
            let group_id = config.group_id.or(sandbox.default_group);
            if let Some(group_id) = group_id {
                if !in_mapping(&config.gid_map, group_id) {
                    err!("Group is not in mapped range (gid: {})",
                        group_id);
                }
            } else {
                err!("Neither group id is specified nor default is found");
            }
        }
    } else {
        // We don't know anything about sandbox.
        // Skip checking container uid_maps, note: they are there for a very
        // rare and specialized use case, so isn't big issue
    }
    Ok(config)
}

fn network_contains(netw: &IpNetwork, ip: IpAddr) -> bool {
    match (*netw, ip) {
        (IpNetwork::V4(net), IpAddr::V4(ip)) => net.contains(ip),
        (IpNetwork::V6(net), IpAddr::V6(ip)) => net.contains(ip),
        _ => false,
    }
}

fn check(config_file: &Path, verbose: bool,
    altered_sandbox: Option<String>, alter_config: Option<PathBuf>)
{
    let mut alter_config = alter_config;
    let master: MasterConfig = match parse_config(&config_file,
        &MasterConfig::validator(), &Options::default()) {
        Ok(cfg) => cfg,
        Err(e) => {
            err!("Can't parse config: {}", e);
            return;
        }
    };

    check_master_config(&master, verbose);

    let config_dir = config_file.parent().unwrap().join(&master.sandboxes_dir);
    scan_dir::ScanDir::files().read(&config_dir, |iter| {
        let yamls = iter.filter(|&(_, ref name)| name.ends_with(".yaml"));
        for (entry, current_fn) in yamls {
            // strip yaml suffix
            let current_name = &current_fn[..current_fn.len()-5];
            let sandbox: SandboxConfig = match parse_config(&entry.path(),
                &SandboxConfig::validator(), &Options::default()) {
                Ok(cfg) => cfg,
                Err(e) => {
                    err!("Can't parse config: {}", e);
                    continue;
                }
            };
            check_sandbox_config(&sandbox);

            let default_config = config_file.parent().unwrap()
                .join(&master.processes_dir)
                .join(sandbox.config_file.as_ref().unwrap_or(
                    &PathBuf::from(&current_fn)));
            let config_file = match (current_name, &altered_sandbox)
            {
                (name, &Some(ref t)) if name == t
                => alter_config.take().unwrap_or(default_config),
                _ => default_config,
            };

            debug!("Checking {:?}", config_file);
            let all_children: BTreeMap<String, ChildConfig>;
            all_children = match parse_config(&config_file,
                &ChildConfig::mapping_validator(), &Options::default()) {
                Ok(cfg) => cfg,
                Err(e) => {
                    warn!("Can't read child config {:?}: {}", config_file, e);
                    continue;
                }
            };
            for (ref child_name, ref child_cfg) in all_children.iter() {
                let cfg_path = Path::new(&child_cfg.config);
                if !cfg_path.is_absolute() {
                    err!("Config path must be absolute");
                    continue;
                }
                if !sandbox.check_path(&child_cfg.image) {
                    err!("Image {} in sandbox {}, proccess {} is invalid",
                        child_cfg.image, current_name, child_name);
                    continue;
                }
                debug!("Opening config for {:?}", child_name);
                let config = match check_container(&sandbox.image_dir
                    .join(&child_cfg.image)
                    .join(&relative(cfg_path, &Path::new("/"))),
                    Some(&sandbox))
                {
                    Ok(config) => config,
                    Err(()) => continue,
                };
                // Uidmaps aren't substituted
                if config.uid_map.len() > 0 {
                    if sandbox.uid_map.len() > 0 {
                        err!("Can't have uid_maps in both the sandbox and a \
                              container itself");
                    }
                } else {
                    let user_id = config.user_id
                        .or(sandbox.default_user);
                    if let Some(user_id) = user_id {
                        if sandbox.uid_map.len() > 0 {
                            if sandbox.uid_map.map_id(user_id).is_none() {
                                err!("User is not in mapped range \
                                    (uid: {})",
                                    user_id);
                            }
                        }
                        if !in_range(&sandbox.allow_users, user_id) {
                            err!("User is not in allowed range (uid: {})",
                                user_id);
                        }
                    } else {
                        err!("Neither user id is specified \
                            nor default is found");
                    }
                }
                if config.gid_map.len() > 0 {
                    if sandbox.gid_map.len() > 0 {
                        err!("Can't have uid_maps in both the sandbox and a \
                              container itself");
                    }
                } else {
                    let group_id = config.group_id
                        .or(sandbox.default_group);
                    if let Some(group_id) = group_id {
                        if sandbox.gid_map.len() > 0 {
                            if sandbox.gid_map.map_id(group_id).is_none() {
                                err!("Group is not in mapped range \
                                    (gid: {})",
                                    group_id);
                            }
                        }
                        if !in_range(&sandbox.allow_groups, group_id) {
                            err!("Group is not in allowed range (gid: {})",
                                group_id);
                        }
                    } else {
                        err!("Neither group id is specified \
                            nor default is found");
                    }
                }
                if !check_mapping(&sandbox.allow_users, &config.uid_map) {
                    err!("Bad uid mapping (probably doesn't match allow_users)");
                }
                if !check_mapping(&sandbox.allow_groups, &config.gid_map) {
                    err!("Bad gid mapping (probably doesn't match allow_groups)");
                }
                // Per-instance validation
                for (key, typ) in &config.variables {
                    if let Some(value) = child_cfg.variables.get(key) {
                        if let Err(e) = typ.validate(value, &sandbox) {
                            err!("Variable {:?} is invalid: {}", key, e);
                        }
                    } else {
                        err!("Variable {:?} is undefined", key);
                    }
                }
                for i in 0..child_cfg.instances {
                    let name = format!("{}/{}.{}",
                        current_name, child_name, i);
                    let ichild = match child_cfg.instantiate(i) {
                        Ok(x) => x,
                        Err(e) => {
                            err!("{}: Can't instantiate child: {}",
                                name, e);
                            continue;
                        }
                    };

                    if let Some(ref bridge) = sandbox.bridged_network {
                        if let Some(ip) = ichild.ip_address {
                            if !network_contains(&bridge.network, ip) {
                                err!("{}: invalid ip {}", name, ip);
                            }
                        } else if ichild.kind == ChildKind::Command {
                            // okay to have no IP for commands
                        } else {
                            err!("{}: no IP address specified", name);
                        }
                    }

                    let icfg = match config.instantiate(&Variables {
                            user_vars: &ichild.variables,
                            lithos_name: &name,
                            lithos_config_filename: &ichild.config,
                        }) {
                        Ok(x) => x,
                        Err(e) => {
                            err!("Variable substitution error {:?} \
                                of sandbox {:?} of image {:?}: {}",
                                &ichild.config, current_name,
                                ichild.image,
                                e.join("; "));
                            continue;
                        }
                    };
                    // TODO(tailhook) check tcp ports
                    for (port, _) in icfg.tcp_ports {
                        if !in_range(&sandbox.allow_tcp_ports, port as u32) {
                            err!("Port {} is not allowed for {:?} \
                                of sandbox {:?} of image {:?}",
                                port, &ichild.config, current_name,
                                ichild.image);
                        }
                    }
                }
            }
        }
    }).map_err(|e| {
        err!("Can't read config directory {:?}: {}", config_dir, e);
    }).ok();
    if alter_config.is_some() {
        err!("Tree {:?} is not used", altered_sandbox);
    }
}

fn check_binaries() {
    let dir = match env::current_exe().ok()
        .and_then(|x| x.parent().map(|x| x.to_path_buf()))
    {
        Some(dir) => dir,
        None => {
            err!("Can't find out exe path");
            return;
        }
    };
    if metadata(&dir.join("lithos_tree")).is_err() {
        err!("Can't find lithos_tree binary");
    }
    if metadata(&dir.join("lithos_knot")).is_err() {
        err!("Can't find lithos_knot binary");
    }
}

fn main() {

    if env::var("RUST_LOG").is_err() {
        env::set_var("RUST_LOG", "warn");
    }
    env_logger::init();

    let mut config_file = PathBuf::from("/etc/lithos/master.yaml");
    let mut verbose = false;
    let mut alter_config = None;
    let mut sandbox_name = None;
    let mut check_containers = Vec::<String>::new();
    {
        let mut ap = ArgumentParser::new();
        ap.set_description("Checks if lithos configuration is ok");
        ap.refer(&mut config_file)
          .add_option(&["-C", "--config"], Parse,
            "Name of the global configuration file \
             (default /etc/lithos/master.yaml)")
          .metavar("FILE");
        ap.refer(&mut verbose)
          .add_option(&["-v", "--verbose"], StoreTrue,
            "Verbose output");
        ap.refer(&mut alter_config)
          .add_option(&["--alternate-config"], ParseOption,
            "Name of the alterate file name with configs.
             Useful to test configuration file before
             switching it to be primary one.
             You must also specify --sandbox.")
          .metavar("FILE");
        ap.refer(&mut sandbox_name)
          .add_option(&["--sandbox", "--sandbox-name",
            // Compatibility names
            "-T", "--tree", "--subtree-name",
            ], ParseOption,
            "Name of the sandbox for which --config-dir takes effect")
          .metavar("NAME");
        ap.refer(&mut check_containers)
          .add_option(&["--check-container"], Collect, "
            Instead of checking full lithos configuration check the
            container's configuration in the FILE. This is useful to check
            container locally, where you don't have lithos configured,
            before actually uploading the container. Multiple files may be
            specified in multiple arguments.
            ")
          .metavar("FILE");
        ap.add_option(&["--version"],
            Print(env!("CARGO_PKG_VERSION").to_string()),
            "Show version");
        match ap.parse_args() {
            Ok(()) => {}
            Err(x) => {
                exit(x);
            }
        }
    }
    if alter_config.is_some() && sandbox_name.is_none() {
        err!("Please specify --sandbox if you use --dir");
    }
    if check_containers.len() > 0 {
        for file in &check_containers {
            check_container(Path::new(file), None).ok();
        }
    } else {
        check_binaries();
        check(&config_file, verbose, sandbox_name, alter_config);
    }
    let exit_status = EXIT_STATUS.load(Ordering::SeqCst) as i32;
    if exit_status != 0 {
        warn!("Lithos version v{}", env!("CARGO_PKG_VERSION"));
    }
    exit(exit_status);
}
<commit_msg>A bit better message for error in alternate child config in lithos_check<commit_after>extern crate argparse;
extern crate env_logger;
extern crate ipnetwork;
extern crate libc;
extern crate lithos;
extern crate quire;
extern crate scan_dir;
#[macro_use] extern crate log;

use std::collections::BTreeMap;
use std::env;
use std::fs::{metadata};
use std::net::IpAddr;
use std::path::{Path, PathBuf};
use std::process::exit;
use std::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT};

use argparse::{ArgumentParser, Parse, ParseOption, StoreTrue, Print, Collect};
use ipnetwork::IpNetwork;
use quire::{parse_config, Options};

use lithos::utils::{in_mapping, check_mapping, relative};
use lithos::range::in_range;
use lithos::master_config::MasterConfig;
use lithos::sandbox_config::SandboxConfig;
use lithos::container_config::{ContainerConfig, Variables};
use lithos::child_config::{ChildConfig, ChildKind};
use lithos::network::{get_host_name, get_host_ip};
use lithos::id_map::{IdMapExt};

static EXIT_STATUS: AtomicUsize = ATOMIC_USIZE_INIT;

macro_rules! err {
    ( $( $x:expr ),* ) => {
        {
            error!($($x),*);
            EXIT_STATUS.store(1,  Ordering::SeqCst);
        }
    }
}

fn check_master_config(master: &MasterConfig, verbose: bool) {
    // TODO(tailhook) maybe check host only if we need it for hosts file
    match get_host_name() {
        Ok(hostname) => {
            if verbose {
                println!("Hostname is {}", hostname);
            }
        }
        Err(e) => {
            err!("Can't get hostname: {}", e);
        }
    }
    match get_host_ip() {
        Ok(ipaddr) => {
            if verbose {
                println!("IPAddr is {}", ipaddr);
            }
        }
        Err(e) => {
            err!("Can't get IPAddress: {}", e);
        }
    }

    if metadata(&master.devfs_dir).is_err() {
        err!("Devfs dir ({:?}) must exist and contain device nodes",
            master.devfs_dir);
    }
}

fn check_sandbox_config(sandbox: &SandboxConfig) {
    if sandbox.allow_users.len() == 0 {
        err!("No allowed users range. Please add `allow-users: [1-1000]`");
    }
    if sandbox.allow_groups.len() == 0 {
        err!("No allowed groups range. Please add `allow-groups: [1-1000]`");
    }
    // TODO(tailhook) check allow_users/allow_groups against uid_map/gid_map
}

fn check_container(config_file: &Path,
    sandbox: Option<&SandboxConfig>,)
    -> Result<ContainerConfig, ()>
{
    // Only checks things that can be checked without other configs
    let config: ContainerConfig = match parse_config(config_file,
        &ContainerConfig::validator(), &Options::default())
    {
        Ok(cfg) => cfg,
        Err(e) => {
            err!("Can't read container config {:?}: {}", config_file, e);
            return Err(());
        }
    };
    if let Some(sandbox) = sandbox {
        if config.uid_map.len() > 0 {
            let user_id = config.user_id.or(sandbox.default_user);
            if let Some(user_id) = user_id {
                if !in_mapping(&config.uid_map, user_id) {
                    err!("User is not in mapped range (uid: {})",
                        user_id);
                }
            } else {
                err!("Neither user id is specified nor default is found");
            }
        }
        if config.gid_map.len() > 0 {
            let group_id = config.group_id.or(sandbox.default_group);
            if let Some(group_id) = group_id {
                if !in_mapping(&config.gid_map, group_id) {
                    err!("Group is not in mapped range (gid: {})",
                        group_id);
                }
            } else {
                err!("Neither group id is specified nor default is found");
            }
        }
    } else {
        // We don't know anything about sandbox.
        // Skip checking container uid_maps, note: they are there for a very
        // rare and specialized use case, so isn't big issue
    }
    Ok(config)
}

fn network_contains(netw: &IpNetwork, ip: IpAddr) -> bool {
    match (*netw, ip) {
        (IpNetwork::V4(net), IpAddr::V4(ip)) => net.contains(ip),
        (IpNetwork::V6(net), IpAddr::V6(ip)) => net.contains(ip),
        _ => false,
    }
}

fn check(config_file: &Path, verbose: bool,
    altered_sandbox: Option<String>, alter_config: Option<PathBuf>)
{
    let mut alter_config = alter_config;
    let master: MasterConfig = match parse_config(&config_file,
        &MasterConfig::validator(), &Options::default()) {
        Ok(cfg) => cfg,
        Err(e) => {
            err!("Can't parse config: {}", e);
            return;
        }
    };

    check_master_config(&master, verbose);

    let config_dir = config_file.parent().unwrap().join(&master.sandboxes_dir);
    scan_dir::ScanDir::files().read(&config_dir, |iter| {
        let yamls = iter.filter(|&(_, ref name)| name.ends_with(".yaml"));
        for (entry, current_fn) in yamls {
            // strip yaml suffix
            let current_name = &current_fn[..current_fn.len()-5];
            let sandbox: SandboxConfig = match parse_config(&entry.path(),
                &SandboxConfig::validator(), &Options::default()) {
                Ok(cfg) => cfg,
                Err(e) => {
                    err!("Can't parse config: {}", e);
                    continue;
                }
            };
            check_sandbox_config(&sandbox);

            let default_config = config_file.parent().unwrap()
                .join(&master.processes_dir)
                .join(sandbox.config_file.as_ref().unwrap_or(
                    &PathBuf::from(&current_fn)));
            let config_file = match (current_name, &altered_sandbox)
            {
                (name, &Some(ref t)) if name == t
                => alter_config.take().unwrap_or(default_config),
                _ => default_config,
            };

            debug!("Checking {:?}", config_file);
            let all_children: BTreeMap<String, ChildConfig>;
            all_children = match parse_config(&config_file,
                &ChildConfig::mapping_validator(), &Options::default()) {
                Ok(cfg) => cfg,
                Err(e) => {
                    warn!("Can't read child config for {:?}: {}",
                        current_name, e);
                    continue;
                }
            };
            for (ref child_name, ref child_cfg) in all_children.iter() {
                let cfg_path = Path::new(&child_cfg.config);
                if !cfg_path.is_absolute() {
                    err!("Config path must be absolute");
                    continue;
                }
                if !sandbox.check_path(&child_cfg.image) {
                    err!("Image {} in sandbox {}, proccess {} is invalid",
                        child_cfg.image, current_name, child_name);
                    continue;
                }
                debug!("Opening config for {:?}", child_name);
                let config = match check_container(&sandbox.image_dir
                    .join(&child_cfg.image)
                    .join(&relative(cfg_path, &Path::new("/"))),
                    Some(&sandbox))
                {
                    Ok(config) => config,
                    Err(()) => continue,
                };
                // Uidmaps aren't substituted
                if config.uid_map.len() > 0 {
                    if sandbox.uid_map.len() > 0 {
                        err!("Can't have uid_maps in both the sandbox and a \
                              container itself");
                    }
                } else {
                    let user_id = config.user_id
                        .or(sandbox.default_user);
                    if let Some(user_id) = user_id {
                        if sandbox.uid_map.len() > 0 {
                            if sandbox.uid_map.map_id(user_id).is_none() {
                                err!("User is not in mapped range \
                                    (uid: {})",
                                    user_id);
                            }
                        }
                        if !in_range(&sandbox.allow_users, user_id) {
                            err!("User is not in allowed range (uid: {})",
                                user_id);
                        }
                    } else {
                        err!("Neither user id is specified \
                            nor default is found");
                    }
                }
                if config.gid_map.len() > 0 {
                    if sandbox.gid_map.len() > 0 {
                        err!("Can't have uid_maps in both the sandbox and a \
                              container itself");
                    }
                } else {
                    let group_id = config.group_id
                        .or(sandbox.default_group);
                    if let Some(group_id) = group_id {
                        if sandbox.gid_map.len() > 0 {
                            if sandbox.gid_map.map_id(group_id).is_none() {
                                err!("Group is not in mapped range \
                                    (gid: {})",
                                    group_id);
                            }
                        }
                        if !in_range(&sandbox.allow_groups, group_id) {
                            err!("Group is not in allowed range (gid: {})",
                                group_id);
                        }
                    } else {
                        err!("Neither group id is specified \
                            nor default is found");
                    }
                }
                if !check_mapping(&sandbox.allow_users, &config.uid_map) {
                    err!("Bad uid mapping (probably doesn't match allow_users)");
                }
                if !check_mapping(&sandbox.allow_groups, &config.gid_map) {
                    err!("Bad gid mapping (probably doesn't match allow_groups)");
                }
                // Per-instance validation
                for (key, typ) in &config.variables {
                    if let Some(value) = child_cfg.variables.get(key) {
                        if let Err(e) = typ.validate(value, &sandbox) {
                            err!("Variable {:?} is invalid: {}", key, e);
                        }
                    } else {
                        err!("Variable {:?} is undefined", key);
                    }
                }
                for i in 0..child_cfg.instances {
                    let name = format!("{}/{}.{}",
                        current_name, child_name, i);
                    let ichild = match child_cfg.instantiate(i) {
                        Ok(x) => x,
                        Err(e) => {
                            err!("{}: Can't instantiate child: {}",
                                name, e);
                            continue;
                        }
                    };

                    if let Some(ref bridge) = sandbox.bridged_network {
                        if let Some(ip) = ichild.ip_address {
                            if !network_contains(&bridge.network, ip) {
                                err!("{}: invalid ip {}", name, ip);
                            }
                        } else if ichild.kind == ChildKind::Command {
                            // okay to have no IP for commands
                        } else {
                            err!("{}: no IP address specified", name);
                        }
                    }

                    let icfg = match config.instantiate(&Variables {
                            user_vars: &ichild.variables,
                            lithos_name: &name,
                            lithos_config_filename: &ichild.config,
                        }) {
                        Ok(x) => x,
                        Err(e) => {
                            err!("Variable substitution error {:?} \
                                of sandbox {:?} of image {:?}: {}",
                                &ichild.config, current_name,
                                ichild.image,
                                e.join("; "));
                            continue;
                        }
                    };
                    // TODO(tailhook) check tcp ports
                    for (port, _) in icfg.tcp_ports {
                        if !in_range(&sandbox.allow_tcp_ports, port as u32) {
                            err!("Port {} is not allowed for {:?} \
                                of sandbox {:?} of image {:?}",
                                port, &ichild.config, current_name,
                                ichild.image);
                        }
                    }
                }
            }
        }
    }).map_err(|e| {
        err!("Can't read config directory {:?}: {}", config_dir, e);
    }).ok();
    if alter_config.is_some() {
        err!("Tree {:?} is not used", altered_sandbox);
    }
}

fn check_binaries() {
    let dir = match env::current_exe().ok()
        .and_then(|x| x.parent().map(|x| x.to_path_buf()))
    {
        Some(dir) => dir,
        None => {
            err!("Can't find out exe path");
            return;
        }
    };
    if metadata(&dir.join("lithos_tree")).is_err() {
        err!("Can't find lithos_tree binary");
    }
    if metadata(&dir.join("lithos_knot")).is_err() {
        err!("Can't find lithos_knot binary");
    }
}

fn main() {

    if env::var("RUST_LOG").is_err() {
        env::set_var("RUST_LOG", "warn");
    }
    env_logger::init();

    let mut config_file = PathBuf::from("/etc/lithos/master.yaml");
    let mut verbose = false;
    let mut alter_config = None;
    let mut sandbox_name = None;
    let mut check_containers = Vec::<String>::new();
    {
        let mut ap = ArgumentParser::new();
        ap.set_description("Checks if lithos configuration is ok");
        ap.refer(&mut config_file)
          .add_option(&["-C", "--config"], Parse,
            "Name of the global configuration file \
             (default /etc/lithos/master.yaml)")
          .metavar("FILE");
        ap.refer(&mut verbose)
          .add_option(&["-v", "--verbose"], StoreTrue,
            "Verbose output");
        ap.refer(&mut alter_config)
          .add_option(&["--alternate-config"], ParseOption,
            "Name of the alterate file name with configs.
             Useful to test configuration file before
             switching it to be primary one.
             You must also specify --sandbox.")
          .metavar("FILE");
        ap.refer(&mut sandbox_name)
          .add_option(&["--sandbox", "--sandbox-name",
            // Compatibility names
            "-T", "--tree", "--subtree-name",
            ], ParseOption,
            "Name of the sandbox for which --config-dir takes effect")
          .metavar("NAME");
        ap.refer(&mut check_containers)
          .add_option(&["--check-container"], Collect, "
            Instead of checking full lithos configuration check the
            container's configuration in the FILE. This is useful to check
            container locally, where you don't have lithos configured,
            before actually uploading the container. Multiple files may be
            specified in multiple arguments.
            ")
          .metavar("FILE");
        ap.add_option(&["--version"],
            Print(env!("CARGO_PKG_VERSION").to_string()),
            "Show version");
        match ap.parse_args() {
            Ok(()) => {}
            Err(x) => {
                exit(x);
            }
        }
    }
    if alter_config.is_some() && sandbox_name.is_none() {
        err!("Please specify --sandbox if you use --dir");
    }
    if check_containers.len() > 0 {
        for file in &check_containers {
            check_container(Path::new(file), None).ok();
        }
    } else {
        check_binaries();
        check(&config_file, verbose, sandbox_name, alter_config);
    }
    let exit_status = EXIT_STATUS.load(Ordering::SeqCst) as i32;
    if exit_status != 0 {
        warn!("Lithos version v{}", env!("CARGO_PKG_VERSION"));
    }
    exit(exit_status);
}
<|endoftext|>


<commit_before><commit_msg>codegen: derive PartialEq in specs<commit_after><|endoftext|>