<commit_before>extern crate chrono;
extern crate xcb;
extern crate xcb_util;
extern crate cairo;
extern crate cairo_sys;
extern crate pango;
extern crate pangocairo;

use std::rc::Rc;

use cairo::{Context, Surface, XCBConnection, XCBDrawable, XCBSurface, XCBVisualType};
use pango::LayoutExt;
use pangocairo::CairoContextExt;
use xcb::ffi::*;
use chrono::prelude::*;

fn get_root_visual_type(conn: &xcb::Connection, screen: &xcb::Screen) -> xcb::Visualtype {
    for root in conn.get_setup().roots() {
        for allowed_depth in root.allowed_depths() {
            for visual in allowed_depth.visuals() {
                if visual.visual_id() == screen.root_visual() {
                    return visual;
                }
            }
        }
    }
    panic!("No visual type found");
}

#[derive(Clone, Debug)]
struct Color {
    red: f64,
    green: f64,
    blue: f64,
}

impl Color {
    fn white() -> Color {
        Color {
            red: 1.0,
            green: 1.0,
            blue: 1.0,
        }
    }

    fn red() -> Color {
        Color {
            red: 1.0,
            green: 0.0,
            blue: 0.0,
        }
    }

    fn blue() -> Color {
        Color {
            red: 0.0,
            green: 0.0,
            blue: 1.0,
        }
    }

    fn apply_to_context(&self, cr: &Context) {
        cr.set_source_rgb(self.red, self.green, self.blue);
    }
}

#[derive(Clone, Debug)]
struct Padding {
    left: f64,
    right: f64,
    top: f64,
    bottom: f64,
}

impl Padding {
    fn none() -> Padding {
        Padding {
            left: 0.0,
            right: 0.0,
            top: 0.0,
            bottom: 0.0,
        }
    }
    fn new(left: f64, right: f64, top: f64, bottom: f64) -> Padding {
        Padding {
            left,
            right,
            top,
            bottom,
        }
    }

    fn uniform(value: f64) -> Padding {
        Padding {
            left: value,
            right: value,
            top: value,
            bottom: value,
        }
    }
}

#[derive(Clone)]
struct TextAttributes {
    font: pango::FontDescription,
    fg_color: Color,
    bg_color: Option<Color>,
    padding: Padding,
}

impl TextAttributes {
    fn with_font(&self, font: pango::FontDescription) -> TextAttributes {
        let mut new = self.clone();
        new.font = font;
        new
    }

    fn with_fg_color(&self, fg_color: Color) -> TextAttributes {
        let mut new = self.clone();
        new.fg_color = fg_color;
        new
    }

    fn with_bg_color(&self, bg_color: Option<Color>) -> TextAttributes {
        let mut new = self.clone();
        new.bg_color = bg_color;
        new
    }

    fn with_padding(&self, padding: Padding) -> TextAttributes {
        let mut new = self.clone();
        new.padding = padding;
        new
    }
}

struct Text {
    attr: TextAttributes,
    text: String,
    stretch: bool,
}

impl Text {
    fn layout(self, surface: &Surface) -> TextLayout {
        let context = Context::new(&surface);

        let layout = context.create_pango_layout();
        layout.set_text(&self.text, self.text.len() as i32);
        layout.set_font_description(Some(&self.attr.font));

        TextLayout {
            attr: self.attr.clone(),
            stretch: self.stretch,
            width: None,
            height: None,
            context: context,
            layout: layout,
        }
    }
}

struct TextLayout {
    attr: TextAttributes,
    stretch: bool,
    width: Option<f64>,
    height: Option<f64>,
    context: Context,
    layout: pango::Layout,
}

impl TextLayout {
    fn width(&self) -> f64 {
        self.width
            .unwrap_or_else(|| {
                                let text_width = self.layout.get_pixel_size().0 as f64;
                                text_width + self.attr.padding.left + self.attr.padding.right
                            })
    }

    fn height(&self) -> f64 {
        self.height
            .unwrap_or_else(|| {
                                let text_height = self.layout.get_pixel_size().1 as f64;
                                text_height + self.attr.padding.top + self.attr.padding.bottom
                            })
    }

    fn set_width(&mut self, width: f64) {
        self.width = Some(width);
    }

    fn set_height(&mut self, height: f64) {
        self.height = Some(height);
    }

    fn stretch(&self) -> bool {
        self.stretch
    }

    fn render(&self, x: f64, y: f64) {
        self.context.save();
        self.context.translate(x, y + self.attr.padding.top);

        if let Some(ref bg_color) = self.attr.bg_color {
            bg_color.apply_to_context(&self.context);
            // FIXME: The use of `height` isnt' right here: we want to do the
            // full height of the bar, not the full height of the text. It
            // would be useful if we could do Surface.get_height(), but that
            // doesn't seem to be available in cairo-rs for some reason?
            self.context
                .rectangle(0.0, 0.0, self.width(), self.height());
            self.context.fill();
        }

        self.attr.fg_color.apply_to_context(&self.context);
        self.context
            .translate(self.attr.padding.left, self.attr.padding.top);
        self.context.show_pango_layout(&self.layout);

        self.context.restore();
    }
}

struct Pager {
    conn: xcb_util::ewmh::Connection,
    screen_idx: i32,

    active_attr: TextAttributes,
    inactive_attr: TextAttributes,
}

impl Pager {
    fn new(active_attr: TextAttributes, inactive_attr: TextAttributes) -> Pager {
        let (conn, screen_idx) = xcb::Connection::connect_with_xlib_display().unwrap();
        let ewmh_conn = xcb_util::ewmh::Connection::connect(conn)
            .map_err(|_| ())
            .unwrap();

        Pager {
            conn: ewmh_conn,
            screen_idx: screen_idx,
            active_attr: active_attr,
            inactive_attr: inactive_attr,
        }
    }

    fn get_desktops_info(&self) -> Vec<(bool, String)> {
        let number = xcb_util::ewmh::get_number_of_desktops(&self.conn, self.screen_idx)
            .get_reply()
            .unwrap() as usize;
        let current = xcb_util::ewmh::get_current_desktop(&self.conn, self.screen_idx)
            .get_reply()
            .unwrap() as usize;
        let names_reply = xcb_util::ewmh::get_desktop_names(&self.conn, self.screen_idx)
            .get_reply()
            .unwrap();
        let mut names = names_reply.strings();

        // EWMH states that `number` may not equal `names.len()`, as there may
        // be unnamed desktops, or more desktops than are currently in use.
        if names.len() > number {
            names.truncate(number);
        } else if number > names.len() {
            for i in 0..(number - names.len()) {
                names.push("?");
            }
        }

        names
            .into_iter()
            .enumerate()
            .map(|(i, name)| (i == current, name.to_owned()))
            .collect()
    }

    fn compute_text(&self) -> Vec<Text> {
        let desktops = self.get_desktops_info();
        self.get_desktops_info()
            .into_iter()
            .map(|(active, name)| {
                Text {
                    attr: if active {
                        self.active_attr.clone()
                    } else {
                        self.inactive_attr.clone()
                    },
                    text: name,
                    stretch: false,
                }
            })
            .collect()
    }
}

struct ActiveWindowTitle {
    conn: xcb_util::ewmh::Connection,
    screen_idx: i32,

    attr: TextAttributes,
}

impl ActiveWindowTitle {
    fn new(attr: TextAttributes) -> ActiveWindowTitle {
        let (conn, screen_idx) = xcb::Connection::connect_with_xlib_display().unwrap();
        let ewmh_conn = xcb_util::ewmh::Connection::connect(conn)
            .map_err(|_| ())
            .unwrap();

        ActiveWindowTitle {
            conn: ewmh_conn,
            screen_idx: screen_idx,

            attr: attr,
        }
    }

    fn get_active_window_title(&self) -> String {
        let active_window = xcb_util::ewmh::get_active_window(&self.conn, self.screen_idx)
            .get_reply()
            .unwrap();
        let reply = xcb_util::ewmh::get_wm_name(&self.conn, active_window).get_reply();

        match reply {
            Ok(inner) => inner.string().to_owned(),
            // Probably means there's no window focused, or it doesn't have _NET_WM_NAME:
            Err(_) => "".to_owned(),
        }
    }

    fn compute_text(&self) -> Vec<Text> {
        vec![Text {
                 attr: self.attr.clone(),
                 text: self.get_active_window_title(),
                 stretch: true,
             }]
    }
}

struct Clock {
    conn: xcb::Connection,

    attr: TextAttributes,
}

impl Clock {
    fn new(attr: TextAttributes) -> Clock {
        let (conn, screen_idx) = xcb::Connection::connect_with_xlib_display().unwrap();

        Clock {
            conn: conn,
            attr: attr,
        }
    }

    fn compute_text(&self) -> Vec<Text> {
        let current_time = Local::now().format("%Y-%m-%d %a %I:%M %p").to_string();
        vec![Text {
                 attr: self.attr.clone(),
                 text: current_time,
                 stretch: false,
             }]
    }
}

// TODO: impl Drop?
struct Window {
    conn: Rc<xcb::Connection>,
    screen_idx: usize,
    id: u32,
    surface: Surface,
}

impl Window {
    fn new(conn: Rc<xcb::Connection>, screen_idx: usize) -> Window {
        let id = conn.generate_id();

        let surface = {
            let screen = conn.get_setup()
                .roots()
                .nth(screen_idx)
                .expect("invalid screen");
            let values = [(xcb::CW_BACK_PIXEL, screen.black_pixel()),
                          (xcb::CW_EVENT_MASK, xcb::EVENT_MASK_EXPOSURE)];

            let (width, height) = (screen.width_in_pixels(), 100);

            xcb::create_window(&conn,
                               xcb::COPY_FROM_PARENT as u8,
                               id,
                               screen.root(),
                               0,
                               0,
                               width,
                               height,
                               10,
                               xcb::WINDOW_CLASS_INPUT_OUTPUT as u16,
                               screen.root_visual(),
                               &values);

            let surface = unsafe {
                let cairo_conn = XCBConnection::from_raw_none(conn.get_raw_conn() as
                                                              *mut cairo_sys::xcb_connection_t);
                let visual =
                    XCBVisualType::from_raw_none(&mut get_root_visual_type(&conn, &screen).base as
                                                 *mut xcb::ffi::xcb_visualtype_t as
                                                 *mut cairo_sys::xcb_visualtype_t);
                let drawable = XCBDrawable(id);
                Surface::create(&cairo_conn, &drawable, &visual, width as i32, height as i32)
                // TODO: Update surface width/height when window size changes.
            };

            surface
        };

        xcb::map_window(&conn, id);
        conn.flush();

        Window {
            conn,
            screen_idx,
            id,
            surface,
        }
    }

