<commit_before>//
//  SOS: the Stupid Operating System
//  by Hawk Weisman (hi@hawkweisman.me)
//
//  Copyright (c) 2015 Hawk Weisman
//  Released under the terms of the MIT license. See `LICENSE` in the root
//  directory of this repository for more information.
//
//! # SOS VGA
//! Code for interacting with the system's VGA buffer.
//!
//! # Features
//! This crate exposes the following feature flags:
//! + `system_term`: Use the VGA buffer as the system terminal. This provides
//!   implementations of the `print!` and `println!` macros.
//! + `log`: provides a `log!` macro for kernel log messages.
#![crate_name = "sos_vga"]
#![crate_type = "lib"]
#![feature( const_fn
          , slice_patterns
          , unique
          , question_mark )]
#![cfg_attr(feature = "system_term", feature(lang_items))]
#![cfg_attr(feature = "clippy", feature(plugin))]
#![cfg_attr(feature = "clippy", plugin(clippy))]
#![no_std]

#[cfg(feature = "system_term")]
extern crate spin;

use core::mem;
use core::fmt::{Write, Result};
use core::ptr::Unique;

#[cfg(feature = "system_term")]
use spin::Mutex;

/// The system's global VGA terminal
#[cfg(feature = "system_term")]
pub static CONSOLE: Mutex<Terminal>
    = Mutex::new(unsafe { Terminal::new(
         Palette::new(Color::LightGrey, Color::Black )
       , 0xB8000
    )});

// TODO: support varying VGA screen sizes?
pub const X_MAX: usize = 80;
pub const Y_MAX: usize = 25;

/// The type signature fot the actual VGA buffer
pub type Buffer = [[Char; X_MAX]; Y_MAX];

const ANSI_ESCAPE: &'static str = "\x1b";
const FG_MASK: u8 = 0b0000_1111;
const BG_MASK: u8 = 0b1111_0000;

/// VGA color codes
#[derive(Eq, PartialEq, Ord, PartialOrd, Copy, Clone)]
#[repr(u8)]
pub enum Color { Black = 0
               , Blue = 1
               , Green = 2
               , Cyan = 3
               , Red = 4
               , Magenta = 5
               , Brown = 6
               , LightGrey = 7
               , DarkGrey = 8
               , LightBlue = 9
               , LightGreen = 10
               , LightCyan = 11
               , LightRed = 12
               , LightMagenta = 13
               , Yellow = 14
               , White = 15
               }

#[derive(Eq, PartialEq, Ord, PartialOrd, Copy, Clone)]
pub struct Palette(u8);

impl Palette {
    /// Returns a `Palette` with the given foreground and background color.
    pub const fn new(fg: Color, bg: Color) -> Self {
        Palette( (bg as u8) << 4 | (fg as u8) )
    }

    /// Returns a new `Palette` with this palette's background color, and
    /// the specified foreground color.
    pub fn set_foreground(&self, fg: Color) -> Self {
        Palette( ( self.0 & BG_MASK) | (fg as u8 & FG_MASK) )
    }

    /// Returns a new `Palette` with this palette's foreground color, and
    /// the specified background color.
    pub fn set_background(&self, bg: Color) -> Self {
        Palette( ( (bg as u8) << 4 & BG_MASK) | (self.0 & FG_MASK) )
    }

    /// Returns this `Palette`'s foreground color.
    pub fn foreground(&self) -> Color {
        unsafe { mem::transmute(self.0 & FG_MASK) }
    }

    /// Returns this `Palette`'s background color.
    pub fn background(&self) -> Color {
        unsafe { mem::transmute((self.0 & BG_MASK) >> 4) }
    }

}
/// A colored VGA character.
#[derive(Copy, Clone)]
#[repr(C)]
pub struct Char { pub ascii: u8
                , pub colors: Palette
                }

pub struct Terminal { buffer: Unique<Buffer>
                    , x: usize
                    , y: usize
                    , colors: Palette
                    }
impl Terminal {

    /// Constructs a new `Terminal` for abuffer starting at the given address.
    ///
    /// # Arguments:
    /// + `colors`: the default color palette for the terminal
    /// + `buffer_start`: the address of the to the memory location where
    ///                   the terminal's VGA buffer begins
    ///
    /// # Unsafe due to:
    /// + Casting a raw address to an array
    pub const unsafe fn new( colors: Palette
                           , buffer_start: usize)
                           -> Terminal {
        Terminal { x: 0, y: 0
                 , colors: colors
                 , buffer: Unique::new(buffer_start as *mut _)
                 }
    }

    #[inline]
    fn buffer(&mut self) -> &mut  Buffer {
        unsafe { self.buffer.get_mut() }
    }

    /// Set the color palette used for writing subsequent characters.
    pub fn set_colors(&mut self, bg: Color, fg: Color) -> &mut Self {
        self.colors = Palette::new(bg,fg);
        self
    }

    /// Scrolls the terminal one row.
    fn scroll(&mut self) {
        // // construct an iterator over the whole buffer
        // let mut rows = self.buffer()
        //                    .iter_mut();
        //
        // // the current row in the buffer
        // let mut current = rows.next()
        //                    .unwrap();
        //
        // while let Some(next) = rows.next() {
        //     // while there are rows remaining in the iterator, swap the
        //     // next row with the current one (moving it back by one)
        //     mem::swap(current, next);
        //     // and advance our pointer to the current row.
        //     current = next;
        // }
        let buffer: &mut _ = self.buffer();
        for row in 1..Y_MAX {
            buffer[row - 1] = buffer[row];
        }
        // empty the last line in the buffer
        unsafe { buffer[Y_MAX - 1] = mem::zeroed() }
    }

    /// Clear the terminal
    pub fn clear(&mut self) -> &mut Self {
        // to clear the terminal, we just zero out the whole buffer.
        unsafe { *(self.buffer()) = mem::zeroed(); }
        self.x = 0;
        self.y = 0;
        self
    }

    /// Write the given byte to the terminal, and advance the cursor position.
    pub fn write_byte(&mut self, byte: u8) -> &mut Self {
        if byte == b'\n' {
            // if the byte is a newline, we just advance to the next line
            // and reset the column position.
            self.x = 0;
            self.y += 1;
        } else {
            // otherwise, it's a regular character, so we just set the
            // byte at the current position in the buffer to that
            // character (with the current color palette)
            //
            self.buffer()[self.y][self.x]
                = Char { ascii: byte
                       , colors: self.colors };
            // and advance our column position by one
            self.x += 1;

            if self.x >= X_MAX {
                // if we've reached the end of the line, advance to the next
                self.x = 0;
                self.y += 1;
            }
        }
        if self.y >= Y_MAX {
            // if we've reached the bottom of the terminal, scroll.
            self.scroll();
            self.y -= 1;
        }
        self
    }

    fn handle_ansi_escape(&self, escape_code: &str) -> Result {
        match escape_code.as_bytes() {
            // `\x1b[3Nm` sets the foreground color to N.
            &[0x1b, b'[', b'3', n, b'm'] => {
                unsafe { self.colors
                             .set_foreground(mem::transmute(n - 48)); }
                Ok(())
            }
            // `\x1b[4Nm` sets the background color to N
          , &[0x1b, b'[', b'4', n, b'm'] => {
                unsafe { self.colors
                             .set_background(mem::transmute(n - 48)); }
                Ok(())
            }
          , _ => unimplemented!()
        }
        // let escape_seq: &str = bytes.take_while(|b| b != b'm')
        //                       .collect::<&str>();
        // match escape_seq {
        //     [b'3', n] => unsafe {
        //         self.colors.set_foreground(mem::transmute(n - 48))
        //     }
        // }
        // while let Some(byte) = bytes.next() {
        //     match *byte {
        //         // we've recieved an ANSI escape sequence.
        //         // this basically enters a mediocre FSM for matching ANSI
        //         // control codes.
        //         0x1b => match *next_ansi_byte!(bytes) {
        //             // handle multi-char ANSI escapes
        //             b'[' => match *next_ansi_byte!(bytes) {
        //                 // foreground color code
        //                 fg @ 30 ... 37 => {
        //                     if !(*next_ansi_byte!(bytes) == b'm') {
        //                         unsafe {
        //                             let color: vga::Color
        //                                 = mem::transmute(fg - 30);
        //                             self.colors
        //                                 .set_foreground(color);
        //                         }
        //
        //                     }
        //                 }
        //                 // background color code
        //               , 40 ... 47 => {
        //
        //                 }
        //               , _ => unimplemented!()
        //             }
        //           , _    => unimplemented!()
        //         }
        //         // otherwise, treat the byte as a normal ASCII char
        //       , b => { self.write_byte(b); }
        //     }
        // }

    }

}

struct AnsiEscapeIter<'a> { curr_slice: &'a str
                          , in_escape: bool
                          }

impl<'a> AnsiEscapeIter<'a> {

    pub fn new(s: &'a str) -> Self {
        AnsiEscapeIter { curr_slice: s
                       , in_escape: false
                       }
    }
}

impl<'a> Iterator for AnsiEscapeIter<'a> {
    type Item = &'a str;

    fn next(&mut self) -> Option<Self::Item> {
        if self.curr_slice.len() == 0 {
            // if the remaining string is empty, we just return `None`
            None
        } else {
            // otherwise, find the next index to chunk on.
            let maybe_idx
                = if self.in_escape {
                     // if we're in an escape code, we split the chunk at the
                     // index of the next 'm' character, adding 1 so that the
                     // 'm' is in the escape code chunk.
                    self.curr_slice.find('m')
                        .map(|idx| idx + 1)
                } else {
                    // otherwise, split at the next ANSI escape sequence
                    self.curr_slice.find(ANSI_ESCAPE)
                };

            // if we found another index to chunk on, map over that index;
            // otherwise, we just yield the rest of the string
            maybe_idx.map_or(
                Some(self.curr_slice) // remainder (if no index to chunk on)
              , |idx| { // otherwise, chunk along that index...
                    let (chunk, next_slice) = self.curr_slice
                                                  .split_at(idx);
                    self.curr_slice = next_slice; // update current chunk
                    Some(chunk)                   // return the chunk
                })
        }

    }
}

impl Write for Terminal {

    fn write_str(&mut self, s: &str) -> Result {

        if s.contains(ANSI_ESCAPE) {
            // if the segment contains an ANSI escape, construct an iterator
            // over each chunk containing either an escape sequence or text
            for segment in AnsiEscapeIter::new(s) {
                if segment.starts_with(ANSI_ESCAPE) {
                    // if the current segment is an ANSI escape code,
                    // try to handle the escape and fail if it is malformed
                    self.handle_ansi_escape(segment)?
                } else {
                    // otherwise, just write each chunk in the string.
                    for byte in segment.as_bytes() {
                        self.write_byte(*byte);
                    }
                }
            }
        } else {
            // otherwise, if there are no ANSI escape codes,
            // we can just write each byte in the string.
            for byte in s.as_bytes() {
                self.write_byte(*byte);
            }
        }
        Ok(())
    }

}