    fn screen<'a>(&'a self) -> xcb::Screen<'a> {
        self.conn
            .get_setup()
            .roots()
            .nth(self.screen_idx)
            .expect("Invalid screen")
    }

    fn expose(&self) {
        let inactive_attr = TextAttributes {
            font: pango::FontDescription::from_string("Envy Code R 27"),
            fg_color: Color::white(),
            bg_color: None,
            padding: Padding::new(10.0, 10.0, 5.0, 5.0),
        };
        let active_attr = inactive_attr.with_bg_color(Some(Color::blue()));

        let mut texts = Pager::new(active_attr, inactive_attr.clone()).compute_text();
        texts.extend(ActiveWindowTitle::new(inactive_attr.clone()).compute_text());
        texts.extend(Clock::new(inactive_attr.clone()).compute_text());

        self.render_text_blocks(texts);

        self.conn.flush();
    }

    fn render_text_blocks(&self, texts: Vec<Text>) {
        // Layout each block of text. After this, we can query the width of each
        // block, which will allow us to do more complex layout below.
        let mut layouts: Vec<_> = texts.into_iter().map(|t| t.layout(&self.surface)).collect();

        // Calculate how much free space we have after laying out all the
        // non-stretch blocks. Split the remaining space (if any) between the
        // stretch blocks. If there isn't enough space for the non-stretch blocks
        // do nothing and allow it to overflow.
        {
            let mut width = 0.0;
            let mut stretched = Vec::new();
            for layout in layouts.iter_mut() {
                if !layout.stretch() {
                    width += layout.width();
                } else {
                    stretched.push(layout);
                }
            }

            let remaining_width = self.screen().width_in_pixels() as f64 - width;
            let remaining_width = if remaining_width < 0.0 {
                0.0
            } else {
                remaining_width
            };
            let width_per_stretched = remaining_width / (stretched.len() as f64);
            for layout in stretched.iter_mut() {
                layout.set_width(width_per_stretched);
            }
        }

        // TODO: Set the height of the window and the height of each text block(?)
        // to the height of the largest bit of text.

        // Finally, just render each block of text in turn.
        let mut x = 0.0;
        for layout in &layouts {
            layout.render(x, 0.0);
            x += layout.width();
        }
    }
}

fn main() {
    let (conn, screen_idx) = xcb::Connection::connect_with_xlib_display().unwrap();
    let conn = Rc::new(conn);

    let w = Window::new(conn.clone(), screen_idx as usize);

    loop {
        let event = conn.wait_for_event();
        match event {
            None => {
                break;
            }
            Some(event) => {
                let r = event.response_type() & !0x80;
                match r {
                    xcb::EXPOSE => w.expose(),
                    _ => {}
                }
            }
        }
    }
}
<commit_msg>TextAttributes -> Attributes<commit_after>extern crate chrono;
extern crate xcb;
extern crate xcb_util;
extern crate cairo;
extern crate cairo_sys;
extern crate pango;
extern crate pangocairo;

use std::rc::Rc;

use cairo::{Context, Surface, XCBConnection, XCBDrawable, XCBSurface, XCBVisualType};
use pango::LayoutExt;
use pangocairo::CairoContextExt;
use xcb::ffi::*;
use chrono::prelude::*;

fn get_root_visual_type(conn: &xcb::Connection, screen: &xcb::Screen) -> xcb::Visualtype {
    for root in conn.get_setup().roots() {
        for allowed_depth in root.allowed_depths() {
            for visual in allowed_depth.visuals() {
                if visual.visual_id() == screen.root_visual() {
                    return visual;
                }
            }
        }
    }
    panic!("No visual type found");
}

#[derive(Clone, Debug)]
struct Color {
    red: f64,
    green: f64,
    blue: f64,
}

impl Color {
    fn white() -> Color {
        Color {
            red: 1.0,
            green: 1.0,
            blue: 1.0,
        }
    }

    fn red() -> Color {
        Color {
            red: 1.0,
            green: 0.0,
            blue: 0.0,
        }
    }

    fn blue() -> Color {
        Color {
            red: 0.0,
            green: 0.0,
            blue: 1.0,
        }
    }

    fn apply_to_context(&self, cr: &Context) {
        cr.set_source_rgb(self.red, self.green, self.blue);
    }
}

#[derive(Clone, Debug)]
struct Padding {
    left: f64,
    right: f64,
    top: f64,
    bottom: f64,
}

impl Padding {
    fn none() -> Padding {
        Padding {
            left: 0.0,
            right: 0.0,
            top: 0.0,
            bottom: 0.0,
        }
    }
    fn new(left: f64, right: f64, top: f64, bottom: f64) -> Padding {
        Padding {
            left,
            right,
            top,
            bottom,
        }
    }

    fn uniform(value: f64) -> Padding {
        Padding {
            left: value,
            right: value,
            top: value,
            bottom: value,
        }
    }
}

#[derive(Clone)]
struct Attributes {
    font: pango::FontDescription,
    fg_color: Color,
    bg_color: Option<Color>,
    padding: Padding,
}

impl Attributes {
    fn with_font(&self, font: pango::FontDescription) -> Attributes {
        let mut new = self.clone();
        new.font = font;
        new
    }

    fn with_fg_color(&self, fg_color: Color) -> Attributes {
        let mut new = self.clone();
        new.fg_color = fg_color;
        new
    }

    fn with_bg_color(&self, bg_color: Option<Color>) -> Attributes {
        let mut new = self.clone();
        new.bg_color = bg_color;
        new
    }

    fn with_padding(&self, padding: Padding) -> Attributes {
        let mut new = self.clone();
        new.padding = padding;
        new
    }
}

struct Text {
    attr: Attributes,
    text: String,
    stretch: bool,
}

impl Text {
    fn layout(self, surface: &Surface) -> TextLayout {
        let context = Context::new(&surface);

        let layout = context.create_pango_layout();
        layout.set_text(&self.text, self.text.len() as i32);
        layout.set_font_description(Some(&self.attr.font));

        TextLayout {
            attr: self.attr.clone(),
            stretch: self.stretch,
            width: None,
            height: None,
            context: context,
            layout: layout,
        }
    }
}

struct TextLayout {
    attr: Attributes,
    stretch: bool,
    width: Option<f64>,
    height: Option<f64>,
    context: Context,
    layout: pango::Layout,
}

impl TextLayout {
    fn width(&self) -> f64 {
        self.width
            .unwrap_or_else(|| {
                                let text_width = self.layout.get_pixel_size().0 as f64;
                                text_width + self.attr.padding.left + self.attr.padding.right
                            })
    }

    fn height(&self) -> f64 {
        self.height
            .unwrap_or_else(|| {
                                let text_height = self.layout.get_pixel_size().1 as f64;
                                text_height + self.attr.padding.top + self.attr.padding.bottom
                            })
    }

    fn set_width(&mut self, width: f64) {
        self.width = Some(width);
    }

    fn set_height(&mut self, height: f64) {
        self.height = Some(height);
    }

    fn stretch(&self) -> bool {
        self.stretch
    }

    fn render(&self, x: f64, y: f64) {
        self.context.save();
        self.context.translate(x, y + self.attr.padding.top);

        if let Some(ref bg_color) = self.attr.bg_color {
            bg_color.apply_to_context(&self.context);
            // FIXME: The use of `height` isnt' right here: we want to do the
            // full height of the bar, not the full height of the text. It
            // would be useful if we could do Surface.get_height(), but that
            // doesn't seem to be available in cairo-rs for some reason?
            self.context
                .rectangle(0.0, 0.0, self.width(), self.height());
            self.context.fill();
        }

        self.attr.fg_color.apply_to_context(&self.context);
        self.context
            .translate(self.attr.padding.left, self.attr.padding.top);
        self.context.show_pango_layout(&self.layout);

        self.context.restore();
    }
}

struct Pager {
    conn: xcb_util::ewmh::Connection,
    screen_idx: i32,

    active_attr: Attributes,
    inactive_attr: Attributes,
}

impl Pager {
    fn new(active_attr: Attributes, inactive_attr: Attributes) -> Pager {
        let (conn, screen_idx) = xcb::Connection::connect_with_xlib_display().unwrap();
        let ewmh_conn = xcb_util::ewmh::Connection::connect(conn)
            .map_err(|_| ())
            .unwrap();

        Pager {
            conn: ewmh_conn,
            screen_idx: screen_idx,
            active_attr: active_attr,
            inactive_attr: inactive_attr,
        }
    }

    fn get_desktops_info(&self) -> Vec<(bool, String)> {
        let number = xcb_util::ewmh::get_number_of_desktops(&self.conn, self.screen_idx)
            .get_reply()
            .unwrap() as usize;
        let current = xcb_util::ewmh::get_current_desktop(&self.conn, self.screen_idx)
            .get_reply()
            .unwrap() as usize;
        let names_reply = xcb_util::ewmh::get_desktop_names(&self.conn, self.screen_idx)
            .get_reply()
            .unwrap();
        let mut names = names_reply.strings();

        // EWMH states that `number` may not equal `names.len()`, as there may
        // be unnamed desktops, or more desktops than are currently in use.
        if names.len() > number {
            names.truncate(number);
        } else if number > names.len() {
            for i in 0..(number - names.len()) {
                names.push("?");
            }
        }

        names
            .into_iter()
            .enumerate()
            .map(|(i, name)| (i == current, name.to_owned()))
            .collect()
    }

    fn compute_text(&self) -> Vec<Text> {
        let desktops = self.get_desktops_info();
        self.get_desktops_info()
            .into_iter()
            .map(|(active, name)| {
                Text {
                    attr: if active {
                        self.active_attr.clone()
                    } else {
                        self.inactive_attr.clone()
                    },
                    text: name,
                    stretch: false,
                }
            })
            .collect()
    }
}

struct ActiveWindowTitle {
    conn: xcb_util::ewmh::Connection,
    screen_idx: i32,

    attr: Attributes,
}

impl ActiveWindowTitle {
    fn new(attr: Attributes) -> ActiveWindowTitle {
        let (conn, screen_idx) = xcb::Connection::connect_with_xlib_display().unwrap();
        let ewmh_conn = xcb_util::ewmh::Connection::connect(conn)
            .map_err(|_| ())
            .unwrap();

        ActiveWindowTitle {
            conn: ewmh_conn,
            screen_idx: screen_idx,

            attr: attr,
        }
    }

    fn get_active_window_title(&self) -> String {
        let active_window = xcb_util::ewmh::get_active_window(&self.conn, self.screen_idx)
            .get_reply()
            .unwrap();
        let reply = xcb_util::ewmh::get_wm_name(&self.conn, active_window).get_reply();

        match reply {
            Ok(inner) => inner.string().to_owned(),
            // Probably means there's no window focused, or it doesn't have _NET_WM_NAME:
            Err(_) => "".to_owned(),
        }
    }

    fn compute_text(&self) -> Vec<Text> {
        vec![Text {
                 attr: self.attr.clone(),
                 text: self.get_active_window_title(),
                 stretch: true,
             }]
    }
}

struct Clock {
    conn: xcb::Connection,

    attr: Attributes,
}

impl Clock {
    fn new(attr: Attributes) -> Clock {
        let (conn, screen_idx) = xcb::Connection::connect_with_xlib_display().unwrap();

        Clock {
            conn: conn,
            attr: attr,
        }
    }

    fn compute_text(&self) -> Vec<Text> {
        let current_time = Local::now().format("%Y-%m-%d %a %I:%M %p").to_string();
        vec![Text {
                 attr: self.attr.clone(),
                 text: current_time,
                 stretch: false,
             }]
    }
}

// TODO: impl Drop?
struct Window {
    conn: Rc<xcb::Connection>,
    screen_idx: usize,
    id: u32,
    surface: Surface,
}

impl Window {
    fn new(conn: Rc<xcb::Connection>, screen_idx: usize) -> Window {
        let id = conn.generate_id();

        let surface = {
            let screen = conn.get_setup()
                .roots()
                .nth(screen_idx)
                .expect("invalid screen");
            let values = [(xcb::CW_BACK_PIXEL, screen.black_pixel()),
                          (xcb::CW_EVENT_MASK, xcb::EVENT_MASK_EXPOSURE)];

            let (width, height) = (screen.width_in_pixels(), 100);

            xcb::create_window(&conn,
                               xcb::COPY_FROM_PARENT as u8,
                               id,
                               screen.root(),
                               0,
                               0,
                               width,
                               height,
                               10,
                               xcb::WINDOW_CLASS_INPUT_OUTPUT as u16,
                               screen.root_visual(),
                               &values);

            let surface = unsafe {
                let cairo_conn = XCBConnection::from_raw_none(conn.get_raw_conn() as
                                                              *mut cairo_sys::xcb_connection_t);
                let visual =
                    XCBVisualType::from_raw_none(&mut get_root_visual_type(&conn, &screen).base as
                                                 *mut xcb::ffi::xcb_visualtype_t as
                                                 *mut cairo_sys::xcb_visualtype_t);
                let drawable = XCBDrawable(id);
                Surface::create(&cairo_conn, &drawable, &visual, width as i32, height as i32)
                // TODO: Update surface width/height when window size changes.
            };

            surface
        };

        xcb::map_window(&conn, id);
        conn.flush();

        Window {
            conn,
            screen_idx,
            id,
            surface,
        }
    }

    fn screen<'a>(&'a self) -> xcb::Screen<'a> {
        self.conn
            .get_setup()
            .roots()
            .nth(self.screen_idx)
            .expect("Invalid screen")
    }

    fn expose(&self) {
        let inactive_attr = Attributes {
            font: pango::FontDescription::from_string("Envy Code R 27"),
            fg_color: Color::white(),
            bg_color: None,
            padding: Padding::new(10.0, 10.0, 5.0, 5.0),
        };
        let active_attr = inactive_attr.with_bg_color(Some(Color::blue()));

        let mut texts = Pager::new(active_attr, inactive_attr.clone()).compute_text();
        texts.extend(ActiveWindowTitle::new(inactive_attr.clone()).compute_text());
        texts.extend(Clock::new(inactive_attr.clone()).compute_text());

        self.render_text_blocks(texts);

        self.conn.flush();
    }

    fn render_text_blocks(&self, texts: Vec<Text>) {
        // Layout each block of text. After this, we can query the width of each
        // block, which will allow us to do more complex layout below.
        let mut layouts: Vec<_> = texts.into_iter().map(|t| t.layout(&self.surface)).collect();

        // Calculate how much free space we have after laying out all the
        // non-stretch blocks. Split the remaining space (if any) between the
        // stretch blocks. If there isn't enough space for the non-stretch blocks
        // do nothing and allow it to overflow.
        {
            let mut width = 0.0;
            let mut stretched = Vec::new();
            for layout in layouts.iter_mut() {
                if !layout.stretch() {
                    width += layout.width();
                } else {
                    stretched.push(layout);
                }
            }

            let remaining_width = self.screen().width_in_pixels() as f64 - width;
            let remaining_width = if remaining_width < 0.0 {
                0.0
            } else {
                remaining_width
            };
            let width_per_stretched = remaining_width / (stretched.len() as f64);
            for layout in stretched.iter_mut() {
                layout.set_width(width_per_stretched);
            }
        }

        // TODO: Set the height of the window and the height of each text block(?)
        // to the height of the largest bit of text.

        // Finally, just render each block of text in turn.
        let mut x = 0.0;
        for layout in &layouts {
            layout.render(x, 0.0);
            x += layout.width();
        }
    }
}