/// Macro for printing to the standard output.
///
/// Equivalent to the `println!` macro except that a newline is not printed at
/// the end of the message.
///
/// # Panics
///
/// Panics if writing to the VGA console fails.
///
#[cfg(feature = "system_term")]
#[macro_export]
macro_rules! println {
    ($fmt:expr) => (print!(concat!($fmt, "\n")));
    ($fmt:expr, $($arg:tt)*) => (print!(concat!($fmt, "\n"), $($arg)*));
}

/// Macro for printing to the standard output.
///
/// Equivalent to the `println!` macro except that a newline is not printed at
/// the end of the message.
///
/// # Panics
///
/// Panics if writing to the VGA console fails.
#[cfg(feature = "system_term")]
#[macro_export]
macro_rules! print {
    ($($arg:tt)*) => ({
            use core::fmt::Write;
            $crate::CONSOLE.lock()
                           .write_fmt(format_args!($($arg)*))
                           .unwrap();
    });
}

#[cfg(feature = "log")]
#[macro_export]
macro_rules! log {
    (level: $level:expr, $fmt:expr, $($arg:tt)*) => ({
        println!( "[{} {}:{}] {}", $level, file!(), line!()
                , format_args!($fmt, $($arg)*));
    });
    (level: $level:expr, $fmt:expr) => ({
        println!("[{} {}:{}] {}", $level, file!(), line!(), $fmt);
    });
}
<commit_msg>[vga] rewrite VGA crate to use volatile writes<commit_after>//
//  SOS: the Stupid Operating System
//  by Hawk Weisman (hi@hawkweisman.me)
//
//  Copyright (c) 2015 Hawk Weisman
//  Released under the terms of the MIT license. See `LICENSE` in the root
//  directory of this repository for more information.
//
//! # SOS VGA
//! Code for interacting with the system's VGA buffer.
//!
//! # Features
//! This crate exposes the following feature flags:
//! + `system_term`: Use the VGA buffer as the system terminal. This provides
//!   implementations of the `print!` and `println!` macros.
//! + `log`: provides a `log!` macro for kernel log messages.
#![crate_name = "sos_vga"]
#![crate_type = "lib"]
#![feature( const_fn
          , slice_patterns
          , unique
          , question_mark )]
#![cfg_attr(feature = "system_term", feature(lang_items))]
#![cfg_attr(feature = "clippy", feature(plugin))]
#![cfg_attr(feature = "clippy", plugin(clippy))]
#![no_std]

#[cfg(feature = "system_term")]
extern crate spin;

use core::{mem, ptr};
use core::fmt::{Write, Result};
// use core::ptr::Unique;

#[cfg(feature = "system_term")]
use spin::Mutex;

/// The system's global VGA terminal
#[cfg(feature = "system_term")]
pub static CONSOLE: Mutex<Terminal>
    = Mutex::new(unsafe { Terminal::new(
         Palette::new(Color::LightGrey, Color::Black )
       , 0xB8000
    )});

// TODO: support varying VGA screen sizes?
pub const X_MAX: usize = 80;
pub const Y_MAX: usize = 25;

/// The type signature fot the actual VGA buffer
pub type Buffer = [[Char; X_MAX]; Y_MAX];

const ANSI_ESCAPE: &'static str = "\x1b";
const FG_MASK: u8 = 0b0000_1111;
const BG_MASK: u8 = 0b1111_0000;

/// VGA color codes
#[derive(Eq, PartialEq, Ord, PartialOrd, Copy, Clone)]
#[repr(u8)]
pub enum Color { Black = 0
               , Blue = 1
               , Green = 2
               , Cyan = 3
               , Red = 4
               , Magenta = 5
               , Brown = 6
               , LightGrey = 7
               , DarkGrey = 8
               , LightBlue = 9
               , LightGreen = 10
               , LightCyan = 11
               , LightRed = 12
               , LightMagenta = 13
               , Yellow = 14
               , White = 15
               }

#[derive(Eq, PartialEq, Ord, PartialOrd, Copy, Clone)]
pub struct Palette(u8);

impl Palette {
    /// Returns a `Palette` with the given foreground and background color.
    pub const fn new(fg: Color, bg: Color) -> Self {
        Palette( (bg as u8) << 4 | (fg as u8) )
    }

    /// Returns a new `Palette` with this palette's background color, and
    /// the specified foreground color.
    pub fn set_foreground(&self, fg: Color) -> Self {
        Palette( ( self.0 & BG_MASK) | (fg as u8 & FG_MASK) )
    }

    /// Returns a new `Palette` with this palette's foreground color, and
    /// the specified background color.
    pub fn set_background(&self, bg: Color) -> Self {
        Palette( ( (bg as u8) << 4 & BG_MASK) | (self.0 & FG_MASK) )
    }

    /// Returns this `Palette`'s foreground color.
    pub fn foreground(&self) -> Color {
        unsafe { mem::transmute(self.0 & FG_MASK) }
    }

    /// Returns this `Palette`'s background color.
    pub fn background(&self) -> Color {
        unsafe { mem::transmute((self.0 & BG_MASK) >> 4) }
    }

}
/// A colored VGA character.
#[derive(Copy, Clone)]
#[repr(C)]
pub struct Char { pub ascii: u8
                , pub colors: Palette
                }

impl Char {
    pub const fn empty() -> Self {
        Char { ascii: 0, colors: Palette(0) }
    }
}

pub struct Terminal { buffer: ptr::Unique<Buffer>
                    , x: usize
                    , y: usize
                    , colors: Palette
                    }
impl Terminal {

    /// Constructs a new `Terminal` for abuffer starting at the given address.
    ///
    /// # Arguments:
    /// + `colors`: the default color palette for the terminal
    /// + `buffer_start`: the address of the to the memory location where
    ///                   the terminal's VGA buffer begins
    ///
    /// # Unsafe due to:
    /// + Casting a raw address to an array
    pub const unsafe fn new( colors: Palette
                           , buffer_start: usize)
                           -> Terminal {
        Terminal { x: 0, y: 0
                 , colors: colors
                 , buffer: ptr::Unique::new(buffer_start as *mut _)
                 }
    }

    #[inline]
    fn buffer(&mut self) -> &mut  Buffer {
        unsafe { self.buffer.get_mut() }
    }

    /// Set the color palette used for writing subsequent characters.
    pub fn set_colors(&mut self, bg: Color, fg: Color) -> &mut Self {
        self.colors = Palette::new(bg,fg);
        self
    }

    /// Scrolls the terminal one row.
    fn scroll(&mut self) {
        // // construct an iterator over the whole buffer
        // let mut rows = self.buffer()
        //                    .iter_mut();
        //
        // // the current row in the buffer
        // let mut current = rows.next()
        //                    .unwrap();
        //
        // while let Some(next) = rows.next() {
        //     // while there are rows remaining in the iterator, swap the
        //     // next row with the current one (moving it back by one)
        //     mem::swap(current, next);
        //     // and advance our pointer to the current row.
        //     current = next;
        // }
        let buffer: &mut _ = self.buffer();
        for row in 1..Y_MAX {
            for col in 0 .. X_MAX {
                unsafe {
                    let last = ptr::read_volatile(&buffer[row][col]);
                    ptr::write_volatile( &mut buffer[row - 1][col], last);
                }
            }
        }
        // empty the last line in the buffer
        unsafe {
            for col in 0..X_MAX {
                ptr::write_volatile(&mut buffer[Y_MAX - 1][col], Char::empty());
            }
        }
    }

    /// Clear the terminal
    pub fn clear(&mut self) -> &mut Self {
        // to clear the terminal, we just zero out the whole buffer.
        {
            let buffer: &mut _ =self.buffer();
            for row in 0..Y_MAX {
                for col in 0 .. X_MAX {
                    unsafe {
                        ptr::write_volatile(
                            &mut buffer[row][col], Char::empty()
                        );
                    }
                }
            }
        }

        self.x = 0;
        self.y = 0;
        self
    }

    /// Write the given byte to the terminal, and advance the cursor position.
    pub fn write_byte(&mut self, byte: u8) -> &mut Self {
        if byte == b'\n' {
            // if the byte is a newline, we just advance to the next line
            // and reset the column position.
            self.x = 0;
            self.y += 1;
        } else {
            // otherwise, it's a regular character, so we just set the
            // byte at the current position in the buffer to that
            // character (with the current color palette)
            //
            let y = self.y;
            let x = self.x;
            unsafe {
                ptr::write_volatile( &mut (self.buffer()[y][x])
                                   , Char { ascii: byte, colors: self.colors }
                                   );
            }
            // and advance our column position by one
            self.x += 1;

            if self.x >= X_MAX {
                // if we've reached the end of the line, advance to the next
                self.x = 0;
                self.y += 1;
            }
        }
        if self.y >= Y_MAX {
            // if we've reached the bottom of the terminal, scroll.
            self.scroll();
            self.y -= 1;
        }
        self
    }

    fn handle_ansi_escape(&self, escape_code: &str) -> Result {
        match escape_code.as_bytes() {
            // `\x1b[3Nm` sets the foreground color to N.
            &[0x1b, b'[', b'3', n, b'm'] => {
                unsafe { self.colors
                             .set_foreground(mem::transmute(n - 48)); }
                Ok(())
            }
            // `\x1b[4Nm` sets the background color to N
          , &[0x1b, b'[', b'4', n, b'm'] => {
                unsafe { self.colors
                             .set_background(mem::transmute(n - 48)); }
                Ok(())
            }
          , _ => unimplemented!()
        }
        // let escape_seq: &str = bytes.take_while(|b| b != b'm')
        //                       .collect::<&str>();
        // match escape_seq {
        //     [b'3', n] => unsafe {
        //         self.colors.set_foreground(mem::transmute(n - 48))
        //     }
        // }
        // while let Some(byte) = bytes.next() {
        //     match *byte {
        //         // we've recieved an ANSI escape sequence.
        //         // this basically enters a mediocre FSM for matching ANSI
        //         // control codes.
        //         0x1b => match *next_ansi_byte!(bytes) {
        //             // handle multi-char ANSI escapes
        //             b'[' => match *next_ansi_byte!(bytes) {
        //                 // foreground color code
        //                 fg @ 30 ... 37 => {
        //                     if !(*next_ansi_byte!(bytes) == b'm') {
        //                         unsafe {
        //                             let color: vga::Color
        //                                 = mem::transmute(fg - 30);
        //                             self.colors
        //                                 .set_foreground(color);
        //                         }
        //
        //                     }
        //                 }
        //                 // background color code
        //               , 40 ... 47 => {
        //
        //                 }
        //               , _ => unimplemented!()
        //             }
        //           , _    => unimplemented!()
        //         }
        //         // otherwise, treat the byte as a normal ASCII char
        //       , b => { self.write_byte(b); }
        //     }
        // }

    }

}