fn main() {
    let (conn, screen_idx) = xcb::Connection::connect_with_xlib_display().unwrap();
    let conn = Rc::new(conn);

    let w = Window::new(conn.clone(), screen_idx as usize);

    loop {
        let event = conn.wait_for_event();
        match event {
            None => {
                break;
            }
            Some(event) => {
                let r = event.response_type() & !0x80;
                match r {
                    xcb::EXPOSE => w.expose(),
                    _ => {}
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>extern crate rsdocs2docset;
extern crate html5ever;
#[macro_use]
extern crate quick_error;
extern crate clap;
extern crate url;
extern crate sqlite;

use clap::{Arg, App};
use html5ever::driver::{ParseOpts, parse_document};
use html5ever::rcdom::RcDom;
use html5ever::serialize::{SerializeOpts, serialize};
use html5ever::tendril::TendrilSink;

use std::ffi::OsStr;
use std::fs::{self, DirBuilder, DirEntry, File};
use std::io::{self,Write};
use std::path::Path;

use rsdocs2docset::dom::{manipulator, parser};

type Result = std::result::Result<(), RsDoc2DocsetError>;

quick_error! {
    #[derive(Debug)]
    pub enum RsDoc2DocsetError {
        Io(err: std::io::Error) {
            from()
                cause(err)
                description(err.description())
        }
        Utf8(err: std::string::FromUtf8Error) {
            from()
                cause(err)
                description(err.description())
        }
        Sqlite(err: sqlite::Error) {
            from()
                cause(err)
                description(err.description())
        }
    }
}

fn main() {
    let args = App::new("RsDoc2Docset")
        .version(include_str!("../VERSION"))
        .about("A tool that converts rust docs to Dash docset files")
        .arg(Arg::with_name("indir")
             .short("i")
             .long("rsdoc")
             .value_name("INDIR")
             .help("directory that contains rustdoc files")
             .required(true)
             .takes_value(true))
        .arg(Arg::with_name("name")
             .short("n")
             .long("name")
             .value_name("NAME")
             .help("name of the docset")
             .required(true)
             .takes_value(true))
        .get_matches();

    create_docset(args.value_of("indir").unwrap(), args.value_of("name").unwrap()).expect("error creating docset");
    println!("\n{}.docset successfully created!", args.value_of("name").unwrap())
}

fn create_docset(indir: &str, name: &str) -> Result {
    let db_filename = format!("{}.docset/Contents/Resources/docSet.dsidx", name);
    let db_path = Path::new(&db_filename);

    try!(write_file(Path::new(format!("{}.docset/Contents/Info.plist", name).as_str()), format!(include_str!("Info.plist.tmpl"), name = name, identifier = name.replace(" ", "-").to_lowercase()).as_ref()));
    try!(write_file(Path::new(format!("{}.docset/icon.png", name).as_str()), include_bytes!("icon.png")));
    try!(write_file(Path::new(format!("{}.docset/icon@2x.png", name).as_str()), include_bytes!("icon@2x.png")));

    try!(mkdir_p(db_path));
    try!(sqlite::open(db_path).and_then(|c| c.execute("CREATE TABLE searchIndex(id INTEGER PRIMARY KEY, name TEXT, type TEXT, path TEXT);")));

    try!(docset_from_rs_doc_tree(&Path::new(indir), format!("{}.docset/Contents/Resources/Documents/", name).as_str(), Path::new(db_path)));

    Ok(())
}

fn mkdir_p(path: &Path) -> Result {
    let dir = path.parent().unwrap();

    try!(DirBuilder::new()
         .recursive(true)
         .create(dir));
    Ok(())
}

fn write_file(path: &Path, data: &[u8]) -> Result {
    try!(mkdir_p(path));
    try!(File::create(path).and_then(|mut f| f.write_all(data)));
    Ok(())
}

fn docset_from_rs_doc_tree(source_dir: &Path, out_dir: &str, db_path: &Path) -> Result {
        if !source_dir.exists() {
            return Err(io::Error::new(io::ErrorKind::NotFound, format!("{} does not exist", source_dir.to_str().unwrap())).into());
        }

        if source_dir.is_dir() {
            for entry in fs::read_dir(source_dir)? {
                let entry = entry?;
                if entry.path().is_dir() {
                    try!(docset_from_rs_doc_tree(&entry.path(), &out_dir, &db_path));
                } else {
                    try!(annotate_file(&entry, &out_dir, &db_path));
                }
            }
        }
        Ok(())
    }

fn annotate_file(in_file: &DirEntry, output_prefix: &str, db_path: &Path) -> Result {
    let out_file = Path::new(output_prefix).join(in_file.path());

    if in_file.path().extension() != Some(OsStr::new("html")) {
        try!(mkdir_p(&out_file));
        try!(fs::copy(in_file.path(), &out_file));
        //println!("{:70} | ", in_file.path().display());
        print!("."); io::stdout().flush().unwrap();
    } else {
        let mut dom = parse_document(RcDom::default(), ParseOpts::default())
            .from_utf8()
            .read_from( &mut File::open(&in_file.path())? )?;

        let entries = parser::find_entry_elements(&mut dom);
        for entry in entries.iter().filter_map(|x| x.as_ref()) {
            //println!("{:70} | {}", in_file.path().display(), entry);

            let sql_cmd = format!("INSERT OR IGNORE INTO searchIndex(name, type, path) VALUES (\"{}\", \"{}\", \"{}#{}\");", entry.entry_name, entry.entry_type, in_file.path().to_str().unwrap(), entry.anchor_name);
            try!(sqlite::open(db_path).and_then(|c| c.execute(&sql_cmd)))
        }
        manipulator::add_dash_links(&mut dom, &entries);

        let mut bytes = vec![];
        try!(serialize(&mut bytes, &dom.document, SerializeOpts::default()));
        try!(mkdir_p(&out_file));

        print!("o"); io::stdout().flush().unwrap();
    }

    Ok(())
}
<commit_msg>Rename mkdir_p -> mkdir_parent_p to reflect functionality<commit_after>extern crate rsdocs2docset;
extern crate html5ever;
#[macro_use]
extern crate quick_error;
extern crate clap;
extern crate url;
extern crate sqlite;

use clap::{Arg, App};
use html5ever::driver::{ParseOpts, parse_document};
use html5ever::rcdom::RcDom;
use html5ever::serialize::{SerializeOpts, serialize};
use html5ever::tendril::TendrilSink;

use std::ffi::OsStr;
use std::fs::{self, DirBuilder, DirEntry, File};
use std::io::{self,Write};
use std::path::Path;

use rsdocs2docset::dom::{manipulator, parser};

type Result = std::result::Result<(), RsDoc2DocsetError>;

quick_error! {
    #[derive(Debug)]
    pub enum RsDoc2DocsetError {
        Io(err: std::io::Error) {
            from()
                cause(err)
                description(err.description())
        }
        Utf8(err: std::string::FromUtf8Error) {
            from()
                cause(err)
                description(err.description())
        }
        Sqlite(err: sqlite::Error) {
            from()
                cause(err)
                description(err.description())
        }
    }
}

fn main() {
    let args = App::new("RsDoc2Docset")
        .version(include_str!("../VERSION"))
        .about("A tool that converts rust docs to Dash docset files")
        .arg(Arg::with_name("indir")
             .short("i")
             .long("rsdoc")
             .value_name("INDIR")
             .help("directory that contains rustdoc files")
             .required(true)
             .takes_value(true))
        .arg(Arg::with_name("name")
             .short("n")
             .long("name")
             .value_name("NAME")
             .help("name of the docset")
             .required(true)
             .takes_value(true))
        .get_matches();

    create_docset(args.value_of("indir").unwrap(), args.value_of("name").unwrap()).expect("error creating docset");
    println!("\n{}.docset successfully created!", args.value_of("name").unwrap())
}

fn create_docset(indir: &str, name: &str) -> Result {
    let db_filename = format!("{}.docset/Contents/Resources/docSet.dsidx", name);
    let db_path = Path::new(&db_filename);

    try!(write_file(Path::new(format!("{}.docset/Contents/Info.plist", name).as_str()), format!(include_str!("Info.plist.tmpl"), name = name, identifier = name.replace(" ", "-").to_lowercase()).as_ref()));
    try!(write_file(Path::new(format!("{}.docset/icon.png", name).as_str()), include_bytes!("icon.png")));
    try!(write_file(Path::new(format!("{}.docset/icon@2x.png", name).as_str()), include_bytes!("icon@2x.png")));

    try!(mkdir_parent_p(db_path));
    try!(sqlite::open(db_path).and_then(|c| c.execute("CREATE TABLE searchIndex(id INTEGER PRIMARY KEY, name TEXT, type TEXT, path TEXT);")));

    try!(docset_from_rs_doc_tree(&Path::new(indir), format!("{}.docset/Contents/Resources/Documents/", name).as_str(), Path::new(db_path)));

    Ok(())
}

// recursivly creates the parent dir of `path`
fn mkdir_parent_p(path: &Path) -> Result {
    let dir = path.parent().unwrap();

    try!(DirBuilder::new()
         .recursive(true)
         .create(dir));
    Ok(())
}

fn write_file(path: &Path, data: &[u8]) -> Result {
    try!(mkdir_parent_p(path));
    try!(File::create(path).and_then(|mut f| f.write_all(data)));
    Ok(())
}

fn docset_from_rs_doc_tree(source_dir: &Path, out_dir: &str, db_path: &Path) -> Result {
        if !source_dir.exists() {
            return Err(io::Error::new(io::ErrorKind::NotFound, format!("{} does not exist", source_dir.to_str().unwrap())).into());
        }

        if source_dir.is_dir() {
            for entry in fs::read_dir(source_dir)? {
                let entry = entry?;
                if entry.path().is_dir() {
                    try!(docset_from_rs_doc_tree(&entry.path(), &out_dir, &db_path));
                } else {
                    try!(annotate_file(&entry, &out_dir, &db_path));
                }
            }
        }
        Ok(())
    }

fn annotate_file(in_file: &DirEntry, output_prefix: &str, db_path: &Path) -> Result {
    let out_file = Path::new(output_prefix).join(in_file.path());

    if in_file.path().extension() != Some(OsStr::new("html")) {
        try!(mkdir_parent_p(&out_file));
        try!(fs::copy(in_file.path(), &out_file));
        //println!("{:70} | ", in_file.path().display());
        print!("."); io::stdout().flush().unwrap();
    } else {
        let mut dom = parse_document(RcDom::default(), ParseOpts::default())
            .from_utf8()
            .read_from( &mut File::open(&in_file.path())? )?;

        let entries = parser::find_entry_elements(&mut dom);
        for entry in entries.iter().filter_map(|x| x.as_ref()) {
            //println!("{:70} | {}", in_file.path().display(), entry);

            let sql_cmd = format!("INSERT OR IGNORE INTO searchIndex(name, type, path) VALUES (\"{}\", \"{}\", \"{}#{}\");", entry.entry_name, entry.entry_type, in_file.path().to_str().unwrap(), entry.anchor_name);
            try!(sqlite::open(db_path).and_then(|c| c.execute(&sql_cmd)))
        }
        manipulator::add_dash_links(&mut dom, &entries);

        let mut bytes = vec![];
        try!(serialize(&mut bytes, &dom.document, SerializeOpts::default()));
        try!(write_file(&out_file, bytes.as_ref()));

        print!("o"); io::stdout().flush().unwrap();
    }

    Ok(())
}
<|endoftext|>


<commit_before>extern crate colored;
extern crate json;
extern crate structopt;
extern crate syntax;

mod builder;
mod dot_printer;
mod error;
mod manifest;
mod printer;
mod tree;

use std::path;
use std::process;

use syntax::ast::NodeId;
use syntax::parse::{self, ParseSess};
use syntax::source_map;
use syntax::visit::Visitor;

use structopt::StructOpt;

use colored::*;

use builder::Builder;
use builder::Config as BuilderConfig;

use error::Error;

use manifest::{Edition, Manifest, Target};

use printer::Config as PrinterConfig;
use printer::Printer;

use dot_printer::Config as DotPrinterConfig;
use dot_printer::DotPrinter;

fn choose_target<'a>(args: &Arguments, manifest: &'a Manifest) -> Result<&'a Target, Error> {
    if args.lib {
        // If `--lib` is enabled use the first library target.
        manifest
            .targets
            .iter()
            .find(|t| t.is_lib())
            .ok_or(Error::NoLibraryTargetFound)
    } else if let Some(ref name) = args.bin {
        // If a binary target is specified use that.
        manifest
            .targets
            .iter()
            .find(|t| t.is_bin() && &t.name() == &name)
            .ok_or(Error::NoMatchingBinaryTargetFound)
    } else if manifest.targets.len() == 1 {
        // If neither `--lib` is enabled nor `--bin` target is specified but
        // there is only one target, use that.
        Ok(manifest.targets.first().unwrap())
    } else {
        // If there are multiple targets use the first library target.
        manifest
            .targets
            .iter()
            .find(|t| t.is_lib())
            .ok_or(Error::NoTargetProvided)
    }
}

fn run(args: &Arguments) -> Result<(), Error> {
    let manifest: Manifest = {
        let output = process::Command::new("cargo").arg("read-manifest").output();
        let stdout = try!(output.map_err(Error::CargoExecutionFailed)).stdout;
        let json_string = String::from_utf8(stdout).expect("Failed reading cargo output");
        Manifest::from_str(&json_string)?
    };

    if args.enable_edition_2018 && manifest.edition == Edition::E2018 {
        println!(
            "{}\n{}",
            "Edition 2018 support is work in progress.".red(),
            "`--enable-edition-2018` will be ignored.".red()
        );
    }

    // TODO: Check to see if build scripts really need to be ignored.
    //       Seems like they are not mistaken as orphans anyway.
    let build_scripts: Vec<path::PathBuf> = manifest
        .custom_builds()
        .iter()
        .map(|t| t.src_path().clone())
        .collect();

    let target: &Target = choose_target(args, &manifest)?;
    let parse_session = ParseSess::new(source_map::FilePathMapping::empty());

    syntax::with_globals(|| {
        let krate = try!(match parse::parse_crate_from_file(
            target.src_path().as_ref(),
            &parse_session
        ) {
            Ok(_) if parse_session.span_diagnostic.has_errors() => Err(None),
            Ok(krate) => Ok(krate),
            Err(e) => Err(Some(e)),
        }
        .map_err(|e| Error::Syntax(format!("{:?}", e))));

        let builder_config = BuilderConfig {
            include_orphans: args.orphans,
            ignored_files: build_scripts,
        };
        let mut builder = Builder::new(
            builder_config,
            target.name().clone(),
            parse_session.source_map(),
        );
        builder.visit_mod(
            &krate.module,
            krate.span,
            &krate.attrs[..],
            NodeId::from(0_usize),
        );

        match args.command {
            Command::Graph {
                conditional,
                external,
                types,
            } => {
                let printer_config = DotPrinterConfig {
                    colored: !args.plain,
                    show_conditional: conditional,
                    show_external: external,
                    show_types: types,
                };
                println!("digraph something {{");
                let tree = builder.tree();
                let printer = DotPrinter::new(printer_config, tree);
                tree.accept(&mut vec![], &mut vec![], &printer);
                println!("}}");
            }
            Command::Tree => {
                let printer_config = PrinterConfig {
                    colored: !args.plain,
                };
                let printer = Printer::new(printer_config);
                println!();
                let tree = builder.tree();
                tree.accept(&mut vec![], &mut vec![], &printer);
                println!();
            }
        }

        Ok(())
    })
}

#[derive(StructOpt)]
#[structopt(
    name = "cargo-modules",
    about = "Print a crate's module tree or graph.",
    author = "",
    after_help = "If neither `--bin` nor `--example` are given,\n\
                  then if the project only has one bin target it will be run.\n\
                  Otherwise `--bin` specifies the bin target to run.\n\
                  At most one `--bin` can be provided.\n\
                  \n(On 'Windows' systems coloring is disabled. Sorry.)\n"
)]
struct Arguments {
    /// Include orphaned modules (i.e. unused files in /src).
    #[structopt(short = "o", long = "orphans")]
    orphans: bool,

    /// List modules of this package's library (overrides '--bin')
    #[structopt(short = "l", long = "lib")]
    lib: bool,

    /// Plain uncolored output.
    #[structopt(short = "p", long = "plain")]
    plain: bool,

    /// List modules of the specified binary
    #[structopt(short = "b", long = "bin")]
    bin: Option<String>,

    /// [Experimental] Enable support for edition 2018 of Rust (ignored)
    #[structopt(long = "enable-edition-2018")]
    enable_edition_2018: bool,

    /// Sets an explicit crate path (ignored)
    #[structopt(name = "CRATE_DIR")]
    _dir: Option<String>,

    #[structopt(subcommand)]
    command: Command,
}

#[derive(StructOpt)]
enum Command {
    #[structopt(name = "tree", about = "Print a crate's module tree.", author = "")]
    Tree,
    #[structopt(
        name = "graph",
        about = "Print a crate's module graph.",
        author = "",
        after_help = "If you have xdot installed on your system, you can run this using:\n\
                      `cargo modules graph | xdot -`"
    )]
    Graph {
        /// Show external types.
        #[structopt(short = "e", long = "external")]
        external: bool,
        /// Show conditional modules.
        #[structopt(short = "c", long = "conditional")]
        conditional: bool,
        /// Plain uncolored output.
        #[structopt(short = "t", long = "types")]
        types: bool,
    },
}