struct AnsiEscapeIter<'a> { curr_slice: &'a str
                          , in_escape: bool
                          }

impl<'a> AnsiEscapeIter<'a> {

    pub fn new(s: &'a str) -> Self {
        AnsiEscapeIter { curr_slice: s
                       , in_escape: false
                       }
    }
}

impl<'a> Iterator for AnsiEscapeIter<'a> {
    type Item = &'a str;

    fn next(&mut self) -> Option<Self::Item> {
        if self.curr_slice.len() == 0 {
            // if the remaining string is empty, we just return `None`
            None
        } else {
            // otherwise, find the next index to chunk on.
            let maybe_idx
                = if self.in_escape {
                     // if we're in an escape code, we split the chunk at the
                     // index of the next 'm' character, adding 1 so that the
                     // 'm' is in the escape code chunk.
                    self.curr_slice.find('m')
                        .map(|idx| idx + 1)
                } else {
                    // otherwise, split at the next ANSI escape sequence
                    self.curr_slice.find(ANSI_ESCAPE)
                };

            // if we found another index to chunk on, map over that index;
            // otherwise, we just yield the rest of the string
            maybe_idx.map_or(
                Some(self.curr_slice) // remainder (if no index to chunk on)
              , |idx| { // otherwise, chunk along that index...
                    let (chunk, next_slice) = self.curr_slice
                                                  .split_at(idx);
                    self.curr_slice = next_slice; // update current chunk
                    Some(chunk)                   // return the chunk
                })
        }

    }
}

impl Write for Terminal {

    fn write_str(&mut self, s: &str) -> Result {

        if s.contains(ANSI_ESCAPE) {
            // if the segment contains an ANSI escape, construct an iterator
            // over each chunk containing either an escape sequence or text
            for segment in AnsiEscapeIter::new(s) {
                if segment.starts_with(ANSI_ESCAPE) {
                    // if the current segment is an ANSI escape code,
                    // try to handle the escape and fail if it is malformed
                    self.handle_ansi_escape(segment)?
                } else {
                    // otherwise, just write each chunk in the string.
                    for byte in segment.as_bytes() {
                        self.write_byte(*byte);
                    }
                }
            }
        } else {
            // otherwise, if there are no ANSI escape codes,
            // we can just write each byte in the string.
            for byte in s.as_bytes() {
                self.write_byte(*byte);
            }
        }
        Ok(())
    }

}

/// Macro for printing to the standard output.
///
/// Equivalent to the `println!` macro except that a newline is not printed at
/// the end of the message.
///
/// # Panics
///
/// Panics if writing to the VGA console fails.
///
#[cfg(feature = "system_term")]
#[macro_export]
macro_rules! println {
    ($fmt:expr) => (print!(concat!($fmt, "\n")));
    ($fmt:expr, $($arg:tt)*) => (print!(concat!($fmt, "\n"), $($arg)*));
}

/// Macro for printing to the standard output.
///
/// Equivalent to the `println!` macro except that a newline is not printed at
/// the end of the message.
///
/// # Panics
///
/// Panics if writing to the VGA console fails.
#[cfg(feature = "system_term")]
#[macro_export]
macro_rules! print {
    ($($arg:tt)*) => ({
            use core::fmt::Write;
            $crate::CONSOLE.lock()
                           .write_fmt(format_args!($($arg)*))
                           .unwrap();
    });
}

#[cfg(feature = "log")]
#[macro_export]
macro_rules! log {
    (level: $level:expr, $fmt:expr, $($arg:tt)*) => ({
        println!( "[{} {}:{}] {}", $level, file!(), line!()
                , format_args!($fmt, $($arg)*));
    });
    (level: $level:expr, $fmt:expr) => ({
        println!("[{} {}:{}] {}", $level, file!(), line!(), $fmt);
    });
}
<|endoftext|>


<commit_before>use std::str;
use std::cell::RefCell;
use std::time::Instant;
use std::collections::BTreeMap;
use std::net::SocketAddr;
use mio::net::UdpSocket;
use std::io::{self,Write};
use sozu_command::proxy::{FilteredData,MetricsData};

mod network_drain;
mod local_drain;
mod writer;

use self::network_drain::NetworkDrain;
use self::local_drain::LocalDrain;

thread_local! {
  pub static METRICS: RefCell<Aggregator> = RefCell::new(Aggregator::new(String::from("sozu")));
}

#[derive(Debug,Clone,PartialEq)]
pub enum MetricData {
  Gauge(usize),
  GaugeAdd(i64),
  Count(i64),
  Time(usize),
}

impl MetricData {
  fn is_time(&self) -> bool {
    match self {
      &MetricData::Time(_) => true,
      _ => false,
    }
  }

  fn update(&mut self, key: &'static str, m: MetricData) -> bool {
    match (self, m) {
      (&mut MetricData::Gauge(ref mut v1), MetricData::Gauge(v2)) => {
        let changed = *v1 != v2;
        *v1 = v2;
        changed
      },
      (&mut MetricData::Gauge(ref mut v1), MetricData::GaugeAdd(v2)) => {
        debug_assert!(*v1 as i64 + v2 >= 0, "metric {} underflow: previous value: {}, adding: {}", key, v1, v2);
        let changed = v2 != 0;
        let res = *v1 as i64 + v2;
        *v1 = if res >= 0 {
          res as usize
        } else {
          error!("metric {} underflow: previous value: {}, adding: {}", key, v1, v2);
          0
        };

        changed
      },
      (&mut MetricData::Count(ref mut v1), MetricData::Count(v2)) => {
        let changed = v2 != 0;
        *v1 += v2;
        changed
      },
      (s,m) => panic!("tried to update metric {} of value {:?} with an incompatible metric: {:?}", key, s, m)
    }
  }
}

#[derive(Debug,Clone)]
pub struct StoredMetricData {
  last_sent: Instant,
  updated:   bool,
  data:      MetricData,
}

impl StoredMetricData {
  pub fn new(last_sent: Instant, data: MetricData) -> StoredMetricData {
    StoredMetricData {
      last_sent,
      updated: true,
      data: if let MetricData::GaugeAdd(v) = data {
        if v >= 0 {
          MetricData::Gauge(v as usize)
        } else {
          MetricData::Gauge(0)
        }
      } else { data }
    }
  }

  pub fn update(&mut self, key: &'static str, m: MetricData) {
    let updated = self.data.update(key, m);
    if ! self.updated {
      self.updated = updated;
    }
  }
}

pub fn setup<O: Into<String>>(metrics_host: &SocketAddr, origin: O, use_tagged_metrics: bool, prefix: Option<String>) {
  let metrics_socket = udp_bind();

  debug!("setting up metrics: local address = {:#?}", metrics_socket.local_addr());

  METRICS.with(|metrics| {
    if let Some(p) = prefix {
      (*metrics.borrow_mut()).set_up_prefix(p);
    }
    (*metrics.borrow_mut()).set_up_remote(metrics_socket, metrics_host.clone());
    (*metrics.borrow_mut()).set_up_origin(origin.into());
    (*metrics.borrow_mut()).set_up_tagged_metrics(use_tagged_metrics);
  });
}

pub trait Subscriber {
  fn receive_metric(&mut self, label: &'static str, app_id: Option<&str>, backend_id: Option<&str>, metric: MetricData);
}

pub struct Aggregator {
  prefix:  String,
  network: Option<NetworkDrain>,
  local:   LocalDrain,
}

impl Aggregator {
  pub fn new(prefix: String) -> Aggregator {
    Aggregator {
      prefix: prefix.clone(),
      network: None,
      local: LocalDrain::new(prefix),
    }
  }

  pub fn set_up_prefix(&mut self, prefix: String) {
    self.prefix = prefix;
  }

  pub fn set_up_remote(&mut self, socket: UdpSocket, addr: SocketAddr) {
    self.network = Some(NetworkDrain::new(self.prefix.clone(), socket, addr));
  }

  pub fn set_up_origin(&mut self, origin: String) {
    self.network.as_mut().map(|n| n.origin = origin);
  }

  pub fn set_up_tagged_metrics(&mut self, tagged: bool) {
    self.network.as_mut().map(|n| n.use_tagged_metrics = tagged);
  }

  pub fn socket(&self) -> Option<&UdpSocket> {
    self.network.as_ref().map(|n| &n.remote.get_ref().socket)
  }

  pub fn count_add(&mut self, key: &'static str, count_value: i64) {
    self.receive_metric(key, None, None, MetricData::Count(count_value));
  }

  pub fn set_gauge(&mut self, key: &'static str, gauge_value: usize) {
    self.receive_metric(key, None, None, MetricData::Gauge(gauge_value));
  }

  pub fn gauge_add(&mut self, key: &'static str, gauge_value: i64) {
    self.receive_metric(key, None, None, MetricData::GaugeAdd(gauge_value));
  }

  pub fn writable(&mut self) {
    if let Some(ref mut net) = self.network.as_mut() {
      net.writable();
    }
  }

  pub fn send_data(&mut self) {
    if let Some(ref mut net) = self.network.as_mut() {
      net.send_data();
    }
  }

  pub fn dump_metrics_data(&mut self) -> MetricsData {
    self.local.dump_metrics_data()
  }

  pub fn dump_process_data(&mut self) -> BTreeMap<String, FilteredData> {
    self.local.dump_process_data()
  }

  pub fn clear_local(&mut self) {
    self.local.clear();
  }
}

impl Subscriber for Aggregator {
  fn receive_metric(&mut self, label: &'static str, app_id: Option<&str>, backend_id: Option<&str>, metric: MetricData) {
    if let Some(ref mut net) = self.network.as_mut() {
      net.receive_metric(label, app_id, backend_id, metric.clone());
    }
    self.local.receive_metric(label, app_id, backend_id, metric.clone());
  }
}

#[derive(Debug,Clone,PartialEq)]
pub struct MetricLine {
  label:      &'static str,
  app_id:     Option<String>,
  backend_id: Option<String>,
  /// in milliseconds
  duration:   usize,
}

pub struct MetricSocket {
  pub addr:   SocketAddr,
  pub socket: UdpSocket,
}

impl Write for MetricSocket {
  fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
    self.socket.send_to(buf, &self.addr)
  }

  fn flush(&mut self) -> io::Result<()> {
    Ok(())
  }
}

pub fn udp_bind() -> UdpSocket {
  UdpSocket::bind(&("0.0.0.0:0".parse().unwrap())).expect("could not parse address")
}

#[macro_export]
macro_rules! count (
  ($key:expr, $value: expr) => ({
    let v = $value;
    $crate::metrics::METRICS.with(|metrics| {
      (*metrics.borrow_mut()).count_add($key, v);
    });
  })
);

#[macro_export]
macro_rules! incr (
  ($key:expr) => (count!($key, 1););
  ($key:expr, $app_id:expr, $backend_id:expr) => {
    use $crate::metrics::Subscriber;

    $crate::metrics::METRICS.with(|metrics| {
      (*metrics.borrow_mut()).receive_metric($key, $app_id, $backend_id, $crate::metrics::MetricData::Count(1));
    });
  }
);

#[macro_export]
macro_rules! decr (
  ($key:expr) => (count!($key, -1);)
);

#[macro_export]
macro_rules! gauge (
  ($key:expr, $value: expr) => ({
    let v = $value;
    $crate::metrics::METRICS.with(|metrics| {
      (*metrics.borrow_mut()).set_gauge($key, v);
    });
  })
);

#[macro_export]
macro_rules! gauge_add (
  ($key:expr, $value: expr) => ({
    let v = $value;
    $crate::metrics::METRICS.with(|metrics| {
      (*metrics.borrow_mut()).gauge_add($key, v);
    });
  });
  ($key:expr, $value:expr, $app_id:expr, $backend_id:expr) => {
    use $crate::metrics::Subscriber;
    let v = $value;

    $crate::metrics::METRICS.with(|metrics| {
      (*metrics.borrow_mut()).receive_metric($key, $app_id, $backend_id, $crate::metrics::MetricData::GaugeAdd(v));
    });
  }
);

#[macro_export]
macro_rules! time (
  ($key:expr, $value: expr) => {
    use $crate::metrics::{MetricData,Subscriber};
    let v = $value;
    $crate::metrics::METRICS.with(|metrics| {
      let ref mut m = *metrics.borrow_mut();

      m.receive_metric($key, None, None, MetricData::Time(v as usize));
    });
  };
  ($key:expr, $app_id:expr, $value: expr) => ({
    use $crate::metrics::{MetricData,Subscriber};
    let v = $value;
    $crate::metrics::METRICS.with(|metrics| {
      let ref mut m = *metrics.borrow_mut();
      let app: &str = $app_id;

      m.receive_metric($key, Some(app), None, MetricData::Time(v as usize));
    });
  })
);

#[macro_export]
macro_rules! record_backend_metrics (
  ($app_id:expr, $backend_id:expr, $response_time: expr, $backend_connection_time: expr, $bin: expr, $bout: expr) => {
    use $crate::metrics::{MetricData,Subscriber};
    $crate::metrics::METRICS.with(|metrics| {
      let ref mut m = *metrics.borrow_mut();
      let app_id: &str = $app_id.as_str();
      let backend_id: &str = $backend_id;

      m.receive_metric("bytes_in", Some(app_id), Some(backend_id), MetricData::Count($bin as i64));
      m.receive_metric("bytes_out", Some(app_id), Some(backend_id), MetricData::Count($bout as i64));
      m.receive_metric("response_time", Some(app_id), Some(backend_id), MetricData::Time($response_time as usize));
      if let Some(t) = $backend_connection_time {
        m.receive_metric("connection_time", Some(app_id), Some(backend_id), MetricData::Time(t.whole_milliseconds() as usize));
      }
    });
  }
);

<commit_msg>fix time macro expansion<commit_after>use std::str;
use std::cell::RefCell;
use std::time::Instant;
use std::collections::BTreeMap;
use std::net::SocketAddr;
use mio::net::UdpSocket;
use std::io::{self,Write};
use sozu_command::proxy::{FilteredData,MetricsData};

mod network_drain;
mod local_drain;
mod writer;

use self::network_drain::NetworkDrain;
use self::local_drain::LocalDrain;

thread_local! {
  pub static METRICS: RefCell<Aggregator> = RefCell::new(Aggregator::new(String::from("sozu")));
}

#[derive(Debug,Clone,PartialEq)]
pub enum MetricData {
  Gauge(usize),
  GaugeAdd(i64),
  Count(i64),
  Time(usize),
}

impl MetricData {
  fn is_time(&self) -> bool {
    match self {
      &MetricData::Time(_) => true,
      _ => false,
    }
  }

  fn update(&mut self, key: &'static str, m: MetricData) -> bool {
    match (self, m) {
      (&mut MetricData::Gauge(ref mut v1), MetricData::Gauge(v2)) => {
        let changed = *v1 != v2;
        *v1 = v2;
        changed
      },
      (&mut MetricData::Gauge(ref mut v1), MetricData::GaugeAdd(v2)) => {
        debug_assert!(*v1 as i64 + v2 >= 0, "metric {} underflow: previous value: {}, adding: {}", key, v1, v2);
        let changed = v2 != 0;
        let res = *v1 as i64 + v2;
        *v1 = if res >= 0 {
          res as usize
        } else {
          error!("metric {} underflow: previous value: {}, adding: {}", key, v1, v2);
          0
        };

        changed
      },
      (&mut MetricData::Count(ref mut v1), MetricData::Count(v2)) => {
        let changed = v2 != 0;
        *v1 += v2;
        changed
      },
      (s,m) => panic!("tried to update metric {} of value {:?} with an incompatible metric: {:?}", key, s, m)
    }
  }
}

#[derive(Debug,Clone)]
pub struct StoredMetricData {
  last_sent: Instant,
  updated:   bool,
  data:      MetricData,
}

impl StoredMetricData {
  pub fn new(last_sent: Instant, data: MetricData) -> StoredMetricData {
    StoredMetricData {
      last_sent,
      updated: true,
      data: if let MetricData::GaugeAdd(v) = data {
        if v >= 0 {
          MetricData::Gauge(v as usize)
        } else {
          MetricData::Gauge(0)
        }
      } else { data }
    }
  }

  pub fn update(&mut self, key: &'static str, m: MetricData) {
    let updated = self.data.update(key, m);
    if ! self.updated {
      self.updated = updated;
    }
  }
}

pub fn setup<O: Into<String>>(metrics_host: &SocketAddr, origin: O, use_tagged_metrics: bool, prefix: Option<String>) {
  let metrics_socket = udp_bind();

  debug!("setting up metrics: local address = {:#?}", metrics_socket.local_addr());

  METRICS.with(|metrics| {
    if let Some(p) = prefix {
      (*metrics.borrow_mut()).set_up_prefix(p);
    }
    (*metrics.borrow_mut()).set_up_remote(metrics_socket, metrics_host.clone());
    (*metrics.borrow_mut()).set_up_origin(origin.into());
    (*metrics.borrow_mut()).set_up_tagged_metrics(use_tagged_metrics);
  });
}

pub trait Subscriber {
  fn receive_metric(&mut self, label: &'static str, app_id: Option<&str>, backend_id: Option<&str>, metric: MetricData);
}

pub struct Aggregator {
  prefix:  String,
  network: Option<NetworkDrain>,
  local:   LocalDrain,
}

impl Aggregator {
  pub fn new(prefix: String) -> Aggregator {
    Aggregator {
      prefix: prefix.clone(),
      network: None,
      local: LocalDrain::new(prefix),
    }
  }

  pub fn set_up_prefix(&mut self, prefix: String) {
    self.prefix = prefix;
  }

  pub fn set_up_remote(&mut self, socket: UdpSocket, addr: SocketAddr) {
    self.network = Some(NetworkDrain::new(self.prefix.clone(), socket, addr));
  }

  pub fn set_up_origin(&mut self, origin: String) {
    self.network.as_mut().map(|n| n.origin = origin);
  }

  pub fn set_up_tagged_metrics(&mut self, tagged: bool) {
    self.network.as_mut().map(|n| n.use_tagged_metrics = tagged);
  }

  pub fn socket(&self) -> Option<&UdpSocket> {
    self.network.as_ref().map(|n| &n.remote.get_ref().socket)
  }

  pub fn count_add(&mut self, key: &'static str, count_value: i64) {
    self.receive_metric(key, None, None, MetricData::Count(count_value));
  }

  pub fn set_gauge(&mut self, key: &'static str, gauge_value: usize) {
    self.receive_metric(key, None, None, MetricData::Gauge(gauge_value));
  }

  pub fn gauge_add(&mut self, key: &'static str, gauge_value: i64) {
    self.receive_metric(key, None, None, MetricData::GaugeAdd(gauge_value));
  }

  pub fn writable(&mut self) {
    if let Some(ref mut net) = self.network.as_mut() {
      net.writable();
    }
  }

  pub fn send_data(&mut self) {
    if let Some(ref mut net) = self.network.as_mut() {
      net.send_data();
    }
  }

  pub fn dump_metrics_data(&mut self) -> MetricsData {
    self.local.dump_metrics_data()
  }

  pub fn dump_process_data(&mut self) -> BTreeMap<String, FilteredData> {
    self.local.dump_process_data()
  }

  pub fn clear_local(&mut self) {
    self.local.clear();
  }
}

impl Subscriber for Aggregator {
  fn receive_metric(&mut self, label: &'static str, app_id: Option<&str>, backend_id: Option<&str>, metric: MetricData) {
    if let Some(ref mut net) = self.network.as_mut() {
      net.receive_metric(label, app_id, backend_id, metric.clone());
    }
    self.local.receive_metric(label, app_id, backend_id, metric.clone());
  }
}

#[derive(Debug,Clone,PartialEq)]
pub struct MetricLine {
  label:      &'static str,
  app_id:     Option<String>,
  backend_id: Option<String>,
  /// in milliseconds
  duration:   usize,
}

pub struct MetricSocket {
  pub addr:   SocketAddr,
  pub socket: UdpSocket,
}

impl Write for MetricSocket {
  fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
    self.socket.send_to(buf, &self.addr)
  }

  fn flush(&mut self) -> io::Result<()> {
    Ok(())
  }
}

pub fn udp_bind() -> UdpSocket {
  UdpSocket::bind(&("0.0.0.0:0".parse().unwrap())).expect("could not parse address")
}

#[macro_export]
macro_rules! count (
  ($key:expr, $value: expr) => ({
    let v = $value;
    $crate::metrics::METRICS.with(|metrics| {
      (*metrics.borrow_mut()).count_add($key, v);
    });
  })
);

#[macro_export]
macro_rules! incr (
  ($key:expr) => (count!($key, 1););
  ($key:expr, $app_id:expr, $backend_id:expr) => {
    use $crate::metrics::Subscriber;

    $crate::metrics::METRICS.with(|metrics| {
      (*metrics.borrow_mut()).receive_metric($key, $app_id, $backend_id, $crate::metrics::MetricData::Count(1));
    });
  }
);

#[macro_export]
macro_rules! decr (
  ($key:expr) => (count!($key, -1);)
);

#[macro_export]
macro_rules! gauge (
  ($key:expr, $value: expr) => ({
    let v = $value;
    $crate::metrics::METRICS.with(|metrics| {
      (*metrics.borrow_mut()).set_gauge($key, v);
    });
  })
);

#[macro_export]
macro_rules! gauge_add (
  ($key:expr, $value: expr) => ({
    let v = $value;
    $crate::metrics::METRICS.with(|metrics| {
      (*metrics.borrow_mut()).gauge_add($key, v);
    });
  });
  ($key:expr, $value:expr, $app_id:expr, $backend_id:expr) => {
    use $crate::metrics::Subscriber;
    let v = $value;

    $crate::metrics::METRICS.with(|metrics| {
      (*metrics.borrow_mut()).receive_metric($key, $app_id, $backend_id, $crate::metrics::MetricData::GaugeAdd(v));
    });
  }
);