fn main() {
    let arguments = Arguments::from_args();

    if let Err(error) = run(&arguments) {
        let error_string = match error {
            Error::CargoExecutionFailed(error) => {
                format!("Error: Failed to run `cargo` command.\n{:?}", error)
            }
            Error::InvalidManifestJson(error) => {
                format!("Error: Failed to parse JSON response.\n{:?}", error)
            }
            Error::NoLibraryTargetFound => "Error: No library target found.".to_string(),
            Error::NoMatchingBinaryTargetFound => {
                "Error: No matching binary target found.".to_string()
            }
            Error::NoTargetProvided => "Error: Please specify a target to process.".to_string(),
            Error::Syntax(error) => format!("Error: Failed to parse: {}", error),
        };
        println!("{}", error_string.red());
    }
}
<commit_msg>Clippy<commit_after>extern crate colored;
extern crate json;
extern crate structopt;
extern crate syntax;

mod builder;
mod dot_printer;
mod error;
mod manifest;
mod printer;
mod tree;

use std::path;
use std::process;

use syntax::ast::NodeId;
use syntax::parse::{self, ParseSess};
use syntax::source_map;
use syntax::visit::Visitor;

use structopt::StructOpt;

use colored::*;

use builder::Builder;
use builder::Config as BuilderConfig;

use error::Error;

use manifest::{Edition, Manifest, Target};

use printer::Config as PrinterConfig;
use printer::Printer;

use dot_printer::Config as DotPrinterConfig;
use dot_printer::DotPrinter;

fn choose_target<'a>(args: &Arguments, manifest: &'a Manifest) -> Result<&'a Target, Error> {
    if args.lib {
        // If `--lib` is enabled use the first library target.
        manifest
            .targets
            .iter()
            .find(|t| t.is_lib())
            .ok_or(Error::NoLibraryTargetFound)
    } else if let Some(ref name) = args.bin {
        // If a binary target is specified use that.
        manifest
            .targets
            .iter()
            .find(|t| t.is_bin() && t.name() == name)
            .ok_or(Error::NoMatchingBinaryTargetFound)
    } else if manifest.targets.len() == 1 {
        // If neither `--lib` is enabled nor `--bin` target is specified but
        // there is only one target, use that.
        Ok(manifest.targets.first().unwrap())
    } else {
        // If there are multiple targets use the first library target.
        manifest
            .targets
            .iter()
            .find(|t| t.is_lib())
            .ok_or(Error::NoTargetProvided)
    }
}

fn run(args: &Arguments) -> Result<(), Error> {
    let manifest: Manifest = {
        let output = process::Command::new("cargo").arg("read-manifest").output();
        let stdout = try!(output.map_err(Error::CargoExecutionFailed)).stdout;
        let json_string = String::from_utf8(stdout).expect("Failed reading cargo output");
        Manifest::from_str(&json_string)?
    };

    if args.enable_edition_2018 && manifest.edition == Edition::E2018 {
        println!(
            "{}\n{}",
            "Edition 2018 support is work in progress.".red(),
            "`--enable-edition-2018` will be ignored.".red()
        );
    }

    // TODO: Check to see if build scripts really need to be ignored.
    //       Seems like they are not mistaken as orphans anyway.
    let build_scripts: Vec<path::PathBuf> = manifest
        .custom_builds()
        .iter()
        .map(|t| t.src_path().clone())
        .collect();

    let target: &Target = choose_target(args, &manifest)?;
    let parse_session = ParseSess::new(source_map::FilePathMapping::empty());

    syntax::with_globals(|| {
        let krate = try!(match parse::parse_crate_from_file(
            target.src_path().as_ref(),
            &parse_session
        ) {
            Ok(_) if parse_session.span_diagnostic.has_errors() => Err(None),
            Ok(krate) => Ok(krate),
            Err(e) => Err(Some(e)),
        }
        .map_err(|e| Error::Syntax(format!("{:?}", e))));

        let builder_config = BuilderConfig {
            include_orphans: args.orphans,
            ignored_files: build_scripts,
        };
        let mut builder = Builder::new(
            builder_config,
            target.name().clone(),
            parse_session.source_map(),
        );
        builder.visit_mod(
            &krate.module,
            krate.span,
            &krate.attrs[..],
            NodeId::from(0_usize),
        );

        match args.command {
            Command::Graph {
                conditional,
                external,
                types,
            } => {
                let printer_config = DotPrinterConfig {
                    colored: !args.plain,
                    show_conditional: conditional,
                    show_external: external,
                    show_types: types,
                };
                println!("digraph something {{");
                let tree = builder.tree();
                let printer = DotPrinter::new(printer_config, tree);
                tree.accept(&mut vec![], &mut vec![], &printer);
                println!("}}");
            }
            Command::Tree => {
                let printer_config = PrinterConfig {
                    colored: !args.plain,
                };
                let printer = Printer::new(printer_config);
                println!();
                let tree = builder.tree();
                tree.accept(&mut vec![], &mut vec![], &printer);
                println!();
            }
        }

        Ok(())
    })
}

#[derive(StructOpt)]
#[structopt(
    name = "cargo-modules",
    about = "Print a crate's module tree or graph.",
    author = "",
    after_help = "If neither `--bin` nor `--example` are given,\n\
                  then if the project only has one bin target it will be run.\n\
                  Otherwise `--bin` specifies the bin target to run.\n\
                  At most one `--bin` can be provided.\n\
                  \n(On 'Windows' systems coloring is disabled. Sorry.)\n"
)]
struct Arguments {
    /// Include orphaned modules (i.e. unused files in /src).
    #[structopt(short = "o", long = "orphans")]
    orphans: bool,

    /// List modules of this package's library (overrides '--bin')
    #[structopt(short = "l", long = "lib")]
    lib: bool,

    /// Plain uncolored output.
    #[structopt(short = "p", long = "plain")]
    plain: bool,

    /// List modules of the specified binary
    #[structopt(short = "b", long = "bin")]
    bin: Option<String>,

    /// [Experimental] Enable support for edition 2018 of Rust (ignored)
    #[structopt(long = "enable-edition-2018")]
    enable_edition_2018: bool,

    /// Sets an explicit crate path (ignored)
    #[structopt(name = "CRATE_DIR")]
    _dir: Option<String>,

    #[structopt(subcommand)]
    command: Command,
}

#[derive(StructOpt)]
enum Command {
    #[structopt(name = "tree", about = "Print a crate's module tree.", author = "")]
    Tree,
    #[structopt(
        name = "graph",
        about = "Print a crate's module graph.",
        author = "",
        after_help = "If you have xdot installed on your system, you can run this using:\n\
                      `cargo modules graph | xdot -`"
    )]
    Graph {
        /// Show external types.
        #[structopt(short = "e", long = "external")]
        external: bool,
        /// Show conditional modules.
        #[structopt(short = "c", long = "conditional")]
        conditional: bool,
        /// Plain uncolored output.
        #[structopt(short = "t", long = "types")]
        types: bool,
    },
}

fn main() {
    let arguments = Arguments::from_args();

    if let Err(error) = run(&arguments) {
        let error_string = match error {
            Error::CargoExecutionFailed(error) => {
                format!("Error: Failed to run `cargo` command.\n{:?}", error)
            }
            Error::InvalidManifestJson(error) => {
                format!("Error: Failed to parse JSON response.\n{:?}", error)
            }
            Error::NoLibraryTargetFound => "Error: No library target found.".to_string(),
            Error::NoMatchingBinaryTargetFound => {
                "Error: No matching binary target found.".to_string()
            }
            Error::NoTargetProvided => "Error: Please specify a target to process.".to_string(),
            Error::Syntax(error) => format!("Error: Failed to parse: {}", error),
        };
        println!("{}", error_string.red());
    }
}
<|endoftext|>


<commit_before>#![feature(globs)]
#![feature(macro_rules)]

extern crate xml;

use std::io::{File, BufferedReader};
use std::default::Default;
use std::num::from_str_radix;