#[macro_export]
macro_rules! time (
  ($key:expr, $value: expr) => ({
    use $crate::metrics::{MetricData,Subscriber};
    let v = $value;
    $crate::metrics::METRICS.with(|metrics| {
      let ref mut m = *metrics.borrow_mut();

      m.receive_metric($key, None, None, MetricData::Time(v as usize));
    });
  });
  ($key:expr, $app_id:expr, $value: expr) => ({
    use $crate::metrics::{MetricData,Subscriber};
    let v = $value;
    $crate::metrics::METRICS.with(|metrics| {
      let ref mut m = *metrics.borrow_mut();
      let app: &str = $app_id;

      m.receive_metric($key, Some(app), None, MetricData::Time(v as usize));
    });
  })
);

#[macro_export]
macro_rules! record_backend_metrics (
  ($app_id:expr, $backend_id:expr, $response_time: expr, $backend_connection_time: expr, $bin: expr, $bout: expr) => {
    use $crate::metrics::{MetricData,Subscriber};
    $crate::metrics::METRICS.with(|metrics| {
      let ref mut m = *metrics.borrow_mut();
      let app_id: &str = $app_id.as_str();
      let backend_id: &str = $backend_id;

      m.receive_metric("bytes_in", Some(app_id), Some(backend_id), MetricData::Count($bin as i64));
      m.receive_metric("bytes_out", Some(app_id), Some(backend_id), MetricData::Count($bout as i64));
      m.receive_metric("response_time", Some(app_id), Some(backend_id), MetricData::Time($response_time as usize));
      if let Some(t) = $backend_connection_time {
        m.receive_metric("connection_time", Some(app_id), Some(backend_id), MetricData::Time(t.whole_milliseconds() as usize));
      }
    });
  }
);

<|endoftext|>


<commit_before>use failure::{format_err, Error};
use serde_json::{self, Map, Value};
use std::collections::{hash_map::Entry, HashMap};
use std::fs;
use std::path::Path;

fn parse_modules(
    m: &Map<String, Value>,
) -> Result<HashMap<String, HashMap<String, String>>, Error> {
    let mut res = HashMap::new();
    for (modulename, values) in m {
        match values.as_object() {
            Some(methods) => {
                let methodmap = parse_methods(methods)?;
                res.insert(modulename.to_owned(), methodmap);
            }
            None => Err(format_err!(""))?,
        }
    }
    Ok(res)
}

fn parse_methods(m: &Map<String, Value>) -> Result<HashMap<String, String>, Error> {
    let mut res = HashMap::new();
    for (method, i) in m {
        match i.as_str() {
            Some(importbinding) => {
                res.insert(method.to_owned(), importbinding.to_owned());
            }
            None => Err(format_err!(""))?,
        }
    }
    Ok(res)
}

#[derive(Debug, Clone)]
pub struct Bindings {
    bindings: HashMap<String, HashMap<String, String>>,
}

impl Bindings {
    pub fn new(bindings: HashMap<String, HashMap<String, String>>) -> Bindings {
        Self { bindings: bindings }
    }

    pub fn env(env: HashMap<String, String>) -> Bindings {
        let mut bindings = HashMap::new();
        bindings.insert("env".to_owned(), env);
        Self::new(bindings)
    }

    pub fn empty() -> Bindings {
        Self::new(HashMap::new())
    }

    pub fn from_json(v: &Value) -> Result<Bindings, Error> {
        let bindings = match v.as_object() {
            Some(modules) => parse_modules(modules)?,
            None => Err(format_err!("top level json expected to be object"))?,
        };
        Ok(Self::new(bindings))
    }

    pub fn from_str(s: &str) -> Result<Bindings, Error> {
        let top: Value = serde_json::from_str(s)?;
        Ok(Self::from_json(&top)?)
    }

    pub fn from_file<P: AsRef<Path>>(path: P) -> Result<Bindings, Error> {
        let contents = fs::read_to_string(path.as_ref())?;
        Ok(Self::from_str(&contents)?)
    }

    pub fn extend(&mut self, other: &Bindings) -> Result<(), Error> {
        //self.bindings.extend(other.bindings);
        for (modname, othermodbindings) in other.bindings.iter() {
            match self.bindings.entry(modname.clone()) {
                Entry::Occupied(mut e) => {
                    let existing = e.get_mut();
                    for (bindname, binding) in othermodbindings {
                        match existing.entry(bindname.clone()) {
                            Entry::Vacant(e) => {
                                e.insert(binding.clone());
                            }
                            Entry::Occupied(e) => {
                                if binding != e.get() {
                                    Err(format_err!(
                                        "cannot re-bind {} from {} to {}",
                                        e.key(),
                                        binding,
                                        e.get()
                                    ))?;
                                }
                            }
                        }
                    }
                }
                Entry::Vacant(e) => {
                    e.insert(othermodbindings.clone());
                }
            }
        }
        Ok(())
    }

    pub fn translate(&self, module: &str, symbol: &str) -> Result<String, Error> {
        match self.bindings.get(module) {
            Some(m) => match m.get(symbol) {
                Some(s) => Ok(s.clone()),
                None => Err(format_err!("Unknown symbol `{}::{}`", module, symbol)),
            },
            None => Err(format_err!(
                "Unknown module for symbol `{}::{}`",
                module,
                symbol
            )),
        }
    }
}

#[cfg(test)]
mod tests {
    fn test_file(f: &str) -> PathBuf {
        PathBuf::from(format!("tests/bindings/{}", f))
    }

    use super::Bindings;
    use std::collections::HashMap;
    use std::path::PathBuf;

    #[test]
    fn explicit() {
        let mut explicit_map = HashMap::new();
        explicit_map.insert(String::from("hello"), String::from("goodbye"));
        let map = Bindings::env(explicit_map);

        let result = map.translate("env", "hello").unwrap();
        assert!(result == "goodbye");

        let result = map.translate("env", "nonexistent");
        if let Ok(_) = result {
            assert!(
                false,
                "explicit import map returned value for non-existent symbol"
            )
        }
    }

    #[test]
    fn explicit_from_nonexistent_file() {
        let fail_map = Bindings::from_file(&test_file("nonexistent_bindings.json"));
        assert!(
            fail_map.is_err(),
            "ImportMap::explicit_from_file did not fail on a non-existent file"
        );
    }

    #[test]
    fn explicit_from_garbage_file() {
        let fail_map = Bindings::from_file(&test_file("garbage.json"));
        assert!(
            fail_map.is_err(),
            "ImportMap::explicit_from_file did not fail on a garbage file"
        );
    }

    #[test]
    fn explicit_from_file() {
        let map = Bindings::from_file(&test_file("bindings_test.json"))
            .expect("load valid bindings from file");
        let result = map.translate("env", "hello").expect("hello has a binding");
        assert!(result == "json is cool");

        assert!(
            map.translate("env", "nonexistent").is_err(),
            "bindings from file returned value for non-existent symbol"
        );
    }
}
<commit_msg>lucetc: bindings can be serialized to json as well<commit_after>use failure::{format_err, Error};
use serde_json::{self, Map, Value};
use std::collections::{hash_map::Entry, HashMap};
use std::fs;
use std::path::Path;

#[derive(Debug, Clone)]
pub struct Bindings {
    bindings: HashMap<String, HashMap<String, String>>,
}

impl Bindings {
    pub fn new(bindings: HashMap<String, HashMap<String, String>>) -> Bindings {
        Self { bindings: bindings }
    }

    pub fn env(env: HashMap<String, String>) -> Bindings {
        let mut bindings = HashMap::new();
        bindings.insert("env".to_owned(), env);
        Self::new(bindings)
    }

    pub fn empty() -> Bindings {
        Self::new(HashMap::new())
    }

    pub fn from_json(v: &Value) -> Result<Bindings, Error> {
        match v.as_object() {
            Some(modules) => Self::parse_modules_json_obj(modules),
            None => Err(format_err!("top level json expected to be object"))?,
        }
    }

    pub fn from_str(s: &str) -> Result<Bindings, Error> {
        let top: Value = serde_json::from_str(s)?;
        Ok(Self::from_json(&top)?)
    }

    pub fn from_file<P: AsRef<Path>>(path: P) -> Result<Bindings, Error> {
        let contents = fs::read_to_string(path.as_ref())?;
        Ok(Self::from_str(&contents)?)
    }

    pub fn extend(&mut self, other: &Bindings) -> Result<(), Error> {
        for (modname, othermodbindings) in other.bindings.iter() {
            match self.bindings.entry(modname.clone()) {
                Entry::Occupied(mut e) => {
                    let existing = e.get_mut();
                    for (bindname, binding) in othermodbindings {
                        match existing.entry(bindname.clone()) {
                            Entry::Vacant(e) => {
                                e.insert(binding.clone());
                            }
                            Entry::Occupied(e) => {
                                if binding != e.get() {
                                    Err(format_err!(
                                        "cannot re-bind {} from {} to {}",
                                        e.key(),
                                        binding,
                                        e.get()
                                    ))?;
                                }
                            }
                        }
                    }
                }
                Entry::Vacant(e) => {
                    e.insert(othermodbindings.clone());
                }
            }
        }
        Ok(())
    }

    pub fn translate(&self, module: &str, symbol: &str) -> Result<String, Error> {
        match self.bindings.get(module) {
            Some(m) => match m.get(symbol) {
                Some(s) => Ok(s.clone()),
                None => Err(format_err!("Unknown symbol `{}::{}`", module, symbol)),
            },
            None => Err(format_err!(
                "Unknown module for symbol `{}::{}`",
                module,
                symbol
            )),
        }
    }

    fn parse_modules_json_obj(m: &Map<String, Value>) -> Result<Self, Error> {
        let mut res = HashMap::new();
        for (modulename, values) in m {
            match values.as_object() {
                Some(methods) => {
                    let methodmap = Self::parse_methods_json_obj(methods)?;
                    res.insert(modulename.to_owned(), methodmap);
                }
                None => Err(format_err!(""))?,
            }
        }
        Ok(Self::new(res))
    }

    fn parse_methods_json_obj(m: &Map<String, Value>) -> Result<HashMap<String, String>, Error> {
        let mut res = HashMap::new();
        for (method, i) in m {
            match i.as_str() {
                Some(importbinding) => {
                    res.insert(method.to_owned(), importbinding.to_owned());
                }
                None => Err(format_err!(""))?,
            }
        }
        Ok(res)
    }

    pub fn to_string(&self) -> Result<String, Error> {
        let s = serde_json::to_string(&self.to_json())?;
        Ok(s)
    }

    pub fn to_json(&self) -> Value {
        Value::from(self.serialize_modules_json_obj())
    }