use xml::reader::EventReader;
use fromxml::{FromXml, XmlIter};

mod fromxml;

deriving_fromxml! {
    struct Device {
        vendor:Option<String>,
        vendorID:Option<String>,
        licenseText:Option<String>,
        series:Option<String>,
        version:Option<String>,
        description:Option<String>,
        cpu:Option<CPU>,
        peripherals: Vec<Peripheral>,
    }
}

deriving_fromxml! {
    struct Peripheral {
        name: String,
        version:Option<String>,
        description:Option<String>,
        groupName:Option<String>,
        prependToName:Option<String>,
        baseAddress:Option<String>,
        access:Option<String>,
        interrupt:Option<Interrupt>,
        registers: Vec<Regs>,
    }
}

deriving_fromxml! {
    struct Interrupt {
        name: String,
        value:Option<uint>,
    }
}

deriving_fromxml! {
    enum Regs {
        register(Register),
        cluster(Cluster),
    }
}

deriving_fromxml! {
    struct Register {
        name: String,
        dim:Option<uint>,
        dimIncrement:Option<String>,
        description:Option<String>,
        addressOffset:Option<String>,
        size:Option<uint>,
        access:Option<String>,
        fields: Vec<Field>,
    }
}

#[deriving(Default, Show)]
struct Cluster {
    name: String,
    registers: Vec<Register>,
}

impl ::fromxml::FromXml for Cluster {
    fn from_xml(iter: &mut ::fromxml::XmlIter) -> Result<Cluster, ()> {
        let mut obj = Cluster{..Default::default()};
        try!(iter.each_child(|iter| {
            match iter.tag_name() {
                    "name" => obj.name = try!(::fromxml::FromXml::from_xml(iter)),
                    "register" => obj.registers.push(try!(::fromxml::FromXml::from_xml(iter))),
                    _ => try!(iter.skip_node())
            }
            Ok(())
        }));
        Ok(obj)
    }
}

deriving_fromxml! {
    struct Field {
        name: String,
        description:Option<String>,
        bitOffset:Option<uint>,
        bitWidth:Option<uint>,
        bitRange:Option<String>,
        access:Option<String>,
        enumeratedValues: Vec<EnumeratedValue>,
    }
}

deriving_fromxml! {
    struct EnumeratedValue {
        name: String,
        value: String,
        description:Option<String>,
    }
}

deriving_fromxml! {
    struct CPU {
        name:Option<String>,
        revision:Option<String>,
    }
}

fn parse_num(s: &str) -> Option<u32> {
    if s.slice_to(2) == "0x" {
        from_str_radix(s.slice_from(2), 16)
    } else {
        from_str_radix(s, 10)
    }
}

fn write_doc_comment(doc: Option<&String>) {
    if let Some(d) = doc {
        print!(" //= {}", d);
    }
}

fn write_device(device: &Device) {
    for peripheral in device.peripherals.iter() {
        write_peripheral(peripheral);
    }
}

fn write_peripheral(peripheral: &Peripheral) {
    let mut registers = vec![];

    println!("ioregs!({} = {{", peripheral.name);
    for register in peripheral.registers.iter() {
        match *register {
            Regs::register(ref r) => registers.push(r),
            Regs::cluster(ref c) => {
                let mut cluster_regs: Vec<_> = c.registers.iter().collect();

                println!("    // cluster: {}", c.name);
                write_registers(&mut *cluster_regs);
            }
        }
    }
    write_registers(&mut *registers);
    println!("}});")
}

fn write_registers(registers: &mut[&Register]) {
    registers.as_mut_slice().sort_by(|a, b| {
        let a = a.addressOffset.as_ref().map(|x| parse_num(x.as_slice()));
        let b = b.addressOffset.as_ref().map(|x| parse_num(x.as_slice()));
        a.cmp(&b)
    });

    for &r in registers.iter() {
        write_register(r);
    }
}

fn write_register(register: &Register) {
    if let (Some(dim), Some(dim_increment)) = (register.dim, register.dimIncrement.as_ref()) {
        println!("    // repeat: {} increment {}", dim, dim_increment);
    }
    let offset = register.addressOffset.as_ref().map_or("", |x| x.as_slice());
    let size = register.size.unwrap_or(0);
    print!("    {} => reg{} {} {{", offset, size, register.name);
    write_doc_comment(register.description.as_ref());
    print!("\n");

    for field in register.fields.iter() {
        write_field(field);
    }
    println!("    }}");
}

fn write_field(field: &Field) {
    if field.name == "RESERVED" { return }

    let (lsb, width) = if let Some(ref br) = field.bitRange {
        let split: Vec<&str> = br.as_slice().split(':').collect();
        assert_eq!(split.len(), 2);
        let end: uint = split[0].slice_from(1).parse().unwrap();
        let start: uint = split[1].slice_to(split[1].len()-1).parse().unwrap();
        (start, end-start+1)
    } else {
        (field.bitOffset.unwrap(), field.bitWidth.unwrap())
    };

    print!("         {}", lsb);

    if width > 1 {
        print!("..{}", lsb + width - 1);
    }

    print!(" => {}", field.name);

    if field.enumeratedValues.len() == 0 {
        print!(",");
    } else {
        print!(" {{");
    }

    write_doc_comment(field.description.as_ref());
    print!("\n");

    if field.enumeratedValues.len() != 0 {
        for en in field.enumeratedValues.iter() {
            if en.name.len() == 0 { continue } // TODO: this is a <name>, not an <enumeratedValues>
            print!("             {} => {},", en.value, en.name);
            write_doc_comment(en.description.as_ref());
            print!("\n");
        }
        println!("         }}");
    }
}

fn main() {
    let file = File::open(&Path::new("file.xml")).unwrap();
    let reader = BufferedReader::new(file);

    let mut parser = EventReader::new(reader);
    let mut iter = XmlIter::new(parser.events()).unwrap();
    let device: Device = FromXml::from_xml(&mut iter).unwrap();

    write_device(&device);
}
<commit_msg>Don't hardcode input filename<commit_after>#![feature(globs)]
#![feature(macro_rules)]

extern crate xml;

use std::io::{File, BufferedReader};
use std::default::Default;
use std::num::from_str_radix;

use xml::reader::EventReader;
use fromxml::{FromXml, XmlIter};

mod fromxml;

deriving_fromxml! {
    struct Device {
        vendor:Option<String>,
        vendorID:Option<String>,
        licenseText:Option<String>,
        series:Option<String>,
        version:Option<String>,
        description:Option<String>,
        cpu:Option<CPU>,
        peripherals: Vec<Peripheral>,
    }
}

deriving_fromxml! {
    struct Peripheral {
        name: String,
        version:Option<String>,
        description:Option<String>,
        groupName:Option<String>,
        prependToName:Option<String>,
        baseAddress:Option<String>,
        access:Option<String>,
        interrupt:Option<Interrupt>,
        registers: Vec<Regs>,
    }
}

deriving_fromxml! {
    struct Interrupt {
        name: String,
        value:Option<uint>,
    }
}

deriving_fromxml! {
    enum Regs {
        register(Register),
        cluster(Cluster),
    }
}

deriving_fromxml! {
    struct Register {
        name: String,
        dim:Option<uint>,
        dimIncrement:Option<String>,
        description:Option<String>,
        addressOffset:Option<String>,
        size:Option<uint>,
        access:Option<String>,
        fields: Vec<Field>,
    }
}

#[deriving(Default, Show)]
struct Cluster {
    name: String,
    registers: Vec<Register>,
}

impl ::fromxml::FromXml for Cluster {
    fn from_xml(iter: &mut ::fromxml::XmlIter) -> Result<Cluster, ()> {
        let mut obj = Cluster{..Default::default()};
        try!(iter.each_child(|iter| {
            match iter.tag_name() {
                    "name" => obj.name = try!(::fromxml::FromXml::from_xml(iter)),
                    "register" => obj.registers.push(try!(::fromxml::FromXml::from_xml(iter))),
                    _ => try!(iter.skip_node())
            }
            Ok(())
        }));
        Ok(obj)
    }
}

deriving_fromxml! {
    struct Field {
        name: String,
        description:Option<String>,
        bitOffset:Option<uint>,
        bitWidth:Option<uint>,
        bitRange:Option<String>,
        access:Option<String>,
        enumeratedValues: Vec<EnumeratedValue>,
    }
}

deriving_fromxml! {
    struct EnumeratedValue {
        name: String,
        value: String,
        description:Option<String>,
    }
}

deriving_fromxml! {
    struct CPU {
        name:Option<String>,
        revision:Option<String>,
    }
}

fn parse_num(s: &str) -> Option<u32> {
    if s.slice_to(2) == "0x" {
        from_str_radix(s.slice_from(2), 16)
    } else {
        from_str_radix(s, 10)
    }
}

fn write_doc_comment(doc: Option<&String>) {
    if let Some(d) = doc {
        print!(" //= {}", d);
    }
}

fn write_device(device: &Device) {
    for peripheral in device.peripherals.iter() {
        write_peripheral(peripheral);
    }
}

fn write_peripheral(peripheral: &Peripheral) {
    let mut registers = vec![];

    println!("ioregs!({} = {{", peripheral.name);
    for register in peripheral.registers.iter() {
        match *register {
            Regs::register(ref r) => registers.push(r),
            Regs::cluster(ref c) => {
                let mut cluster_regs: Vec<_> = c.registers.iter().collect();

                println!("    // cluster: {}", c.name);
                write_registers(&mut *cluster_regs);
            }
        }
    }
    write_registers(&mut *registers);
    println!("}});")
}

fn write_registers(registers: &mut[&Register]) {
    registers.as_mut_slice().sort_by(|a, b| {
        let a = a.addressOffset.as_ref().map(|x| parse_num(x.as_slice()));
        let b = b.addressOffset.as_ref().map(|x| parse_num(x.as_slice()));
        a.cmp(&b)
    });

    for &r in registers.iter() {
        write_register(r);
    }
}

fn write_register(register: &Register) {
    if let (Some(dim), Some(dim_increment)) = (register.dim, register.dimIncrement.as_ref()) {
        println!("    // repeat: {} increment {}", dim, dim_increment);
    }
    let offset = register.addressOffset.as_ref().map_or("", |x| x.as_slice());
    let size = register.size.unwrap_or(0);
    print!("    {} => reg{} {} {{", offset, size, register.name);
    write_doc_comment(register.description.as_ref());
    print!("\n");

    for field in register.fields.iter() {
        write_field(field);
    }
    println!("    }}");
}

fn write_field(field: &Field) {
    if field.name == "RESERVED" { return }

    let (lsb, width) = if let Some(ref br) = field.bitRange {
        let split: Vec<&str> = br.as_slice().split(':').collect();
        assert_eq!(split.len(), 2);
        let end: uint = split[0].slice_from(1).parse().unwrap();
        let start: uint = split[1].slice_to(split[1].len()-1).parse().unwrap();
        (start, end-start+1)
    } else {
        (field.bitOffset.unwrap(), field.bitWidth.unwrap())
    };

    print!("         {}", lsb);

    if width > 1 {
        print!("..{}", lsb + width - 1);
    }

    print!(" => {}", field.name);

    if field.enumeratedValues.len() == 0 {
        print!(",");
    } else {
        print!(" {{");
    }

    write_doc_comment(field.description.as_ref());
    print!("\n");

    if field.enumeratedValues.len() != 0 {
        for en in field.enumeratedValues.iter() {
            if en.name.len() == 0 { continue } // TODO: this is a <name>, not an <enumeratedValues>
            print!("             {} => {},", en.value, en.name);
            write_doc_comment(en.description.as_ref());
            print!("\n");
        }
        println!("         }}");
    }
}

fn main() {
    let args = std::os::args();
    let filename = args.get(1).expect("No SVD filename provided");
    let file = File::open(&Path::new(filename)).unwrap();
    let reader = BufferedReader::new(file);

    let mut parser = EventReader::new(reader);
    let mut iter = XmlIter::new(parser.events()).unwrap();
    let device: Device = FromXml::from_xml(&mut iter).unwrap();

    write_device(&device);
}
<|endoftext|>


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

//! Arrow Client definitions.

extern crate libc;
extern crate native_tls;
extern crate openssl;
extern crate time;
extern crate uuid;

#[macro_use]
extern crate json;

extern crate bytes;

#[macro_use]
extern crate futures;

extern crate tokio_core;
extern crate tokio_io;
extern crate tokio_timer;
extern crate tokio_tls;

pub mod futures_ex;

#[macro_use]
pub mod utils;

pub mod net;

pub mod config;
pub mod context;
pub mod cmd_handler;
pub mod scanner;
pub mod svc_table;
pub mod timer;

use std::process;
use std::thread;

use std::error::Error;
use std::fmt::Debug;
use std::time::Duration;

use tokio_core::reactor::Core as TokioCore;

use config::usage;

use config::ApplicationConfig;
use context::{ApplicationContext, ConnectionState};

use cmd_handler::{Command, CommandChannel};

use net::arrow;

use net::arrow::{ArrowError, ErrorKind};

use utils::logger::Logger;

/// Connectionn retry timeout.
const RETRY_TIMEOUT: f64 = 60.0;

/// Get maximum duration of the pairing mode.
const PAIRING_MODE_TIMEOUT: f64 = 1200.0;

/// Unwrap a given result (if possible) or print the error message and exit
/// the process printing application usage.
fn result_or_usage<T, E>(res: Result<T, E>) -> T
    where E: Error + Debug {
    match res {
        Ok(res)  => res,
        Err(err) => {
            println!("ERROR: {}\n", err);
            usage(1);
        }
    }
}

/// Arrow Client main thread.
///
/// This function ensures maintaining connection with a remote Arrow Service.
fn arrow_thread(mut app_context: ApplicationContext, cmd_channel: CommandChannel) {
    let mut logger = app_context.get_logger();

    let diagnostic_mode = app_context.get_diagnostic_mode();
    let addr = app_context.get_arrow_service_address();

    let t = time::precise_time_s();

    let mut pairing_mode_timeout = t + 1200.0;
    let mut cur_addr = addr.clone();
    let mut last_attempt;

    loop {
        log_info!(logger, "connecting to remote Arrow Service {}", cur_addr);

        last_attempt = time::precise_time_s();

        app_context.set_connection_state(ConnectionState::Connected);

        let res = arrow::connect(
            app_context.clone(),
            cmd_channel.clone(),
            &cur_addr);

        pairing_mode_timeout = get_pairing_mode_timeout(
            &res,
            last_attempt,
            pairing_mode_timeout);

        if diagnostic_mode {
            diagnose_connection_result(&res);
        }

        match res {
            Ok(addr) => cur_addr = addr,
            Err(err) => {
                log_warn!(logger, "{}", err.description());

                let cstate = match err.kind() {
                    ErrorKind::Unauthorized => ConnectionState::Unauthorized,
                    _                       => ConnectionState::Disconnected,
                };

                app_context.set_connection_state(cstate);

                let t = get_next_retry_timeout(
                    err,
                    last_attempt,
                    pairing_mode_timeout);

                if t > 0.5 {
                    log_info!(logger, "retrying in {:.3} seconds", t);
                    thread::sleep(Duration::from_millis((t * 1000.0) as u64));
                }

                cur_addr = addr.to_string();
            }
        }
    }
}

/// Get new timeout for the pairing mode.
fn get_pairing_mode_timeout(
    connection_result: &Result<String, ArrowError>,
    last_connection_attempt: f64,
    current_timeout: f64) -> f64 {
    let t = time::precise_time_s();

    match connection_result {
        // We know the client is authorized, we can update the timeout.
        &Ok(_)        => t + PAIRING_MODE_TIMEOUT,
        &Err(ref err) => match err.kind() {
            // We don't update the timeout in case the client is unauthorized.
            ErrorKind::Unauthorized => current_timeout,
            // We don't know if the client is authorized but we assume it is
            // if the last connection was longer than RETRY_TIMEOUT seconds.
            _ => if (last_connection_attempt + RETRY_TIMEOUT) < t {
                t + PAIRING_MODE_TIMEOUT
            } else {
                current_timeout
            }
        }
    }
}

/// Get next reconnect timeout for the Arrow Client thread.
fn get_next_retry_timeout(
    connection_error: ArrowError,
    last_connection_attempt: f64,
    pairing_mode_timeout: f64) -> f64 {
    let t = time::precise_time_s();

    match connection_error.kind() {
        // the client is not authorized to access the service yet; check the
        // pairing mode timeout
        ErrorKind::Unauthorized => match pairing_mode_timeout {
            // retry every 10 seconds in the first 10 minutes since the first
            // "unauthorized" response
            timeout if t < (timeout - 600.0) => 10.0,
            // retry every 30 seconds after the first 10 minutes since the
            // first "unauthorized" response
            timeout if t < timeout => 30.0,
            // retry in 10 hours after the first 20 minutes since the first
            // "unauthorized" response
            _ => 36000.0
        },
        // set a very long retry timeout if the version of the Arrow Protocol
        // is not supported by either side
        ErrorKind::UnsupportedProtocolVersion => 36000.0,
        // in all other cases
        _ => RETRY_TIMEOUT + last_connection_attempt - time::precise_time_s()
    }
}

/// Diagnose a given connection result and exit with exit code 0 if the
/// connection was successful or the server responded with UNAUTHORIZED,
/// otherwise exit with exit code 1.
fn diagnose_connection_result(
    connection_result: &Result<String, ArrowError>) -> ! {
    match connection_result {
        &Ok(_)        => process::exit(0),
        &Err(ref err) => match err.kind() {
            ErrorKind::Unauthorized => process::exit(0),
            _ => process::exit(1)
        }
    }
}

/// Arrow Client main function.
fn main() {
    let mut core = TokioCore::new()
        .expect("unable to create an event loop");

    let handle = core.handle();

    let config = result_or_usage(
        ApplicationConfig::create());

    let context = ApplicationContext::new(config);

    let mut logger = context.get_logger();

    log_info!(&mut logger,
        "application started (uuid: {}, mac: {})",
        context.get_arrow_uuid(), context.get_arrow_mac_address());

    // create command handler
    let (tx, rx) = cmd_handler::new(context.clone());

    let cmd_channel = tx.clone();

    // schedule periodic network scan
    let periodic_network_scan = context.get_timer()
        .create_periodic_task(
            Duration::from_secs(1),
            move || {
                tx.send(Command::PeriodicNetworkScan)
            }
        );

    handle.spawn(periodic_network_scan);

    // start the Arrow client thread
    thread::spawn(move || {
        arrow_thread(context, cmd_channel)
    });

    // run the command handler event loop
    core.run(rx)
        .unwrap_or_default();
}
<commit_msg>Suspend the Arrow connection thread after the pairing window timeout<commit_after>// Copyright 2015 click2stream, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Arrow Client definitions.

extern crate libc;
extern crate native_tls;
extern crate openssl;
extern crate time;
extern crate uuid;

#[macro_use]
extern crate json;

extern crate bytes;

#[macro_use]
extern crate futures;

extern crate tokio_core;
extern crate tokio_io;
extern crate tokio_timer;
extern crate tokio_tls;

pub mod futures_ex;

#[macro_use]
pub mod utils;

pub mod net;

pub mod config;
pub mod context;
pub mod cmd_handler;
pub mod scanner;
pub mod svc_table;
pub mod timer;

use std::process;
use std::thread;

use std::error::Error;
use std::fmt::Debug;
use std::time::Duration;

use tokio_core::reactor::Core as TokioCore;

use config::usage;

use config::ApplicationConfig;
use context::{ApplicationContext, ConnectionState};

use cmd_handler::{Command, CommandChannel};

use net::arrow;

use net::arrow::{ArrowError, ErrorKind};

use utils::logger::Logger;

/// Connectionn retry timeout.
const RETRY_TIMEOUT: f64 = 60.0;

/// Get maximum duration of the pairing mode.
const PAIRING_MODE_TIMEOUT: f64 = 1200.0;

/// Unwrap a given result (if possible) or print the error message and exit
/// the process printing application usage.
fn result_or_usage<T, E>(res: Result<T, E>) -> T
    where E: Error + Debug {
    match res {
        Ok(res)  => res,
        Err(err) => {
            println!("ERROR: {}\n", err);
            usage(1);
        }
    }
}