    fn serialize_modules_json_obj(&self) -> Map<String, Value> {
        let mut m = Map::new();
        for (modulename, values) in self.bindings.iter() {
            m.insert(
                modulename.to_owned(),
                Value::from(Self::serialize_methods_json_obj(values)),
            );
        }
        m
    }

    fn serialize_methods_json_obj(methods: &HashMap<String, String>) -> Map<String, Value> {
        let mut m = Map::new();
        for (methodname, symbol) in methods.iter() {
            m.insert(methodname.to_owned(), Value::from(symbol.to_owned()));
        }
        m
    }
}

#[cfg(test)]
mod tests {
    fn test_file(f: &str) -> PathBuf {
        PathBuf::from(format!("tests/bindings/{}", f))
    }

    use super::Bindings;
    use std::collections::HashMap;
    use std::path::PathBuf;

    #[test]
    fn explicit() {
        let mut explicit_map = HashMap::new();
        explicit_map.insert(String::from("hello"), String::from("goodbye"));
        let map = Bindings::env(explicit_map);

        let result = map.translate("env", "hello").unwrap();
        assert!(result == "goodbye");

        let result = map.translate("env", "nonexistent");
        if let Ok(_) = result {
            assert!(
                false,
                "explicit import map returned value for non-existent symbol"
            )
        }
    }

    #[test]
    fn explicit_from_nonexistent_file() {
        let fail_map = Bindings::from_file(&test_file("nonexistent_bindings.json"));
        assert!(
            fail_map.is_err(),
            "ImportMap::explicit_from_file did not fail on a non-existent file"
        );
    }

    #[test]
    fn explicit_from_garbage_file() {
        let fail_map = Bindings::from_file(&test_file("garbage.json"));
        assert!(
            fail_map.is_err(),
            "ImportMap::explicit_from_file did not fail on a garbage file"
        );
    }

    #[test]
    fn explicit_from_file() {
        let map = Bindings::from_file(&test_file("bindings_test.json"))
            .expect("load valid bindings from file");
        let result = map.translate("env", "hello").expect("hello has a binding");
        assert!(result == "json is cool");

        assert!(
            map.translate("env", "nonexistent").is_err(),
            "bindings from file returned value for non-existent symbol"
        );
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2013-2016 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

use std::rc::Rc;
use std::cell::RefCell;
use std::collections::HashMap;

use capnp_rpc::{RpcSystem, twoparty, rpc_twoparty_capnp};
use pubsub_capnp::{publisher, subscriber, subscription};

use capnp::capability::Promise;
use capnp::Error;

use futures::{Future, Stream};

use tokio_core::reactor;
use tokio_io::AsyncRead;

struct SubscriberHandle {
    client: subscriber::Client<::capnp::text::Owned>,
    requests_in_flight: i32,
}

struct SubscriberMap {
    subscribers: HashMap<u64, SubscriberHandle>,
}

impl SubscriberMap {
    fn new() -> SubscriberMap {
        SubscriberMap { subscribers: HashMap::new() }
    }
}

struct SubscriptionImpl {
    id: u64,
    subscribers: Rc<RefCell<SubscriberMap>>,
}

impl SubscriptionImpl {
    fn new(id: u64, subscribers: Rc<RefCell<SubscriberMap>>) -> SubscriptionImpl {
        SubscriptionImpl { id: id, subscribers: subscribers }
    }
}

impl Drop for SubscriptionImpl {
    fn drop(&mut self) {
        println!("subscription dropped");
        self.subscribers.borrow_mut().subscribers.remove(&self.id);
    }
}

impl subscription::Server for SubscriptionImpl {}

struct PublisherImpl {
    next_id: u64,
    subscribers: Rc<RefCell<SubscriberMap>>,
}

impl PublisherImpl {
    pub fn new() -> (PublisherImpl, Rc<RefCell<SubscriberMap>>) {
        let subscribers = Rc::new(RefCell::new(SubscriberMap::new()));
        (PublisherImpl { next_id: 0, subscribers: subscribers.clone() },
         subscribers.clone())
    }
}

impl publisher::Server<::capnp::text::Owned> for PublisherImpl {
    fn subscribe(&mut self,
                 params: publisher::SubscribeParams<::capnp::text::Owned>,
                 mut results: publisher::SubscribeResults<::capnp::text::Owned>,)
                 -> Promise<(), ::capnp::Error>
    {
        println!("subscribe");
        self.subscribers.borrow_mut().subscribers.insert(
            self.next_id,
            SubscriberHandle {
                client: pry!(pry!(params.get()).get_subscriber()),
                requests_in_flight: 0,
            }
        );

        results.get().set_subscription(
            subscription::ToClient::new(SubscriptionImpl::new(self.next_id, self.subscribers.clone()))
                .from_server::<::capnp_rpc::Server>());

        self.next_id += 1;
        Promise::ok(())
    }
}
pub fn main() {
    use std::net::ToSocketAddrs;
    let args: Vec<String> = ::std::env::args().collect();
    if args.len() != 3 {
        println!("usage: {} server HOST:PORT", args[0]);
        return;
    }

    let mut core = reactor::Core::new().unwrap();
    let handle = core.handle();

    let addr = args[2].to_socket_addrs().unwrap().next().expect("could not parse address");
    let socket = ::tokio_core::net::TcpListener::bind(&addr, &handle).unwrap();

    let (publisher_impl, subscribers) = PublisherImpl::new();

    let publisher = publisher::ToClient::new(publisher_impl).from_server::<::capnp_rpc::Server>();

    let handle1 = handle.clone();
    let done = socket.incoming().for_each(move |(socket, _addr)| {
        try!(socket.set_nodelay(true));
        let (reader, writer) = socket.split();
        let handle = handle1.clone();

        let network =
            twoparty::VatNetwork::new(reader, writer,
                                      rpc_twoparty_capnp::Side::Server, Default::default());

        let rpc_system = RpcSystem::new(Box::new(network), Some(publisher.clone().client));

        handle.spawn(rpc_system.map_err(|_| ()));
        Ok(())
    }).map_err(|e| e.into());

    let infinite = ::futures::stream::iter(::std::iter::repeat(()).map(Ok::<(), Error>));
    let send_to_subscribers = infinite.fold((handle, subscribers), move |(handle, subscribers), ()| -> Promise<(::tokio_core::reactor::Handle, Rc<RefCell<SubscriberMap>>), Error> {
        {
            let subscribers1 = subscribers.clone();
            let subs = &mut subscribers.borrow_mut().subscribers;
            for (&idx, mut subscriber) in subs.iter_mut() {
                if subscriber.requests_in_flight < 5 {
                    subscriber.requests_in_flight += 1;
                    let mut request = subscriber.client.push_message_request();
                    pry!(request.get().set_message(
                        &format!("system time is: {:?}", ::std::time::SystemTime::now())[..]));

                    let subscribers2 = subscribers1.clone();
                    handle.spawn(request.send().promise.then(move |r| {
                        match r {
                            Ok(_) => {
                                subscribers2.borrow_mut().subscribers.get_mut(&idx).map(|ref mut s| {
                                    s.requests_in_flight -= 1;
                                });
                            }
                            Err(e) => {
                                println!("Got error: {:?}. Dropping subscriber.", e);
                                subscribers2.borrow_mut().subscribers.remove(&idx);
                            }
                        }
                        Ok::<(), Error>(())
                    }).map_err(|_| unreachable!()));
                }
            }
        }

        let timeout = pry!(::tokio_core::reactor::Timeout::new(::std::time::Duration::from_secs(1), &handle));
        let timeout = timeout.and_then(move |()| Ok((handle, subscribers))).map_err(|e| e.into());
        Promise::from_future(timeout)
    });

    core.run(send_to_subscribers.join(done)).unwrap();
}
<commit_msg>fix deprecation warning: futures::stream::iter() -> futures::stream::iter_ok()<commit_after>// Copyright (c) 2013-2016 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

use std::rc::Rc;
use std::cell::RefCell;
use std::collections::HashMap;

use capnp_rpc::{RpcSystem, twoparty, rpc_twoparty_capnp};
use pubsub_capnp::{publisher, subscriber, subscription};

use capnp::capability::Promise;
use capnp::Error;

use futures::{Future, Stream};

use tokio_core::reactor;
use tokio_io::AsyncRead;

struct SubscriberHandle {
    client: subscriber::Client<::capnp::text::Owned>,
    requests_in_flight: i32,
}

struct SubscriberMap {
    subscribers: HashMap<u64, SubscriberHandle>,
}

impl SubscriberMap {
    fn new() -> SubscriberMap {
        SubscriberMap { subscribers: HashMap::new() }
    }
}

struct SubscriptionImpl {
    id: u64,
    subscribers: Rc<RefCell<SubscriberMap>>,
}

impl SubscriptionImpl {
    fn new(id: u64, subscribers: Rc<RefCell<SubscriberMap>>) -> SubscriptionImpl {
        SubscriptionImpl { id: id, subscribers: subscribers }
    }
}

impl Drop for SubscriptionImpl {
    fn drop(&mut self) {
        println!("subscription dropped");
        self.subscribers.borrow_mut().subscribers.remove(&self.id);
    }
}

impl subscription::Server for SubscriptionImpl {}

struct PublisherImpl {
    next_id: u64,
    subscribers: Rc<RefCell<SubscriberMap>>,
}

impl PublisherImpl {
    pub fn new() -> (PublisherImpl, Rc<RefCell<SubscriberMap>>) {
        let subscribers = Rc::new(RefCell::new(SubscriberMap::new()));
        (PublisherImpl { next_id: 0, subscribers: subscribers.clone() },
         subscribers.clone())
    }
}

impl publisher::Server<::capnp::text::Owned> for PublisherImpl {
    fn subscribe(&mut self,
                 params: publisher::SubscribeParams<::capnp::text::Owned>,
                 mut results: publisher::SubscribeResults<::capnp::text::Owned>,)
                 -> Promise<(), ::capnp::Error>
    {
        println!("subscribe");
        self.subscribers.borrow_mut().subscribers.insert(
            self.next_id,
            SubscriberHandle {
                client: pry!(pry!(params.get()).get_subscriber()),
                requests_in_flight: 0,
            }
        );

        results.get().set_subscription(
            subscription::ToClient::new(SubscriptionImpl::new(self.next_id, self.subscribers.clone()))
                .from_server::<::capnp_rpc::Server>());

        self.next_id += 1;
        Promise::ok(())
    }
}
pub fn main() {
    use std::net::ToSocketAddrs;
    let args: Vec<String> = ::std::env::args().collect();
    if args.len() != 3 {
        println!("usage: {} server HOST:PORT", args[0]);
        return;
    }

    let mut core = reactor::Core::new().unwrap();
    let handle = core.handle();

    let addr = args[2].to_socket_addrs().unwrap().next().expect("could not parse address");
    let socket = ::tokio_core::net::TcpListener::bind(&addr, &handle).unwrap();

    let (publisher_impl, subscribers) = PublisherImpl::new();

    let publisher = publisher::ToClient::new(publisher_impl).from_server::<::capnp_rpc::Server>();

    let handle1 = handle.clone();
    let done = socket.incoming().for_each(move |(socket, _addr)| {
        try!(socket.set_nodelay(true));
        let (reader, writer) = socket.split();
        let handle = handle1.clone();

        let network =
            twoparty::VatNetwork::new(reader, writer,
                                      rpc_twoparty_capnp::Side::Server, Default::default());

        let rpc_system = RpcSystem::new(Box::new(network), Some(publisher.clone().client));

        handle.spawn(rpc_system.map_err(|_| ()));
        Ok(())
    }).map_err(|e| e.into());

    let infinite = ::futures::stream::iter_ok::<_, Error>(::std::iter::repeat(()));
    let send_to_subscribers = infinite.fold((handle, subscribers), move |(handle, subscribers), ()| -> Promise<(::tokio_core::reactor::Handle, Rc<RefCell<SubscriberMap>>), Error> {
        {
            let subscribers1 = subscribers.clone();
            let subs = &mut subscribers.borrow_mut().subscribers;
            for (&idx, mut subscriber) in subs.iter_mut() {
                if subscriber.requests_in_flight < 5 {
                    subscriber.requests_in_flight += 1;
                    let mut request = subscriber.client.push_message_request();
                    pry!(request.get().set_message(
                        &format!("system time is: {:?}", ::std::time::SystemTime::now())[..]));

                    let subscribers2 = subscribers1.clone();
                    handle.spawn(request.send().promise.then(move |r| {
                        match r {
                            Ok(_) => {
                                subscribers2.borrow_mut().subscribers.get_mut(&idx).map(|ref mut s| {
                                    s.requests_in_flight -= 1;
                                });
                            }
                            Err(e) => {
                                println!("Got error: {:?}. Dropping subscriber.", e);
                                subscribers2.borrow_mut().subscribers.remove(&idx);
                            }
                        }
                        Ok::<(), Error>(())
                    }).map_err(|_| unreachable!()));
                }
            }
        }

        let timeout = pry!(::tokio_core::reactor::Timeout::new(::std::time::Duration::from_secs(1), &handle));
        let timeout = timeout.and_then(move |()| Ok((handle, subscribers))).map_err(|e| e.into());
        Promise::from_future(timeout)
    });

    core.run(send_to_subscribers.join(done)).unwrap();
}
<|endoftext|>


<commit_before>#![crate_id(name="uutils", vers="1.0.0", author="Michael Gehring")]

/*
 * This file is part of the uutils coreutils package.
 *
 * (c) Michael Gehring <mg@ebfe.org>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

extern crate getopts;

@CRATES@

use std::os;
use std::collections::hashmap::HashMap;

static NAME: &'static str = "uutils";
static VERSION: &'static str = "1.0.0";

fn util_map() -> HashMap<&str, fn(Vec<String>) -> int> {
    fn uutrue(_: Vec<String>) -> int { 0 }
    fn uufalse(_: Vec<String>) -> int { 1 }

    let mut map = HashMap::new();
    @UTIL_MAP@
    map
}

fn usage(cmap: &HashMap<&str, fn(Vec<String>) -> int>) {
    println!("{} {}", NAME, VERSION);
    println!("");
    println!("Usage:");
    println!("  {} [util [arguments...]", NAME);
    println!("Currently defined functions:");
    let mut utils: Vec<&str> = cmap.keys().map(|&s| s).collect();
    utils.sort();
    for util in utils.iter() {
        println!("\t{}", util);
    }
    println!("");
}

fn main() {
    let umap = util_map();
    let mut args = os::args();

    // try binary name as util name.
    let binary = Path::new(args.get(0).as_slice());
    let binary_as_util = binary.filename_str().unwrap();
    if umap.contains_key(&binary_as_util) {
        let &uumain = umap.get(&binary_as_util);
        os::set_exit_status(uumain(args));
        return
    } else if binary_as_util.starts_with("uutils")
        || binary_as_util.starts_with("busybox") {
            // uutils can be called as either "uutils", "busybox"
            // "uutils-suffix" or "busybox-suffix". Not sure
            // what busybox uses the -suffix pattern for.
    } else {
        println!("{}: applet not found", binary_as_util);
        os::set_exit_status(1);
        return
    }

    // try first arg as util name.
    if args.len() >= 2 {
        args.shift();
        let util = args.get(0).as_slice();
        if umap.contains_key(&util) {
            let &uumain = umap.get(&util);
            os::set_exit_status(uumain(args.clone()));
            return
        } else if args.get(0).as_slice() == "--help" {
            // see if they want help on a specific util
            if args.len() >= 2 {
                let util = args.get(1).as_slice();
                if umap.contains_key(&util) {
                    let &uumain = umap.get(&util);
                    os::set_exit_status(uumain(vec!["--help".to_string()]));
                    return
                } else {
                    println!("{}: applet not found", util);
                    os::set_exit_status(1);
                    return
                }
            }
            usage(&umap);
            os::set_exit_status(0);
            return
        } else {
            println!("{}: applet not found", util);
            os::set_exit_status(1);
            return
        }
    } else {
        // no arguments provided
        usage(&umap);
        os::set_exit_status(0);
        return
    }
}
<commit_msg>uutils: work around rustc strangeness<commit_after>#![crate_id(name="uutils", vers="1.0.0", author="Michael Gehring")]

/*
 * This file is part of the uutils coreutils package.
 *
 * (c) Michael Gehring <mg@ebfe.org>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

extern crate getopts;

@CRATES@

use std::os;
use std::collections::hashmap::HashMap;

static NAME: &'static str = "uutils";
static VERSION: &'static str = "1.0.0";

fn util_map() -> HashMap<&str, fn(Vec<String>) -> int> {
    fn uutrue(_: Vec<String>) -> int { 0 }
    fn uufalse(_: Vec<String>) -> int { 1 }

    let mut map = HashMap::<&str, fn(Vec<String>) -> int>::new();
    @UTIL_MAP@
    map
}

fn usage(cmap: &HashMap<&str, fn(Vec<String>) -> int>) {
    println!("{} {}", NAME, VERSION);
    println!("");
    println!("Usage:");
    println!("  {} [util [arguments...]", NAME);
    println!("Currently defined functions:");
    let mut utils: Vec<&str> = cmap.keys().map(|&s| s).collect();
    utils.sort();
    for util in utils.iter() {
        println!("\t{}", util);
    }
    println!("");
}

fn main() {
    let umap = util_map();
    let mut args = os::args();

    // try binary name as util name.
    let binary = Path::new(args.get(0).as_slice());
    let binary_as_util = binary.filename_str().unwrap();
    if umap.contains_key(&binary_as_util) {
        let &uumain = umap.get(&binary_as_util);
        os::set_exit_status(uumain(args));
        return
    } else if binary_as_util.starts_with("uutils")
        || binary_as_util.starts_with("busybox") {
            // uutils can be called as either "uutils", "busybox"
            // "uutils-suffix" or "busybox-suffix". Not sure
            // what busybox uses the -suffix pattern for.
    } else {
        println!("{}: applet not found", binary_as_util);
        os::set_exit_status(1);
        return
    }

    // try first arg as util name.
    if args.len() >= 2 {
        args.shift();
        let util = args.get(0).as_slice();
        if umap.contains_key(&util) {
            let &uumain = umap.get(&util);
            os::set_exit_status(uumain(args.clone()));
            return
        } else if args.get(0).as_slice() == "--help" {
            // see if they want help on a specific util
            if args.len() >= 2 {
                let util = args.get(1).as_slice();
                if umap.contains_key(&util) {
                    let &uumain = umap.get(&util);
                    os::set_exit_status(uumain(vec!["--help".to_string()]));
                    return
                } else {
                    println!("{}: applet not found", util);
                    os::set_exit_status(1);
                    return
                }
            }
            usage(&umap);
            os::set_exit_status(0);
            return
        } else {
            println!("{}: applet not found", util);
            os::set_exit_status(1);
            return
        }
    } else {
        // no arguments provided
        usage(&umap);
        os::set_exit_status(0);
        return
    }
}
<|endoftext|>


<commit_before>use crate::{
    analysis::{
        imports::Imports, namespaces, override_string_type::override_string_type_return,
        ref_mode::RefMode, rust_type::*,
    },
    config,
    env::Env,
    library::{self, Nullable, TypeId},
};
use log::error;

#[derive(Clone, Debug, Default)]
pub struct Info {
    pub parameter: Option<library::Parameter>,
    pub base_tid: Option<library::TypeId>, // Some only if need downcast
    pub commented: bool,
    pub bool_return_is_error: Option<String>,
    pub nullable_return_is_error: Option<String>,
}

pub fn analyze(
    env: &Env,
    obj: &config::gobjects::GObject,
    func: &library::Function,
    type_tid: library::TypeId,
    configured_functions: &[&config::functions::Function],
    used_types: &mut Vec<String>,
    imports: &mut Imports,
) -> Info {
    let typ = configured_functions
        .iter()
        .filter_map(|f| f.ret.type_name.as_ref())
        .next()
        .and_then(|typ| env.library.find_type(0, typ))
        .unwrap_or_else(|| override_string_type_return(env, func.ret.typ, configured_functions));
    let mut parameter = if typ == Default::default() {
        None
    } else {
        if let Ok(s) = used_rust_type(env, typ, false) {
            used_types.push(s);
        }

        let mut nullable = func.ret.nullable;
        if !obj.trust_return_value_nullability {
            // Since GIRs are bad at specifying return value nullability, assume
            // any returned pointer is nullable unless overridden by the config.
            if !*nullable && can_be_nullable_return(env, typ) {
                *nullable = true;
            }
        }

        let nullable_override = configured_functions
            .iter()
            .filter_map(|f| f.ret.nullable)
            .next();
        if let Some(val) = nullable_override {
            nullable = val;
        }
        Some(library::Parameter {
            typ,
            nullable,
            ..func.ret.clone()
        })
    };

    let commented = if typ == Default::default() {
        false
    } else {
        parameter_rust_type(
            env,
            typ,
            func.ret.direction,
            Nullable(false),
            RefMode::None,
            library::ParameterScope::None,
        )
        .is_err()
    };

    let bool_return_is_error = configured_functions
        .iter()
        .filter_map(|f| f.ret.bool_return_is_error.as_ref())
        .next();
    let bool_return_error_message = bool_return_is_error.and_then(|m| {
        if typ != TypeId::tid_bool() {
            error!(
                "Ignoring bool_return_is_error configuration for non-bool returning function {}",
                func.name
            );
            None
        } else {
            let ns = if env.namespaces.glib_ns_id == namespaces::MAIN {
                "error"
            } else {
                "glib"
            };
            imports.add(ns);

            Some(m.clone())
        }
    });

    let nullable_return_is_error = configured_functions
        .iter()
        .filter_map(|f| f.ret.nullable_return_is_error.as_ref())
        .next();
    let nullable_return_error_message = nullable_return_is_error.and_then(|m| {
        if let Some(library::Parameter { nullable: Nullable(false), ..}) = parameter {
            error!(
                "Ignoring nullable_return_is_error configuration for non-none returning function {}",
                func.name
            );
            None
        } else {
            let ns = if env.namespaces.glib_ns_id == namespaces::MAIN {
                "BoolError"
            } else {
                "glib"
            };
            imports.add(ns);

            Some(m.clone())
        }
    });

    let mut base_tid = None;

    if func.kind == library::FunctionKind::Constructor {
        if let Some(par) = parameter {
            let nullable_override = configured_functions
                .iter()
                .filter_map(|f| f.ret.nullable)
                .next();
            if par.typ != type_tid {
                base_tid = Some(par.typ);
            }
            parameter = Some(library::Parameter {
                typ: type_tid,
                nullable: nullable_override.unwrap_or(func.ret.nullable),
                ..par
            });
        }
    }

    Info {
        parameter,
        base_tid,
        commented,
        bool_return_is_error: bool_return_error_message,
        nullable_return_is_error: nullable_return_error_message,
    }
}

fn can_be_nullable_return(env: &Env, type_id: library::TypeId) -> bool {
    use crate::library::{Fundamental::*, Type::*};
    match *env.library.type_(type_id) {
        Fundamental(fund) => matches!(fund, Pointer | Utf8 | Filename | OsString),
        Alias(ref alias) => can_be_nullable_return(env, alias.typ),
        Enumeration(_) => false,
        Bitfield(_) => false,
        Record(_) => true,
        Union(_) => true,
        Function(_) => true,
        Interface(_) => true,
        Class(_) => true,
        _ => true,
    }
}
<commit_msg>Fix import of BoolError in glib<commit_after>use crate::{
    analysis::{
        imports::Imports, namespaces, override_string_type::override_string_type_return,
        ref_mode::RefMode, rust_type::*,
    },
    config,
    env::Env,
    library::{self, Nullable, TypeId},
};
use log::error;

#[derive(Clone, Debug, Default)]
pub struct Info {
    pub parameter: Option<library::Parameter>,
    pub base_tid: Option<library::TypeId>, // Some only if need downcast
    pub commented: bool,
    pub bool_return_is_error: Option<String>,
    pub nullable_return_is_error: Option<String>,
}

pub fn analyze(
    env: &Env,
    obj: &config::gobjects::GObject,
    func: &library::Function,
    type_tid: library::TypeId,
    configured_functions: &[&config::functions::Function],
    used_types: &mut Vec<String>,
    imports: &mut Imports,
) -> Info {
    let typ = configured_functions
        .iter()
        .filter_map(|f| f.ret.type_name.as_ref())
        .next()
        .and_then(|typ| env.library.find_type(0, typ))
        .unwrap_or_else(|| override_string_type_return(env, func.ret.typ, configured_functions));
    let mut parameter = if typ == Default::default() {
        None
    } else {
        if let Ok(s) = used_rust_type(env, typ, false) {
            used_types.push(s);
        }

        let mut nullable = func.ret.nullable;
        if !obj.trust_return_value_nullability {
            // Since GIRs are bad at specifying return value nullability, assume
            // any returned pointer is nullable unless overridden by the config.
            if !*nullable && can_be_nullable_return(env, typ) {
                *nullable = true;
            }
        }

        let nullable_override = configured_functions
            .iter()
            .filter_map(|f| f.ret.nullable)
            .next();
        if let Some(val) = nullable_override {
            nullable = val;
        }
        Some(library::Parameter {
            typ,
            nullable,
            ..func.ret.clone()
        })
    };

    let commented = if typ == Default::default() {
        false
    } else {
        parameter_rust_type(
            env,
            typ,
            func.ret.direction,
            Nullable(false),
            RefMode::None,
            library::ParameterScope::None,
        )
        .is_err()
    };

    let bool_return_is_error = configured_functions
        .iter()
        .filter_map(|f| f.ret.bool_return_is_error.as_ref())
        .next();
    let bool_return_error_message = bool_return_is_error.and_then(|m| {
        if typ != TypeId::tid_bool() {
            error!(
                "Ignoring bool_return_is_error configuration for non-bool returning function {}",
                func.name
            );
            None
        } else {
            let ns = if env.namespaces.glib_ns_id == namespaces::MAIN {
                "error"
            } else {
                "glib"
            };
            imports.add(ns);

            Some(m.clone())
        }
    });

    let nullable_return_is_error = configured_functions
        .iter()
        .filter_map(|f| f.ret.nullable_return_is_error.as_ref())
        .next();
    let nullable_return_error_message = nullable_return_is_error.and_then(|m| {
        if let Some(library::Parameter { nullable: Nullable(false), ..}) = parameter {
            error!(
                "Ignoring nullable_return_is_error configuration for non-none returning function {}",
                func.name
            );
            None
        } else {
            let ns = if env.namespaces.glib_ns_id == namespaces::MAIN {
                "crate::BoolError"
            } else {
                "glib"
            };
            imports.add(ns);

            Some(m.clone())
        }
    });

    let mut base_tid = None;

    if func.kind == library::FunctionKind::Constructor {
        if let Some(par) = parameter {
            let nullable_override = configured_functions
                .iter()
                .filter_map(|f| f.ret.nullable)
                .next();
            if par.typ != type_tid {
                base_tid = Some(par.typ);
            }
            parameter = Some(library::Parameter {
                typ: type_tid,
                nullable: nullable_override.unwrap_or(func.ret.nullable),
                ..par
            });
        }
    }

    Info {
        parameter,
        base_tid,
        commented,
        bool_return_is_error: bool_return_error_message,
        nullable_return_is_error: nullable_return_error_message,
    }
}

fn can_be_nullable_return(env: &Env, type_id: library::TypeId) -> bool {
    use crate::library::{Fundamental::*, Type::*};
    match *env.library.type_(type_id) {
        Fundamental(fund) => matches!(fund, Pointer | Utf8 | Filename | OsString),
        Alias(ref alias) => can_be_nullable_return(env, alias.typ),
        Enumeration(_) => false,
        Bitfield(_) => false,
        Record(_) => true,
        Union(_) => true,
        Function(_) => true,
        Interface(_) => true,
        Class(_) => true,
        _ => true,
    }
}
<|endoftext|>


<commit_before>extern crate flac;

use flac::metadata;
use std::io::ErrorKind;

#[test]
fn test_get_stream_info() {
  let result = metadata::get_stream_info("tests/assets/input-SCPAP.flac");

  assert!(result.is_ok());
}

#[test]
fn test_get_vorbis_comment() {
  let tags    = metadata::get_vorbis_comment("tests/assets/input-SVAUP.flac");
  let no_tags = metadata::get_vorbis_comment("tests/assets/input-SCPAP.flac");

  assert!(tags.is_ok(), "Should have vorbis comments");
  assert_eq!(no_tags.unwrap_err().kind(), ErrorKind::NotFound);
}

#[test]
fn test_get_cue_sheet() {
  let cue_sheet    = metadata::get_cue_sheet("tests/assets/input-SCPAP.flac");
  let no_cue_sheet = metadata::get_cue_sheet("tests/assets/input-SVAUP.flac");

  assert!(cue_sheet.is_ok(), "Should have a cue sheet");
  assert_eq!(no_cue_sheet.unwrap_err().kind(), ErrorKind::NotFound);
}
<commit_msg>Add test for get_picture<commit_after>extern crate flac;

use flac::metadata;
use flac::metadata::{Picture, PictureType};
use std::io::ErrorKind;

fn compare_all_but_data(picture: &Picture, other_picture: &Picture) -> bool {
  (picture.picture_type == other_picture.picture_type) &&
    (picture.mime_type == other_picture.mime_type) &&
    (picture.description == other_picture.description) &&
    (picture.width == other_picture.width) &&
    (picture.height == other_picture.height) &&
    (picture.depth == other_picture.depth) &&
    (picture.colors == other_picture.colors)
}

#[test]
fn test_get_stream_info() {
  let result = metadata::get_stream_info("tests/assets/input-SCPAP.flac");

  assert!(result.is_ok());
}

#[test]
fn test_get_vorbis_comment() {
  let tags    = metadata::get_vorbis_comment("tests/assets/input-SVAUP.flac");
  let no_tags = metadata::get_vorbis_comment("tests/assets/input-SCPAP.flac");

  assert!(tags.is_ok(), "Should have vorbis comments");
  assert_eq!(no_tags.unwrap_err().kind(), ErrorKind::NotFound);
}

#[test]
fn test_get_cue_sheet() {
  let cue_sheet    = metadata::get_cue_sheet("tests/assets/input-SCPAP.flac");
  let no_cue_sheet = metadata::get_cue_sheet("tests/assets/input-SVAUP.flac");

  assert!(cue_sheet.is_ok(), "Should have a cue sheet");
  assert_eq!(no_cue_sheet.unwrap_err().kind(), ErrorKind::NotFound);
}

#[test]
fn test_get_picture() {
  let picture_file = "tests/assets/input-pictures.flac";
  let all_options  = metadata::get_picture(picture_file,
                                           Some(PictureType::Other),
                                           Some("image/gif"),
                                           Some(""),
                                           Some(16),
                                           Some(14),
                                           Some(24),
                                           Some(128));
  let picture1 = Picture {
    picture_type: PictureType::Other,
    mime_type: "image/gif".to_owned(),
    description: "".to_owned(),
    width: 16,
    height: 14,
    depth: 24,
    colors: 128,
    data: vec![],
  };

  let one_option = metadata::get_picture(picture_file,
                                         Some(PictureType::FileIconStandard),
                                         None, None, None, None, None, None);
  let picture2 = Picture {
    picture_type: PictureType::FileIconStandard,
    mime_type: "image/png".to_owned(),
    description: "8.png".to_owned(),
    width: 32,
    height: 32,
    depth: 32,
    colors: 0,
    data: vec![],
  };

  let no_option = metadata::get_picture(picture_file, None, None, None, None,
                                        None, None, None);
  let picture3  = Picture {
    picture_type: PictureType::FrontCover,
    mime_type: "image/png".to_owned(),
    description: "7.png".to_owned(),
    width: 31,
    height: 47,
    depth: 24,
    colors: 23,
    data: vec![],
  };
  let no_picture = metadata::get_picture("tests/assets/input-SVAUP.flac",
                                         None, None, None, None, None, None,
                                         None);

  assert!(compare_all_but_data(&all_options.unwrap(), &picture1),
          "All constraint options");
  assert!(compare_all_but_data(&one_option.unwrap(), &picture2),
          "One constraint option");
  assert!(compare_all_but_data(&no_option.unwrap(), &picture3),
          "No constraint option");
  assert_eq!(no_picture.unwrap_err().kind(), ErrorKind::NotFound);
}
<|endoftext|>