/// Arrow Client main thread.
///
/// This function ensures maintaining connection with a remote Arrow Service.
fn arrow_thread(mut app_context: ApplicationContext, cmd_channel: CommandChannel) {
    let mut logger = app_context.get_logger();

    let diagnostic_mode = app_context.get_diagnostic_mode();
    let addr = app_context.get_arrow_service_address();

    let t = time::precise_time_s();

    let pairing_mode_timeout = t + PAIRING_MODE_TIMEOUT;

    let mut cur_addr = addr.clone();
    let mut last_attempt;

    loop {
        log_info!(logger, "connecting to remote Arrow Service {}", cur_addr);

        last_attempt = time::precise_time_s();

        app_context.set_connection_state(ConnectionState::Connected);

        let res = arrow::connect(
            app_context.clone(),
            cmd_channel.clone(),
            &cur_addr);

        if diagnostic_mode {
            diagnose_connection_result(&res);
        }

        match res {
            Ok(addr) => cur_addr = addr,
            Err(err) => {
                log_warn!(logger, "{}", err.description());

                let cstate = match err.kind() {
                    ErrorKind::Unauthorized => ConnectionState::Unauthorized,
                    _                       => ConnectionState::Disconnected,
                };

                app_context.set_connection_state(cstate);

                let retry = process_connection_error(
                    err,
                    last_attempt,
                    pairing_mode_timeout);

                wait_for_retry(&mut logger, retry);

                cur_addr = addr.to_string();
            }
        }
    }
}

/// Connection retry variants. There are only two options - the connection can
/// be either retried after a specified timeout or there should be no more
/// connection attempts because of a specified reason.
#[derive(Debug, Copy, Clone)]
enum ConnectionRetry {
    Timeout(f64),
    Suspend(SuspendReason),
}

/// Reason for suspending the Arrow connection thread.
#[derive(Debug, Copy, Clone)]
enum SuspendReason {
    NotInPairingMode,
    UnsupportedProtocolVersion,
}

impl SuspendReason {
    fn to_string(&self) -> &str {
        match *self {
            SuspendReason::NotInPairingMode =>
                "pairing window timeout",
            SuspendReason::UnsupportedProtocolVersion =>
                "unsupported protocol version",
        }
    }
}

/// Process a given connection error and return a ConnectionRetry instance.
fn process_connection_error(
    connection_error: ArrowError,
    last_attempt: f64,
    pairing_mode_timeout: f64) -> ConnectionRetry {
    let t = time::precise_time_s();

    match connection_error.kind() {
        // the client is not authorized to access the service yet; check the
        // pairing mode timeout
        ErrorKind::Unauthorized => match pairing_mode_timeout {
            // retry every 10 seconds in the first 10 minutes since the first
            // "unauthorized" response
            timeout if t < (timeout - 600.0) => ConnectionRetry::Timeout(10.0),
            // retry every 30 seconds after the first 10 minutes since the
            // first "unauthorized" response
            timeout if t < timeout => ConnectionRetry::Timeout(30.0),
            // suspend the thread after the first 20 minutes since the client
            // thread start
            _ => ConnectionRetry::Suspend(SuspendReason::NotInPairingMode),
        },
        // suspend the thread if the version of the Arrow Protocol is not
        // supported by either side
        ErrorKind::UnsupportedProtocolVersion =>
            ConnectionRetry::Suspend(SuspendReason::UnsupportedProtocolVersion),
        // in all other cases
        _ => ConnectionRetry::Timeout(RETRY_TIMEOUT + last_attempt - t),
    }
}

/// Process a given connection retry object.
fn wait_for_retry(logger: &mut Logger, connection_retry: ConnectionRetry) {
    match connection_retry {
        ConnectionRetry::Timeout(t) if t > 0.5 => {
            log_info!(logger, "retrying in {:.3} seconds", t);

            thread::sleep(Duration::from_millis((t * 1000.0) as u64));
        },
        ConnectionRetry::Timeout(_) => (),
        ConnectionRetry::Suspend(reason) => {
            log_info!(logger, "{}", reason.to_string());
            log_info!(logger, "suspending the connection thread");

            loop {
                thread::sleep(Duration::from_millis(60000));
            }
        },
    }
}

/// Diagnose a given connection result and exit with exit code 0 if the
/// connection was successful or the server responded with UNAUTHORIZED,
/// otherwise exit with exit code 1.
fn diagnose_connection_result(
    connection_result: &Result<String, ArrowError>) -> ! {
    match connection_result {
        &Ok(_)        => process::exit(0),
        &Err(ref err) => match err.kind() {
            ErrorKind::Unauthorized => process::exit(0),
            _ => process::exit(1)
        }
    }
}

/// Arrow Client main function.
fn main() {
    let mut core = TokioCore::new()
        .expect("unable to create an event loop");

    let handle = core.handle();

    let config = result_or_usage(
        ApplicationConfig::create());

    let context = ApplicationContext::new(config);

    let mut logger = context.get_logger();

    log_info!(&mut logger,
        "application started (uuid: {}, mac: {})",
        context.get_arrow_uuid(), context.get_arrow_mac_address());

    // create command handler
    let (tx, rx) = cmd_handler::new(context.clone());

    let cmd_channel = tx.clone();

    // schedule periodic network scan
    let periodic_network_scan = context.get_timer()
        .create_periodic_task(
            Duration::from_secs(1),
            move || {
                tx.send(Command::PeriodicNetworkScan)
            }
        );

    handle.spawn(periodic_network_scan);

    // start the Arrow client thread
    thread::spawn(move || {
        arrow_thread(context, cmd_channel)
    });

    // run the command handler event loop
    core.run(rx)
        .unwrap_or_default();
}
<|endoftext|>


<commit_before>use clap::{App, Arg};
use failure::Error;
use human_panic::setup_panic;
use serde_json::Deserializer;
use serde_json::Value;
use std::fs;
use std::io;
use std::result;
use std::string::String;
use std::vec::Vec;

type Result<T> = result::Result<T, Error>;

fn main() {
    setup_panic!();

    match run() {
        Ok(_) => {}
        Err(e) => {
            eprintln!("error: {}", e);
            std::process::exit(1);
        }
    }
}

fn run() -> Result<()> {
    let matches = App::new(env!("CARGO_PKG_NAME"))
        .version(env!("CARGO_PKG_VERSION"))
        .about("Extract JSON elements from a stream via JSON Pointers")
        .arg(
            Arg::with_name("INPUT")
                .short("i")
                .long("input")
                .takes_value(true)
                .help("input file to use if not receiving on stdin"),
        ).arg(
            Arg::with_name("DELIMITER")
                .short("d")
                .long("delimiter")
                .takes_value(true)
                .help("delimiter between output values, default is tab"),
        ).arg(
            Arg::with_name("POINTER")
                .required(true)
                .multiple(true)
                .help("JSON Pointer expressions to match on input"),
        ).get_matches();

    let input = matches.value_of("INPUT").unwrap_or("-");
    let rdr: Box<io::Read> = match input {
        "-" => Box::new(io::stdin()),
        _ => Box::new(fs::File::open(input)?),
    };

    let delim = matches.value_of("DELIMITER").unwrap_or("\t");

    let mut pointers = Vec::new();
    // unwrap is safe as POINTER is required
    for p in matches.values_of("POINTER").unwrap() {
        pointers.push(p);
    }

    let iter = Deserializer::from_reader(rdr).into_iter::<Value>();
    for rv in iter {
        let v = rv?;
        let mut line = String::new();
        for p in &pointers {
            line.push_str(&render(v.pointer(p).unwrap_or(&Value::Null)));
            line.push_str(delim);
        }
        line.pop(); // remove the final tab
        println!("{}", line);
    }
    Ok(())
}

fn render(v: &Value) -> String {
    match v {
        &Value::Null => String::new(),
        &Value::Bool(ref b) => format!("{}", b),
        &Value::Number(ref b) => format!("{}", b),
        &Value::String(ref s) => format!("{}", s.replace("\n", "\\n").replace("\t", "\\t")),
        &Value::Array(_) => format!("{}", v),
        &Value::Object(_) => format!("{}", v),
    }
}
<commit_msg>cargo fix --edition-idioms<commit_after>use clap::{App, Arg};
use failure::Error;
use human_panic::setup_panic;
use serde_json::Deserializer;
use serde_json::Value;
use std::fs;
use std::io;
use std::result;
use std::string::String;
use std::vec::Vec;

type Result<T> = result::Result<T, Error>;

fn main() {
    setup_panic!();

    match run() {
        Ok(_) => {}
        Err(e) => {
            eprintln!("error: {}", e);
            std::process::exit(1);
        }
    }
}

fn run() -> Result<()> {
    let matches = App::new(env!("CARGO_PKG_NAME"))
        .version(env!("CARGO_PKG_VERSION"))
        .about("Extract JSON elements from a stream via JSON Pointers")
        .arg(
            Arg::with_name("INPUT")
                .short("i")
                .long("input")
                .takes_value(true)
                .help("input file to use if not receiving on stdin"),
        )
        .arg(
            Arg::with_name("DELIMITER")
                .short("d")
                .long("delimiter")
                .takes_value(true)
                .help("delimiter between output values, default is tab"),
        )
        .arg(
            Arg::with_name("POINTER")
                .required(true)
                .multiple(true)
                .help("JSON Pointer expressions to match on input"),
        )
        .get_matches();

    let input = matches.value_of("INPUT").unwrap_or("-");
    let rdr: Box<dyn io::Read> = match input {
        "-" => Box::new(io::stdin()),
        _ => Box::new(fs::File::open(input)?),
    };

    let delim = matches.value_of("DELIMITER").unwrap_or("\t");

    let mut pointers = Vec::new();
    // unwrap is safe as POINTER is required
    for p in matches.values_of("POINTER").unwrap() {
        pointers.push(p);
    }

    let iter = Deserializer::from_reader(rdr).into_iter::<Value>();
    for rv in iter {
        let v = rv?;
        let mut line = String::new();
        for p in &pointers {
            line.push_str(&render(v.pointer(p).unwrap_or(&Value::Null)));
            line.push_str(delim);
        }
        line.pop(); // remove the final tab
        println!("{}", line);
    }
    Ok(())
}

fn render(v: &Value) -> String {
    match v {
        &Value::Null => String::new(),
        &Value::Bool(ref b) => format!("{}", b),
        &Value::Number(ref b) => format!("{}", b),
        &Value::String(ref s) => format!("{}", s.replace("\n", "\\n").replace("\t", "\\t")),
        &Value::Array(_) => format!("{}", v),
        &Value::Object(_) => format!("{}", v),
    }
}
<|endoftext|>