<commit_before>//! Fuchsia specific definitions
use std::io::{self, Read, Stdout, Write};
use std::mem;
use std::sync::atomic;
use libc;
use char_iter;

use config::Config;
use consts::{self, KeyPress};
use error;
use Result;
use super::{RawMode, RawReader, Term};
use fuchsia_device::pty;

const STDIN_FILENO: usize = 0;
const STDOUT_FILENO: usize = 1;

fn get_win_size() -> (usize, usize) {
    match pty::get_window_size() {
        Ok(size) => {
            (size.width as usize, size.height as usize)
        }
        _ => (80, 24),
    }
}

struct StdinRaw {}

impl Read for StdinRaw {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        loop {
            let res = unsafe {
                libc::read(
                    STDIN_FILENO as i32,
                    buf.as_mut_ptr() as *mut libc::c_void,
                    buf.len() as libc::size_t,
                )
            };
            if res == -1 {
                let error = io::Error::last_os_error();
                if error.kind() != io::ErrorKind::Interrupted {
                    return Err(error);
                }
            } else {
                return Ok(res as usize);
            }
        }
    }
}

pub type Mode = ConsoleMode;

#[derive(Clone, Copy, Debug)]
pub struct ConsoleMode {}

impl RawMode for Mode {
    /// RAW mode is never on w/ Fuchsia
    fn disable_raw_mode(&self) -> Result<()> {
        Ok(())
    }
}

pub type Terminal = Console;

#[derive(Clone, Debug)]
pub struct Console {
    stdin_isatty: bool,
}

pub struct FuchsiaRawReader {
    chars: char_iter::Chars<StdinRaw>,
}

impl FuchsiaRawReader {
    pub fn new() -> Result<FuchsiaRawReader> {
        let stdin = StdinRaw {};
        Ok(FuchsiaRawReader {
            chars: char_iter::chars(stdin),
        })
    }
    fn escape_sequence(&mut self) -> Result<KeyPress> {
        // Read the next two bytes representing the escape sequence.
        let seq1 = try!(self.next_char());
        if seq1 == '[' {
            // ESC [ sequences.
            let seq2 = try!(self.next_char());
            if seq2.is_digit(10) {
                // Extended escape, read additional byte.
                let seq3 = try!(self.next_char());
                if seq3 == '~' {
                    Ok(match seq2 {
                        '1' | '7' => KeyPress::Home, // '1': xterm
                        '3' => KeyPress::Delete,
                        '4' | '8' => KeyPress::End, // '4': xterm
                        '5' => KeyPress::PageUp,
                        '6' => KeyPress::PageDown,
                        _ => {
                            debug!(target: "rustyline", "unsupported esc sequence: ESC{:?}{:?}{:?}", seq1, seq2, seq3);
                            KeyPress::UnknownEscSeq
                        }
                    })
                } else {
                    debug!(target: "rustyline", "unsupported esc sequence: ESC{:?}{:?}{:?}", seq1, seq2, seq3);
                    Ok(KeyPress::UnknownEscSeq)
                }
            } else {
                Ok(match seq2 {
                    'A' => KeyPress::Up, // ANSI
                    'B' => KeyPress::Down,
                    'C' => KeyPress::Right,
                    'D' => KeyPress::Left,
                    'F' => KeyPress::End,
                    'H' => KeyPress::Home,
                    _ => {
                        debug!(target: "rustyline", "unsupported esc sequence: ESC{:?}{:?}", seq1, seq2);
                        KeyPress::UnknownEscSeq
                    }
                })
            }
        } else if seq1 == 'O' {
            // ESC O sequences.
            let seq2 = try!(self.next_char());
            Ok(match seq2 {
                'A' => KeyPress::Up,
                'B' => KeyPress::Down,
                'C' => KeyPress::Right,
                'D' => KeyPress::Left,
                'F' => KeyPress::End,
                'H' => KeyPress::Home,
                _ => {
                    debug!(target: "rustyline", "unsupported esc sequence: ESC{:?}{:?}", seq1, seq2);
                    KeyPress::UnknownEscSeq
                }
            })
        } else {
            // TODO ESC-R (r): Undo all changes made to this line.
            Ok(match seq1 {
                '\x08' => KeyPress::Meta('\x08'), // Backspace
                '-' => KeyPress::Meta('-'),
                '0'...'9' => KeyPress::Meta(seq1),
                '<' => KeyPress::Meta('<'),
                '>' => KeyPress::Meta('>'),
                'b' | 'B' => KeyPress::Meta('B'),
                'c' | 'C' => KeyPress::Meta('C'),
                'd' | 'D' => KeyPress::Meta('D'),
                'f' | 'F' => KeyPress::Meta('F'),
                'l' | 'L' => KeyPress::Meta('L'),
                'n' | 'N' => KeyPress::Meta('N'),
                'p' | 'P' => KeyPress::Meta('P'),
                't' | 'T' => KeyPress::Meta('T'),
                'u' | 'U' => KeyPress::Meta('U'),
                'y' | 'Y' => KeyPress::Meta('Y'),
                '\x7f' => KeyPress::Meta('\x7f'), // Delete
                _ => {
                    debug!(target: "rustyline", "unsupported esc sequence: M-{:?}", seq1);
                    KeyPress::UnknownEscSeq
                }
            })
        }
    }
}

// TODO properly set raw mode, process escape keys

impl RawReader for FuchsiaRawReader {
    fn next_key(&mut self) -> Result<KeyPress> {
        let c = try!(self.next_char());

        let mut key = consts::char_to_key_press(c);
        if key == KeyPress::Esc {
            // TODO
            debug!(target: "rustyline", "ESC + {:?} currently unsupported", key);
        }

        Ok(key)
    }

    fn next_char(&mut self) -> Result<char> {
        match self.chars.next() {
            Some(ch) => Ok(ch?),
            None => Err(error::ReadlineError::Eof),
        }
    }
}

impl Term for Console {
    type Reader = FuchsiaRawReader;
    type Writer = Stdout;
    type Mode = Mode;

    fn new() -> Console {
        let stdin_isatty = true;
        Console {
            stdin_isatty: stdin_isatty,
        }
    }

    /// Checking for an unsupported TERM in fuchsia is a no-op
    fn is_unsupported(&self) -> bool {
        false
    }

    fn is_stdin_tty(&self) -> bool {
        self.stdin_isatty
    }

    /// Try to get the number of columns in the current terminal,
    /// or assume 80 if it fails.
    fn get_columns(&self) -> usize {
        let (cols, _) = get_win_size();
        cols
    }

    /// Try to get the number of rows in the current terminal,
    /// or assume 24 if it fails.
    fn get_rows(&self) -> usize {
        let (_, rows) = get_win_size();
        rows
    }

    /// Enable RAW mode for the terminal. No termios support, so this fakes it
    fn enable_raw_mode(&self) -> Result<Mode> {
        Ok(Mode {})
    }

    fn create_reader(&self, _: &Config) -> Result<FuchsiaRawReader> {
        FuchsiaRawReader::new()
    }

    fn create_writer(&self) -> Stdout {
        io::stdout()
    }

    fn sigwinch(&self) -> bool {
        false
    }

    /// Clear the screen. Used to handle ctrl+l
    fn clear_screen(&mut self, w: &mut Write) -> Result<()> {
        try!(w.write_all(b"\x1b[H\x1b[2J"));
        try!(w.flush());
        Ok(())
    }
}
<commit_msg>Update fuchsia tty to work with latest<commit_after>//! Fuchsia specific definitions
use std::io::{self, Read, Stdout, Write};
use std::mem;
use std::sync::atomic;
use libc;
use unicode_segmentation::UnicodeSegmentation;
use utf8parse::{Parser, Receiver};

use config::{ColorMode, Config, OutputStreamType};
use highlight::Highlighter;
use keys::{self, KeyPress};
use line_buffer::LineBuffer;
use error;
use Result;
use super::{truncate, width, Position, RawMode, RawReader, Renderer, Term};
use fuchsia_device::pty;
use StdStream;

const STDIN_FILENO: libc::c_int = libc::STDIN_FILENO;
const STDOUT_FILENO: libc::c_int = libc::STDOUT_FILENO;
const STDERR_FILENO: libc::c_int = libc::STDERR_FILENO;

fn get_win_size() -> (usize, usize) {
    match pty::get_window_size() {
        Ok(size) => {
            (size.width as usize, size.height as usize)
        }
        _ => (80, 24),
    }
}

struct StdinRaw {}

impl Read for StdinRaw {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        loop {
            let res = unsafe {
                libc::read(
                    STDIN_FILENO as i32,
                    buf.as_mut_ptr() as *mut libc::c_void,
                    buf.len() as libc::size_t,
                )
            };
            if res == -1 {
                let error = io::Error::last_os_error();
                if error.kind() != io::ErrorKind::Interrupted {
                    return Err(error);
                }
            } else {
                return Ok(res as usize);
            }
        }
    }
}

pub type Mode = ConsoleMode;

#[derive(Clone, Copy, Debug)]
pub struct ConsoleMode {}

impl RawMode for Mode {
    /// RAW mode is never on w/ Fuchsia
    fn disable_raw_mode(&self) -> Result<()> {
        Ok(())
    }
}

pub type Terminal = FuchsiaTerminal;

#[derive(Clone, Debug)]
pub struct FuchsiaTerminal {
    unsupported: bool,
    stdin_isatty: bool,
    stdstream_isatty: bool,
    pub(crate) color_mode: ColorMode,
    stream_type: OutputStreamType,
}

struct Utf8 {
    c: Option<char>,
    valid: bool,
}

pub struct FuchsiaRawReader {
    stdin: StdinRaw,
    timeout_ms: i32,
    buf: [u8; 1],
    parser: Parser,
    receiver: Utf8,
}

impl FuchsiaRawReader {
    pub fn new(config: &Config) -> Result<FuchsiaRawReader> {
        Ok(FuchsiaRawReader {
            stdin: StdinRaw {},
            timeout_ms: config.keyseq_timeout(),
            buf: [0; 1],
            parser: Parser::new(),
            receiver: Utf8 {
                c: None,
                valid: true,
            }
        })
    }

    /// Handle ESC <seq1> sequences
    fn escape_sequence(&mut self) -> Result<KeyPress> {
        // Read the next byte representing the escape sequence.
        let seq1 = try!(self.next_char());
        if seq1 == '[' {
            // ESC [ sequences. (CSI)
            self.escape_csi()
        } else if seq1 == 'O' {
            // xterm
            // ESC O sequences. (SS3)
            self.escape_o()
        } else if seq1 == '\x1b' {
            // ESC ESC
            Ok(KeyPress::Esc)
        } else {
            // TODO ESC-R (r): Undo all changes made to this line.
            Ok(KeyPress::Meta(seq1))
        }
    }

    /// Handle ESC [ <seq2> escape sequences
    fn escape_csi(&mut self) -> Result<KeyPress> {
        let seq2 = try!(self.next_char());
        if seq2.is_digit(10) {
            match seq2 {
                '0' | '9' => {
                    debug!(target: "rustyline", "unsupported esc sequence: ESC [ {:?}", seq2);
                    Ok(KeyPress::UnknownEscSeq)
                }
                _ => {
                    // Extended escape, read additional byte.
                    self.extended_escape(seq2)
                }
            }
        } else if seq2 == '[' {
            let seq3 = try!(self.next_char());
            // Linux console
            Ok(match seq3 {
                'A' => KeyPress::F(1),
                'B' => KeyPress::F(2),
                'C' => KeyPress::F(3),
                'D' => KeyPress::F(4),
                'E' => KeyPress::F(5),
                _ => {
                    debug!(target: "rustyline", "unsupported esc sequence: ESC [ [ {:?}", seq3);
                    KeyPress::UnknownEscSeq
                }
            })
        } else {
            // ANSI
            Ok(match seq2 {
                'A' => KeyPress::Up,    // kcuu1
                'B' => KeyPress::Down,  // kcud1
                'C' => KeyPress::Right, // kcuf1
                'D' => KeyPress::Left,  // kcub1
                'F' => KeyPress::End,
                'H' => KeyPress::Home, // khome
                'Z' => KeyPress::BackTab,
                _ => {
                    debug!(target: "rustyline", "unsupported esc sequence: ESC [ {:?}", seq2);
                    KeyPress::UnknownEscSeq
                }
            })
        }
    }

    /// Handle ESC [ <seq2:digit> escape sequences
    fn extended_escape(&mut self, seq2: char) -> Result<KeyPress> {
        let seq3 = try!(self.next_char());
        if seq3 == '~' {
            Ok(match seq2 {
                '1' | '7' => KeyPress::Home, // tmux, xrvt
                '2' => KeyPress::Insert,
                '3' => KeyPress::Delete,    // kdch1
                '4' | '8' => KeyPress::End, // tmux, xrvt
                '5' => KeyPress::PageUp,    // kpp
                '6' => KeyPress::PageDown,  // knp
                _ => {
                    debug!(target: "rustyline",
                           "unsupported esc sequence: ESC [ {} ~", seq2);
                    KeyPress::UnknownEscSeq
                }
            })
        } else if seq3.is_digit(10) {
            let seq4 = try!(self.next_char());
            if seq4 == '~' {
                Ok(match (seq2, seq3) {
                    ('1', '1') => KeyPress::F(1),  // rxvt-unicode
                    ('1', '2') => KeyPress::F(2),  // rxvt-unicode
                    ('1', '3') => KeyPress::F(3),  // rxvt-unicode
                    ('1', '4') => KeyPress::F(4),  // rxvt-unicode
                    ('1', '5') => KeyPress::F(5),  // kf5
                    ('1', '7') => KeyPress::F(6),  // kf6
                    ('1', '8') => KeyPress::F(7),  // kf7
                    ('1', '9') => KeyPress::F(8),  // kf8
                    ('2', '0') => KeyPress::F(9),  // kf9
                    ('2', '1') => KeyPress::F(10), // kf10
                    ('2', '3') => KeyPress::F(11), // kf11
                    ('2', '4') => KeyPress::F(12), // kf12
                    _ => {
                        debug!(target: "rustyline",
                               "unsupported esc sequence: ESC [ {}{} ~", seq2, seq3);
                        KeyPress::UnknownEscSeq
                    }
                })
            } else if seq4 == ';' {
                let seq5 = try!(self.next_char());
                if seq5.is_digit(10) {
                    let seq6 = try!(self.next_char()); // '~' expected
                    debug!(target: "rustyline",
                           "unsupported esc sequence: ESC [ {}{} ; {} {}", seq2, seq3, seq5, seq6);
                } else {
                    debug!(target: "rustyline",
                           "unsupported esc sequence: ESC [ {}{} ; {:?}", seq2, seq3, seq5);
                }
                Ok(KeyPress::UnknownEscSeq)
            } else {
                debug!(target: "rustyline",
                       "unsupported esc sequence: ESC [ {}{} {:?}", seq2, seq3, seq4);
                Ok(KeyPress::UnknownEscSeq)
            }
        } else if seq3 == ';' {
            let seq4 = try!(self.next_char());
            if seq4.is_digit(10) {
                let seq5 = try!(self.next_char());
                if seq2 == '1' {
                    Ok(match (seq4, seq5) {
                        ('5', 'A') => KeyPress::ControlUp,
                        ('5', 'B') => KeyPress::ControlDown,
                        ('5', 'C') => KeyPress::ControlRight,
                        ('5', 'D') => KeyPress::ControlLeft,
                        ('2', 'A') => KeyPress::ShiftUp,
                        ('2', 'B') => KeyPress::ShiftDown,
                        ('2', 'C') => KeyPress::ShiftRight,
                        ('2', 'D') => KeyPress::ShiftLeft,
                        _ => {
                            debug!(target: "rustyline",
                                   "unsupported esc sequence: ESC [ 1 ; {} {:?}", seq4, seq5);
                            KeyPress::UnknownEscSeq
                        }
                    })
                } else {
                    debug!(target: "rustyline",
                           "unsupported esc sequence: ESC [ {} ; {} {:?}", seq2, seq4, seq5);
                    Ok(KeyPress::UnknownEscSeq)
                }
            } else {
                debug!(target: "rustyline",
                       "unsupported esc sequence: ESC [ {} ; {:?}", seq2, seq4);
                Ok(KeyPress::UnknownEscSeq)
            }
        } else {
            Ok(match (seq2, seq3) {
                ('5', 'A') => KeyPress::ControlUp,
                ('5', 'B') => KeyPress::ControlDown,
                ('5', 'C') => KeyPress::ControlRight,
                ('5', 'D') => KeyPress::ControlLeft,
                _ => {
                    debug!(target: "rustyline",
                           "unsupported esc sequence: ESC [ {} {:?}", seq2, seq3);
                    KeyPress::UnknownEscSeq
                }
            })
        }
    }

    /// Handle ESC O <seq2> escape sequences
    fn escape_o(&mut self) -> Result<KeyPress> {
        let seq2 = try!(self.next_char());
        Ok(match seq2 {
            'A' => KeyPress::Up,    // kcuu1
            'B' => KeyPress::Down,  // kcud1
            'C' => KeyPress::Right, // kcuf1
            'D' => KeyPress::Left,  // kcub1
            'F' => KeyPress::End,   // kend
            'H' => KeyPress::Home,  // khome
            'P' => KeyPress::F(1),  // kf1
            'Q' => KeyPress::F(2),  // kf2
            'R' => KeyPress::F(3),  // kf3
            'S' => KeyPress::F(4),  // kf4
            'a' => KeyPress::ControlUp,
            'b' => KeyPress::ControlDown,
            'c' => KeyPress::ControlRight, // rxvt
            'd' => KeyPress::ControlLeft,  // rxvt
            _ => {
                debug!(target: "rustyline", "unsupported esc sequence: ESC O {:?}", seq2);
                KeyPress::UnknownEscSeq
            }
        })
    }
}

pub struct FuchsiaRenderer {
    out: StdStream,
    cols: usize,
    buffer: String,
}

impl FuchsiaRenderer {
    pub fn new(stream_type: OutputStreamType) -> FuchsiaRenderer {
        let out = StdStream::from_stream_type(stream_type);
        let (cols, _) = get_win_size();
        FuchsiaRenderer {
            out,
            cols,
            buffer: String::with_capacity(1024),
        }
    }
}

impl Renderer for FuchsiaRenderer {
    fn move_cursor(&mut self, old: Position, new: Position) -> Result<()> {
        use std::fmt::Write;
        let mut ab = String::new();
        if new.row > old.row {
            // move down
            let row_shift = new.row - old.row;
            if row_shift == 1 {
                ab.push_str("\x1b[B");
            } else {
                write!(ab, "\x1b[{}B", row_shift).unwrap();
            }
        } else if new.row < old.row {
            // move up
            let row_shift = old.row - new.row;
            if row_shift == 1 {
                ab.push_str("\x1b[A");
            } else {
                write!(ab, "\x1b[{}A", row_shift).unwrap();
            }
        }
        if new.col > old.col {
            // move right
            let col_shift = new.col - old.col;
            if col_shift == 1 {
                ab.push_str("\x1b[C");
            } else {
                write!(ab, "\x1b[{}C", col_shift).unwrap();
            }
        } else if new.col < old.col {
            // move left
            let col_shift = old.col - new.col;
            if col_shift == 1 {
                ab.push_str("\x1b[D");
            } else {
                write!(ab, "\x1b[{}D", col_shift).unwrap();
            }
        }
        self.write_and_flush(ab.as_bytes())
    }

    /// Display `prompt`, line and cursor in terminal output
    fn refresh_line(
        &mut self,
        prompt: &str,
        prompt_size: Position,
        line: &LineBuffer,
        hint: Option<String>,
        current_row: usize,
        old_rows: usize,
        highlighter: Option<&Highlighter>,
    ) -> Result<(Position, Position)> {
        use std::fmt::Write;
        self.buffer.clear();

        // calculate the position of the end of the input line
        let end_pos = self.calculate_position(line, prompt_size);
        // calculate the desired position of the cursor
        let cursor = self.calculate_position(&line[..line.pos()], prompt_size);

        // self.old_rows < self.cursor.row if the prompt spans multiple lines and if
        // this is the default State.
        let cursor_row_movement = old_rows.checked_sub(current_row).unwrap_or(0);
        // move the cursor down as required
        if cursor_row_movement > 0 {
            write!(self.buffer, "\x1b[{}B", cursor_row_movement).unwrap();
        }
        // clear old rows
        for _ in 0..old_rows {
            self.buffer.push_str("\r\x1b[0K\x1b[A");
        }
        // clear the line
        self.buffer.push_str("\r\x1b[0K");

        if let Some(highlighter) = highlighter {
            // display the prompt
            self.buffer.push_str(&highlighter.highlight_prompt(prompt));
            // display the input line
            self.buffer
                .push_str(&highlighter.highlight(line, line.pos()));
        } else {
            // display the prompt
            self.buffer.push_str(prompt);
            // display the input line
            self.buffer.push_str(line);
        }
        // display hint
        if let Some(hint) = hint {
            let truncate = truncate(&hint, end_pos.col, self.cols);
            if let Some(highlighter) = highlighter {
                self.buffer.push_str(&highlighter.highlight_hint(truncate));
            } else {
                self.buffer.push_str(truncate);
            }
        }
        // we have to generate our own newline on line wrap
        if end_pos.col == 0 && end_pos.row > 0 {
            self.buffer.push_str("\n");
        }
        // position the cursor
        let cursor_row_movement = end_pos.row - cursor.row;
        // move the cursor up as required
        if cursor_row_movement > 0 {
            write!(self.buffer, "\x1b[{}A", cursor_row_movement).unwrap();
        }
        // position the cursor within the line
        if cursor.col > 0 {
            write!(self.buffer, "\r\x1b[{}C", cursor.col).unwrap();
        } else {
            self.buffer.push('\r');
        }

        try!(self.out.write_all(self.buffer.as_bytes()));
        try!(self.out.flush());
        Ok((cursor, end_pos))
    }

    /// Calculate the number of columns and rows used to display `s` on a
    /// `cols` width terminal starting at `orig`.
    fn calculate_position(&self, s: &str, orig: Position) -> Position {
        let mut pos = orig;
        let mut esc_seq = 0;
        for c in s.graphemes(true) {
            if c == "\n" {
                pos.row += 1;
                pos.col = 0;
                continue;
            }
            let cw = width(c, &mut esc_seq);
            pos.col += cw;
            if pos.col > self.cols {
                pos.row += 1;
                pos.col = cw;
            }
        }
        if pos.col == self.cols {
            pos.col = 0;
            pos.row += 1;
        }
        pos
    }

    fn write_and_flush(&mut self, buf: &[u8]) -> Result<()> {
        try!(self.out.write_all(buf));
        try!(self.out.flush());
        Ok(())
    }

    /// Clear the screen. Used to handle ctrl+l
    fn clear_screen(&mut self) -> Result<()> {
        self.write_and_flush(b"\x1b[H\x1b[2J")
    }

    /// Check if a SIGWINCH signal has been received
    fn sigwinch(&self) -> bool {
        false
    }

    /// Update the number of columns/rows in the current terminal.
    fn update_size(&mut self) {
        let (cols, _) = get_win_size();
        self.cols = cols;
    }

    /// Get the number of columns in the current terminal.
    fn get_columns(&self) -> usize {
        let (cols, _) = get_win_size();
        cols
    }

    /// Get the number of rows in the current terminal.
    fn get_rows(&self) -> usize {
        let (_, rows) = get_win_size();
        rows
    }
}

// TODO properly set raw mode, handle escape key timeouts

impl RawReader for FuchsiaRawReader {
    fn next_key(&mut self, single_esc_abort: bool) -> Result<KeyPress> {
        let c = try!(self.next_char());

        let mut key = keys::char_to_key_press(c);
        if key == KeyPress::Esc {
            return self.escape_sequence();
        }

        Ok(key)
    }

    fn next_char(&mut self) -> Result<char> {
        loop {
            let n = try!(self.stdin.read(&mut self.buf));
            if n == 0 {
                return Err(error::ReadlineError::Eof);
            }
            let b = self.buf[0];
            self.parser.advance(&mut self.receiver, b);
            if !self.receiver.valid {
                return Err(error::ReadlineError::Utf8Error);
            } else if self.receiver.c.is_some() {
                return Ok(self.receiver.c.take().unwrap());
            }
        }
    }
}

impl Receiver for Utf8 {
    /// Called whenever a codepoint is parsed successfully
    fn codepoint(&mut self, c: char) {
        self.c = Some(c);
        self.valid = true;
    }

    /// Called when an invalid_sequence is detected
    fn invalid_sequence(&mut self) {
        self.c = None;
        self.valid = false;
    }
}

fn is_a_tty(fd: libc::c_int) -> bool {
    unsafe { libc::isatty(fd) != 0 }
}

impl Term for FuchsiaTerminal {
    type Reader = FuchsiaRawReader;
    type Writer = FuchsiaRenderer;
    type Mode = Mode;

    fn new(color_mode: ColorMode, stream_type: OutputStreamType) -> FuchsiaTerminal {
        FuchsiaTerminal {
            unsupported: false,
            stdin_isatty: is_a_tty(STDIN_FILENO as i32),
            stdstream_isatty: is_a_tty(if stream_type == OutputStreamType::Stdout {
                STDOUT_FILENO as i32
            } else {
                STDERR_FILENO as i32
            }),
            color_mode,
            stream_type,
        }
    }
    /// Check if current terminal can provide a rich line-editing user
    /// interface.
    fn is_unsupported(&self) -> bool {
        false
    }
    /// check if stdin is connected to a terminal.
    fn is_stdin_tty(&self) -> bool {
        self.stdin_isatty
    }
    /// Check if output supports colors.
    fn colors_enabled(&self) -> bool {
        true
    }
    /// Enable RAW mode for the terminal.
    fn enable_raw_mode(&mut self) -> Result<Self::Mode> {
        Ok(Mode {})
    }
    /// Create a RAW reader
    fn create_reader(&self, config: &Config) -> Result<Self::Reader> {
        FuchsiaRawReader::new(config)
    }
    /// Create a writer
    fn create_writer(&self) -> Self::Writer {
        FuchsiaRenderer::new(self.stream_type)
    }
}
<|endoftext|>


<commit_before>// Copyright 2014 The Servo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

extern crate ffi;

pub use self::ffi::*;

use libc;
use std::ptr;

pub static EINA_FALSE: Eina_Bool = 0;
pub static EINA_TRUE: Eina_Bool = 1;

/// Convert from a Rust boolean to an `Eina_Bool`.
pub fn to_eina_bool(x: bool) -> Eina_Bool {
    if x { EINA_TRUE } else { EINA_FALSE }
}

/// Convert an `Eina_Bool` to a Rust boolean.
pub fn from_eina_bool(x: Eina_Bool) -> bool {
    if x == EINA_FALSE { false } else { true }
}

pub fn eina_list_iter(list: *const Eina_List) -> EinaListItems {
    EinaListItems {
        iter: unsafe { eina_list_iterator_new(list) },
    }
}

pub struct EinaListItems {
    iter: *mut Eina_Iterator,
}

impl Iterator<*mut libc::c_void> for EinaListItems {
    fn next(&mut self) -> Option<*mut libc::c_void> {
        let mut data = ptr::mut_null();
        match unsafe { eina_iterator_next(self.iter, &mut data) } {
            EINA_TRUE => Some(data),
            _ => None,
        }
    }
}
<commit_msg>Add a destructor to the EinaListItems type<commit_after>// Copyright 2014 The Servo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

extern crate ffi;

pub use self::ffi::*;

use libc;
use std::ptr;

pub static EINA_FALSE: Eina_Bool = 0;
pub static EINA_TRUE: Eina_Bool = 1;

/// Convert from a Rust boolean to an `Eina_Bool`.
pub fn to_eina_bool(x: bool) -> Eina_Bool {
    if x { EINA_TRUE } else { EINA_FALSE }
}

/// Convert an `Eina_Bool` to a Rust boolean.
pub fn from_eina_bool(x: Eina_Bool) -> bool {
    if x == EINA_FALSE { false } else { true }
}

pub fn eina_list_iter(list: *const Eina_List) -> EinaListItems {
    EinaListItems {
        iter: unsafe { eina_list_iterator_new(list) },
    }
}

pub struct EinaListItems {
    iter: *mut Eina_Iterator,
}

impl Iterator<*mut libc::c_void> for EinaListItems {
    fn next(&mut self) -> Option<*mut libc::c_void> {
        let mut data = ptr::mut_null();
        match unsafe { eina_iterator_next(self.iter, &mut data) } {
            EINA_TRUE => Some(data),
            _ => None,
        }
    }
}

impl Drop for EinaListItems {
    fn drop(&mut self) {
        unsafe {
            eina_iterator_free(self.iter);
        }
    }
}
<|endoftext|>


<commit_before>use std::mem;

macro_rules! boxed {
    [$($x:expr),*] => (Box::new([$($x),*]));
}

macro_rules! collect {
    ($x:expr, $y:ty) => ($x.collect::<Vec<$y>>().into_boxed_slice());
}

type Element = Box<[usize]>;

pub struct Polytope<V> {
    dimensions: usize,
    vertices: Box<[V]>,
    elements: Box<[Box<[Element]>]>,
}

impl<V: Default> Polytope<V> {
    pub fn new() -> Self {
        Polytope::<V> {
            dimensions: 0,
            vertices: boxed![V::default()],
            elements: boxed![],
        }
    }
}

impl<V> Polytope<V> {
    pub fn extrude<F1, F2>(&self, pull_in: F1, push_out: F2) -> Self
            where F1: Fn(&V) -> V,
                  F2: Fn(&V) -> V {
        // Vertices are a special case: Create a promoted pair of each and linked the pairs.
        let mut new_vertices = Vec::<V>::new();
        let mut new_edges = Vec::<Element>::new();
        for v in self.vertices.iter() {
            // Pulled in and pushed out copies.
            let i = new_vertices.len();
            new_vertices.push(pull_in(v));
            let j = new_vertices.len();
            new_vertices.push(push_out(v));

            // Linkage element.
            new_edges.push(boxed![i, j]);
        }

        // For the rest of the dimensions, create a pair of each element and linked them via a
        // higher dimensional element.
        let mut new_elements = Vec::<Vec<Element>>::new();
        let mut new_elems_this = new_edges;
        let mut new_elems_next = Vec::<Element>::new();
        for elems_this in self.elements.iter() {
            for e in elems_this.iter() {
                // Pulled in and pushed out copies.
                let i = new_elems_this.len();
                new_elems_this.push(collect!(e.iter().map(|b| b * 2 + 0), usize));
                let j = new_elems_this.len();
                new_elems_this.push(collect!(e.iter().map(|b| b * 2 + 1), usize));

                // Linkage element.
                new_elems_next.push(collect!(e.iter().chain([i, j].iter()).cloned(), usize));
            }
            // Reference-safe way of doing
            //   new_elements.push(new_elems_this);
            //   new_elems_this = new_elems_next;
            //   new_elems_next = vec![];
            new_elements.push(
                mem::replace(&mut new_elems_this,
                             mem::replace(&mut new_elems_next, vec![])));
        }
        // Don't forget the final dimension.
        new_elements.push(new_elems_this);

        let new_vertices = new_vertices.into_boxed_slice();
        let new_elements = collect!(new_elements.drain(..).map(
            |mut elements_of_this_dimension|
            collect!(elements_of_this_dimension.drain(..), Element)
        ), Box<[Element]>);
        Polytope::<V> {
            dimensions: self.dimensions + 1,
            vertices: new_vertices,
            elements: new_elements,
        }
    }
}

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

    struct MyVertex {
        coords: Box<[f64]>,
    }

    impl Default for MyVertex {
        fn default() -> Self {
            MyVertex {
                coords: Box::new([]),
            }
        }
    }

    impl MyVertex {
        fn promote(&self, h: f64) -> Self {
            MyVertex {
                coords: collect!(self.coords.iter().chain([h].iter()).map(|&x| x), f64),
            }
        }
    }

    #[test]
    fn polytope_new() {
        let p = Polytope::<MyVertex>::new();
        assert_eq!(p.dimensions, 0);
        assert_eq!(p.vertices.len(), 1);
        assert_eq!(p.elements.len(), 0);
    }

    #[test]
    fn extrude_point() {
        let p = Polytope::<MyVertex>::new();
        let q = p.extrude(|v| v.promote(-1.0), |v| v.promote(1.0));
        assert_eq!(q.dimensions, 1);
        assert_eq!(q.vertices.len(), 2);
        assert!(q.vertices[0].coords == Box::new([-1.0]));
        assert!(q.vertices[1].coords == Box::new([ 1.0]));
        assert_eq!(q.elements.len(), 1);
        assert!(q.elements[0] == Box::new([Box::new([0, 1])]));
    }

    #[test]
    fn extrude_line() {
        let p = Polytope::<MyVertex>::new();
        let p = p.extrude(|v| v.promote(-1.0), |v| v.promote(1.0));
        let q = p.extrude(|v| v.promote(-2.0), |v| v.promote(2.0));
        assert_eq!(q.dimensions, 2);
        assert_eq!(q.vertices.len(), 4);
        assert!(q.vertices[0].coords == Box::new([-1.0, -2.0]));
        assert!(q.vertices[1].coords == Box::new([-1.0,  2.0]));
        assert!(q.vertices[2].coords == Box::new([ 1.0, -2.0]));
        assert!(q.vertices[3].coords == Box::new([ 1.0,  2.0]));
        assert_eq!(q.elements.len(), 2);
        assert!(q.elements[0] == Box::new([
            Box::new([0, 1]),
            Box::new([2, 3]),
            Box::new([0, 2]),
            Box::new([1, 3]),
        ]));
        assert!(q.elements[1] == Box::new([Box::new([0, 1, 2, 3])]));
    }
}
<commit_msg>Implement cone method.<commit_after>use std::mem;

macro_rules! boxed {
    [$($x:expr),*] => (Box::new([$($x),*]));
}

macro_rules! collect {
    ($x:expr, $y:ty) => ($x.collect::<Vec<$y>>().into_boxed_slice());
}

type Element = Box<[usize]>;

#[derive(Debug)]
pub struct Polytope<V> {
    dimensions: usize,
    vertices: Box<[V]>,
    elements: Box<[Box<[Element]>]>,
}

impl<V: Default> Polytope<V> {
    pub fn new() -> Self {
        Polytope::<V> {
            dimensions: 0,
            vertices: boxed![V::default()],
            elements: boxed![],
        }
    }
}

impl<V> Polytope<V> {
    pub fn extrude<F1, F2>(&self, pull_in: F1, push_out: F2) -> Self
            where F1: Fn(&V) -> V,
                  F2: Fn(&V) -> V {
        // Vertices are a special case: Create a promoted pair of each and linked the pairs.
        let mut new_vertices = Vec::<V>::new();
        let mut new_edges = Vec::<Element>::new();
        for v in self.vertices.iter() {
            // Pulled in and pushed out copies.
            let i = new_vertices.len();
            new_vertices.push(pull_in(v));
            let j = new_vertices.len();
            new_vertices.push(push_out(v));

            // Linkage element.
            new_edges.push(boxed![i, j]);
        }

        // For the rest of the dimensions, create a pair of each element and linked them via a
        // higher dimensional element.
        let mut linkage_offset = 0;
        let mut new_elements = Vec::<Vec<Element>>::new();
        let mut new_elems_this = new_edges;
        let mut new_elems_next = Vec::<Element>::new();
        for elems_this in self.elements.iter() {
            let next_linkage_offset = new_elems_this.len();
            for e in elems_this.iter() {
                // Pulled in and pushed out copies.
                let i = new_elems_this.len();
                new_elems_this.push(collect!(e.iter().map(|b| linkage_offset + b * 2 + 0), usize));
                let j = new_elems_this.len();
                new_elems_this.push(collect!(e.iter().map(|b| linkage_offset + b * 2 + 1), usize));

                // Linkage element.
                new_elems_next.push(collect!(e.iter().chain([i, j].iter()).cloned(), usize));
            }
            linkage_offset = next_linkage_offset;
            // Reference-safe way of doing
            //   new_elements.push(new_elems_this);
            //   new_elems_this = new_elems_next;
            //   new_elems_next = vec![];
            new_elements.push(
                mem::replace(&mut new_elems_this,
                             mem::replace(&mut new_elems_next, vec![])));
        }
        // Don't forget the final dimension.
        new_elements.push(new_elems_this);

        let new_vertices = new_vertices.into_boxed_slice();
        let new_elements = collect!(new_elements.drain(..).map(
            |mut elements_of_this_dimension|
            collect!(elements_of_this_dimension.drain(..), Element)
        ), Box<[Element]>);
        Polytope::<V> {
            dimensions: self.dimensions + 1,
            vertices: new_vertices,
            elements: new_elements,
        }
    }

    pub fn cone<F1, F2>(&self, tip: V, pull_in: F1, push_out: F2) -> Self
            where F1: Fn(&V) -> V,
                  F2: Fn(&V) -> V {
        // Create a promoted pair of each vertex and linked all of them to the tip.
        let mut new_vertices = Vec::<V>::new();
        let mut new_edges = Vec::<Element>::new();
        let tip_index = self.vertices.len() * 2;
        for v in self.vertices.iter() {
            // Pulled in and pushed out copies.
            let i = new_vertices.len();
            new_vertices.push(pull_in(v));
            let j = new_vertices.len();
            new_vertices.push(push_out(v));

            // Linkage elements.
            new_edges.push(boxed![i, tip_index]);
            new_edges.push(boxed![j, tip_index]);
        }
        new_vertices.push(tip);

        // For the rest of the dimensions, create a pair of each element and linked them to the tip.
        let mut new_elements = Vec::<Vec<Element>>::new();
        let mut new_elems_this = new_edges;
        let mut new_elems_next = Vec::<Element>::new();
        for elems_this in self.elements.iter() {
            for e in elems_this.iter() {
                // Pulled in and pushed out copies.
                let i = new_elems_this.len();
                new_elems_this.push(collect!(e.iter().map(|b| b * 2 + 0), usize));
                let j = new_elems_this.len();
                new_elems_this.push(collect!(e.iter().map(|b| b * 2 + 1), usize));

                // Linkage elements.
                new_elems_next.push(collect!(
                    e.iter().map(|b| b * 2 + 0).chain([i].iter().cloned()), usize));
                new_elems_next.push(collect!(
                    e.iter().map(|b| b * 2 + 1).chain([j].iter().cloned()), usize));
            }
            // Reference-safe way of doing
            //   new_elements.push(new_elems_this);
            //   new_elems_this = new_elems_next;
            //   new_elems_next = vec![];
            new_elements.push(
                mem::replace(&mut new_elems_this,
                             mem::replace(&mut new_elems_next, vec![])));
        }
        // Don't forget the final dimension.
        new_elements.push(new_elems_this);

        let new_vertices = new_vertices.into_boxed_slice();
        let new_elements = collect!(new_elements.drain(..).map(
            |mut elements_of_this_dimension|
            collect!(elements_of_this_dimension.drain(..), Element)
        ), Box<[Element]>);
        Polytope::<V> {
            dimensions: self.dimensions + 1,
            vertices: new_vertices,
            elements: new_elements,
        }
    }
}

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

    #[derive(Debug)]
    struct MyVertex {
        coords: Box<[f64]>,
    }

    impl Default for MyVertex {
        fn default() -> Self {
            MyVertex {
                coords: Box::new([]),
            }
        }
    }

    impl MyVertex {
        fn promote(&self, h: f64) -> Self {
            MyVertex {
                coords: collect!(self.coords.iter().chain([h].iter()).map(|&x| x), f64),
            }
        }
    }

    #[test]
    fn polytope_new() {
        let p = Polytope::<MyVertex>::new();
        assert_eq!(p.dimensions, 0);
        assert_eq!(p.vertices.len(), 1);
        assert_eq!(p.elements.len(), 0);
    }

    #[test]
    fn extrude_point() {
        let p = Polytope::<MyVertex>::new();
        let q = p.extrude(|v| v.promote(-1.0), |v| v.promote(1.0));
        assert_eq!(q.dimensions, 1);
        assert_eq!(q.vertices.len(), 2);
        assert!(q.vertices[0].coords == Box::new([-1.0]));
        assert!(q.vertices[1].coords == Box::new([ 1.0]));
        assert_eq!(q.elements.len(), 1);
        assert!(q.elements[0] == Box::new([Box::new([0, 1])]));
    }

    #[test]
    fn extrude_line() {
        let p = Polytope::<MyVertex>::new();
        let p = p.extrude(|v| v.promote(-1.0), |v| v.promote(1.0));
        let q = p.extrude(|v| v.promote(-2.0), |v| v.promote(2.0));
        assert_eq!(q.dimensions, 2);
        assert_eq!(q.vertices.len(), 4);
        assert!(q.vertices[0].coords == Box::new([-1.0, -2.0]));
        assert!(q.vertices[1].coords == Box::new([-1.0,  2.0]));
        assert!(q.vertices[2].coords == Box::new([ 1.0, -2.0]));
        assert!(q.vertices[3].coords == Box::new([ 1.0,  2.0]));
        assert_eq!(q.elements.len(), 2);
        assert!(q.elements[0] == Box::new([
            Box::new([0, 1]),
            Box::new([2, 3]),
            Box::new([0, 2]),
            Box::new([1, 3]),
        ]));
        assert!(q.elements[1] == Box::new([Box::new([0, 1, 2, 3])]));
    }

    #[test]
    fn cone_line() {
        let p = Polytope::<MyVertex>::new();
        let p = p.extrude(|v| v.promote(-1.0), |v| v.promote(1.0));
        let q = p.cone(MyVertex { coords: boxed![0.0, 0.0] },
                       |v| v.promote(-2.0), |v| v.promote(2.0));
        assert_eq!(q.dimensions, 2);
        assert_eq!(q.vertices.len(), 5);
        assert!(q.vertices[0].coords == Box::new([-1.0, -2.0]));
        assert!(q.vertices[1].coords == Box::new([-1.0,  2.0]));
        assert!(q.vertices[2].coords == Box::new([ 1.0, -2.0]));
        assert!(q.vertices[3].coords == Box::new([ 1.0,  2.0]));
        assert!(q.vertices[4].coords == Box::new([ 0.0,  0.0]));
        assert_eq!(q.elements.len(), 2);
        assert!(q.elements[0] == Box::new([
            Box::new([0, 4]),
            Box::new([1, 4]),
            Box::new([2, 4]),
            Box::new([3, 4]),
            Box::new([0, 2]),
            Box::new([1, 3]),
        ]));
        assert!(q.elements[1] == Box::new([
            Box::new([0, 2, 4]),
            Box::new([1, 3, 5]),
        ]));

        let r = q.extrude(|v| v.promote(-3.0), |v| v.promote(3.0));
    }
}
<|endoftext|>


<commit_before>#![crate_name = "env"]

/*
 * This file is part of the uutils coreutils package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/* last synced with: env (GNU coreutils) 8.13 */

#![allow(non_camel_case_types)]

use std::env;
use std::io::Write;
use std::process::Command;

#[path = "../common/util.rs"]
#[macro_use]
mod util;

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

struct options {
    ignore_env: bool,
    null: bool,
    unsets: Vec<String>,
    sets: Vec<(String, String)>,
    program: Vec<String>
}

fn usage() {
    println!("Usage: {} [OPTION]... [-] [NAME=VALUE]... [COMMAND [ARG]...]", NAME);
    println!("Set each NAME to VALUE in the environment and run COMMAND\n");
    println!("Possible options are:");
    println!("  -i --ignore-environment\t start with an empty environment");
    println!("  -0 --null              \t end each output line with a 0 byte rather than newline");
    println!("  -u --unset NAME        \t remove variable from the environment");
    println!("  -h --help              \t display this help and exit");
    println!("  -V --version           \t output version information and exit\n");
    println!("A mere - implies -i. If no COMMAND, print the resulting environment");
}

fn version() {
    println!("{} {}", NAME, VERSION);
}

// print name=value env pairs on screen
// if null is true, separate pairs with a \0, \n otherwise
fn print_env(null: bool) {
    for (n, v) in env::vars() {
        print!("{}={}{}", n, v, if null { '\0' } else { '\n' });
    }
}

pub fn uumain(args: Vec<String>) -> i32 {
    // to handle arguments the same way than GNU env, we can't use getopts
    let mut opts = Box::new(options {
        ignore_env: false,
        null: false,
        unsets: vec!(),
        sets: vec!(),
        program: vec!()
    });

    let mut wait_cmd = false;
    let mut iter = args.iter();
    iter.next(); // skip program
    let mut item = iter.next();

    // the for loop doesn't work here,
    // because we need sometines to read 2 items forward,
    // and the iter can't be borrowed twice
    while item != None {
        let opt = item.unwrap();

        if wait_cmd {
            // we still accept NAME=VAL here but not other options
            let mut sp = opt.splitn(2, '=');
            let name = sp.next();
            let value = sp.next();

            match (name, value) {
                (Some(n), Some(v)) => {
                    opts.sets.push((n.to_string(), v.to_string()));
                }
                _ => {
                    // read the program now
                    opts.program.push(opt.to_string());
                    break;
                }
            }
        } else if opt.starts_with("--") {
            match opt.as_ref() {
                "--help" => { usage(); return 0; }
                "--version" => { version(); return 0; }

                "--ignore-environment" => opts.ignore_env = true,
                "--null" => opts.null = true,
                "--unset" => {
                    let var = iter.next();

                    match var {
                        None => println!("{}: this option requires an argument: {}", NAME, opt),
                        Some(s) => opts.unsets.push(s.to_string())
                    }
                }

                _ => {
                    println!("{}: invalid option \"{}\"", NAME, *opt);
                    println!("Type \"{} --help\" for detailed informations", NAME);
                    return 1;
                }
            }
        } else if opt.starts_with("-") {
            if opt.len() == 0 {
                // implies -i and stop parsing opts
                wait_cmd = true;
                opts.ignore_env = true;
                continue;
            }

            let mut chars = opt.chars();
            chars.next();

            for c in chars {
                // short versions of options
                match c {
                    'h' => { usage(); return 0; }
                    'V' => { version(); return 0; }
                    'i' => opts.ignore_env = true,
                    '0' => opts.null = true,
                    'u' => {
                        let var = iter.next();

                        match var {
                            None => println!("{}: this option requires an argument: {}", NAME, opt),
                            Some(s) => opts.unsets.push(s.to_string())
                        }
                    }
                    _ => {
                        println!("{}: illegal option -- {}", NAME, c);
                        println!("Type \"{} --help\" for detailed informations", NAME);
                        return 1;
                    }
                }
            }
        } else {
            // is it a NAME=VALUE like opt ?
            let mut sp = opt.splitn(2, "=");
            let name = sp.next();
            let value = sp.next();

            match (name, value) {
                (Some(n), Some(v)) => {
                    // yes
                    opts.sets.push((n.to_string(), v.to_string()));
                    wait_cmd = true;
                }
                // no, its a program-like opt
                _ => {
                    opts.program.push(opt.to_string());
                    break;
                }
            }
        }

        item = iter.next();
    }

    // read program arguments
    for opt in iter {
        opts.program.push(opt.to_string());
    }

    if opts.ignore_env {
        for (ref name, _) in env::vars() {
            env::remove_var(name);
        }
    }

    for name in opts.unsets.iter() {
        env::remove_var(name);
    }

    for &(ref name, ref val) in opts.sets.iter() {
        env::set_var(name, val);
    }

    if opts.program.len() >= 1 {
        let prog = opts.program[0].clone();
        let args = &opts.program[1..];
        match Command::new(prog).args(args).status() {
            Ok(exit) => return if exit.success() { 0 } else { exit.code().unwrap() },
            Err(_) => return 1
        }
    } else {
        // no program provided
        print_env(opts.null);
        pipe_flush!();
    }

    0
}
<commit_msg>Fix 'env -' case<commit_after>#![crate_name = "env"]

/*
 * This file is part of the uutils coreutils package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

/* last synced with: env (GNU coreutils) 8.13 */

#![allow(non_camel_case_types)]

use std::env;
use std::io::Write;
use std::process::Command;

#[path = "../common/util.rs"]
#[macro_use]
mod util;

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

struct options {
    ignore_env: bool,
    null: bool,
    unsets: Vec<String>,
    sets: Vec<(String, String)>,
    program: Vec<String>
}

fn usage() {
    println!("Usage: {} [OPTION]... [-] [NAME=VALUE]... [COMMAND [ARG]...]", NAME);
    println!("Set each NAME to VALUE in the environment and run COMMAND\n");
    println!("Possible options are:");
    println!("  -i --ignore-environment\t start with an empty environment");
    println!("  -0 --null              \t end each output line with a 0 byte rather than newline");
    println!("  -u --unset NAME        \t remove variable from the environment");
    println!("  -h --help              \t display this help and exit");
    println!("  -V --version           \t output version information and exit\n");
    println!("A mere - implies -i. If no COMMAND, print the resulting environment");
}

fn version() {
    println!("{} {}", NAME, VERSION);
}

// print name=value env pairs on screen
// if null is true, separate pairs with a \0, \n otherwise
fn print_env(null: bool) {
    for (n, v) in env::vars() {
        print!("{}={}{}", n, v, if null { '\0' } else { '\n' });
    }
}

pub fn uumain(args: Vec<String>) -> i32 {
    // to handle arguments the same way than GNU env, we can't use getopts
    let mut opts = Box::new(options {
        ignore_env: false,
        null: false,
        unsets: vec!(),
        sets: vec!(),
        program: vec!()
    });

    let mut wait_cmd = false;
    let mut iter = args.iter();
    iter.next(); // skip program
    let mut item = iter.next();

    // the for loop doesn't work here,
    // because we need sometines to read 2 items forward,
    // and the iter can't be borrowed twice
    while item != None {
        let opt = item.unwrap();

        if wait_cmd {
            // we still accept NAME=VAL here but not other options
            let mut sp = opt.splitn(2, '=');
            let name = sp.next();
            let value = sp.next();

            match (name, value) {
                (Some(n), Some(v)) => {
                    opts.sets.push((n.to_string(), v.to_string()));
                }
                _ => {
                    // read the program now
                    opts.program.push(opt.to_string());
                    break;
                }
            }
        } else if opt.starts_with("--") {
            match opt.as_ref() {
                "--help" => { usage(); return 0; }
                "--version" => { version(); return 0; }

                "--ignore-environment" => opts.ignore_env = true,
                "--null" => opts.null = true,
                "--unset" => {
                    let var = iter.next();

                    match var {
                        None => println!("{}: this option requires an argument: {}", NAME, opt),
                        Some(s) => opts.unsets.push(s.to_string())
                    }
                }

                _ => {
                    println!("{}: invalid option \"{}\"", NAME, *opt);
                    println!("Type \"{} --help\" for detailed informations", NAME);
                    return 1;
                }
            }
        } else if opt.starts_with("-") {
            if opt.len() == 1 {
                // implies -i and stop parsing opts
                wait_cmd = true;
                opts.ignore_env = true;
                continue;
            }

            let mut chars = opt.chars();
            chars.next();

            for c in chars {
                // short versions of options
                match c {
                    'h' => { usage(); return 0; }
                    'V' => { version(); return 0; }
                    'i' => opts.ignore_env = true,
                    '0' => opts.null = true,
                    'u' => {
                        let var = iter.next();

                        match var {
                            None => println!("{}: this option requires an argument: {}", NAME, opt),
                            Some(s) => opts.unsets.push(s.to_string())
                        }
                    }
                    _ => {
                        println!("{}: illegal option -- {}", NAME, c);
                        println!("Type \"{} --help\" for detailed informations", NAME);
                        return 1;
                    }
                }
            }
        } else {
            // is it a NAME=VALUE like opt ?
            let mut sp = opt.splitn(2, "=");
            let name = sp.next();
            let value = sp.next();

            match (name, value) {
                (Some(n), Some(v)) => {
                    // yes
                    opts.sets.push((n.to_string(), v.to_string()));
                    wait_cmd = true;
                }
                // no, its a program-like opt
                _ => {
                    opts.program.push(opt.to_string());
                    break;
                }
            }
        }

        item = iter.next();
    }

    // read program arguments
    for opt in iter {
        opts.program.push(opt.to_string());
    }

    if opts.ignore_env {
        for (ref name, _) in env::vars() {
            env::remove_var(name);
        }
    }

    for name in opts.unsets.iter() {
        env::remove_var(name);
    }

    for &(ref name, ref val) in opts.sets.iter() {
        env::set_var(name, val);
    }

    if opts.program.len() >= 1 {
        let prog = opts.program[0].clone();
        let args = &opts.program[1..];
        match Command::new(prog).args(args).status() {
            Ok(exit) => return if exit.success() { 0 } else { exit.code().unwrap() },
            Err(_) => return 1
        }
    } else {
        // no program provided
        print_env(opts.null);
        pipe_flush!();
    }

    0
}
<|endoftext|>


<commit_before>mod enums;
mod structs;
mod functions;

pub use self::enums::*;
pub use self::structs::*;
pub use self::functions::*;
<commit_msg>Add comment<commit_after>mod enums;
mod structs;
mod functions;

// re-export to avoid another level of indirection
pub use self::enums::*;
pub use self::structs::*;
pub use self::functions::*;
<|endoftext|>


<commit_before>//! Utilities for scheduling work to happen after a period of time.
//!
//! This crate provides a number of utilities for working with periods of time:
//!
//! * [`Delay`]: A future that completes at a specified instant in time.
//!
//! * [`Interval`] A stream that yields at fixed time intervals.
//!
//! * [`Deadline`]: Wraps a future, requiring it to complete before a specified
//!   instant in time, erroring if the future takes too long.
//!
//! These three types are backed by a [`Timer`] instance. In order for
//! [`Delay`], [`Interval`], and [`Deadline`] to function, the associated
//! [`Timer`] instance must be running on some thread.
//!
//! [`Delay`]: struct.Delay.html
//! [`Deadline`]: struct.Deadline.html
//! [`Interval`]: struct.Interval.html
//! [`Timer`]: timer/struct.Timer.html

#![doc(html_root_url = "https://docs.rs/tokio-timer/0.2.0")]
#![deny(missing_docs, warnings, missing_debug_implementations)]

extern crate tokio_executor;

#[macro_use]
extern crate futures;

pub mod timer;

mod atomic;
mod deadline;
mod delay;
mod error;
mod interval;

pub use self::deadline::{Deadline, DeadlineError};
pub use self::delay::Delay;
pub use self::error::Error;
pub use self::interval::Interval;
pub use self::timer::{Timer, with_default};
<commit_msg>timer: add sleep free function (#347)<commit_after>//! Utilities for scheduling work to happen after a period of time.
//!
//! This crate provides a number of utilities for working with periods of time:
//!
//! * [`Delay`]: A future that completes at a specified instant in time.
//!
//! * [`Interval`] A stream that yields at fixed time intervals.
//!
//! * [`Deadline`]: Wraps a future, requiring it to complete before a specified
//!   instant in time, erroring if the future takes too long.
//!
//! These three types are backed by a [`Timer`] instance. In order for
//! [`Delay`], [`Interval`], and [`Deadline`] to function, the associated
//! [`Timer`] instance must be running on some thread.
//!
//! [`Delay`]: struct.Delay.html
//! [`Deadline`]: struct.Deadline.html
//! [`Interval`]: struct.Interval.html
//! [`Timer`]: timer/struct.Timer.html

#![doc(html_root_url = "https://docs.rs/tokio-timer/0.2.0")]
#![deny(missing_docs, warnings, missing_debug_implementations)]

extern crate tokio_executor;

#[macro_use]
extern crate futures;

pub mod timer;

mod atomic;
mod deadline;
mod delay;
mod error;
mod interval;

use std::time::{Duration, Instant};

pub use self::deadline::{Deadline, DeadlineError};
pub use self::delay::Delay;
pub use self::error::Error;
pub use self::interval::Interval;
pub use self::timer::{with_default, Timer};

/// Create a Future that completes in `duration` from now.
pub fn sleep(duration: Duration) -> Delay {
    Delay::new(Instant::now() + duration)
}
<|endoftext|>


<commit_before>use crate::errors::OscError;

use alloc::string::{String, ToString};
use alloc::vec::Vec;
use nom::branch::alt;
use nom::bytes::complete::{is_a, is_not, tag, take, take_while1};
use nom::character::complete::{char, satisfy};
use nom::combinator::{all_consuming, complete, opt, recognize, verify};
use nom::error::{ErrorKind, ParseError};
use nom::multi::{many1, separated_list1};
use nom::sequence::{delimited, pair, separated_pair};
use nom::{IResult, Parser};

/// With a Matcher OSC method addresses can be [matched](Matcher::match_address) against an OSC address pattern.
/// Refer to the OSC specification for details about OSC address spaces: <http://opensoundcontrol.org/spec-1_0.html#osc-address-spaces-and-osc-addresses>
#[derive(Debug)]
pub struct Matcher {
    pub pattern: String,
    pattern_parts: Vec<AddressPatternComponent>,
}

impl Matcher {
    /// Instantiates a new `Matcher` with the given address pattern.
    /// An error will be returned if the given address pattern is invalid.
    ///
    /// Matcher should be instantiated once per pattern and reused because its construction requires parsing the address pattern which is computationally expensive.
    ///
    /// A valid address pattern begins with a `/` and contains at least a method name, e.g. `/tempo`.
    /// OSC defines a couple of rules that look like regular expression but are subtly different:
    ///
    /// - `?` matches a single character
    /// - `*` matches zero or more characters
    /// - `[a-z]` are basically regex [character classes](https://www.regular-expressions.info/charclass.html)
    /// - `{foo,bar}` is an alternative, matching either `foo` or `bar`
    /// - everything else is matched literally
    ///
    /// Refer to the OSC specification for details about address pattern matching: <osc-message-dispatching-and-pattern-matching>.
    ///
    /// # Examples
    ///
    /// ```
    /// use rosc::address::Matcher;
    ///
    /// Matcher::new("/tempo").expect("valid address");
    /// Matcher::new("").expect_err("address does not start with a slash");
    /// ```
    pub fn new(pattern: &str) -> Result<Self, OscError> {
        verify_address_pattern(pattern)?;
        let pattern_parts = match all_consuming(many1(map_address_pattern_component))(pattern) {
            Ok((_, parts)) => parts,
            // This should never happen because pattern is verified above
            Err(_) => panic!("Address pattern must be valid"),
        };

        Ok(Matcher {
            pattern: pattern.into(),
            pattern_parts,
        })
    }

    /// Match an OSC address against an address pattern.
    /// If the address matches the pattern the result will be `true`, otherwise `false`.
    /// An error is returned if the given OSC address is not valid.
    ///
    /// A valid OSC address begins with a `/` and contains at least a method name, e.g. `/tempo`.
    /// Despite OSC address patterns a plain address must not include any of the following characters `#*,/?[]{}`.
    ///
    /// # Examples
    ///
    /// ```
    /// use rosc::address::Matcher;
    ///
    /// let matcher = Matcher::new("/oscillator/[0-9]/{frequency,phase}").unwrap();
    /// assert!(matcher.match_address("/oscillator/1/frequency").unwrap());
    /// assert!(matcher.match_address("/oscillator/8/phase").unwrap());
    /// assert_eq!(matcher.match_address("/oscillator/4/detune").unwrap(), false);
    /// ```
    pub fn match_address(&self, address: &str) -> Result<bool, OscError> {
        verify_address(address)?; // TODO: Create an address struct so we don't have to re-check addresses every time we match

        // Trivial case
        if address == self.pattern {
            return Ok(true);
        }
        let mut remainder: &str = address;
        // Match the the address component by component
        for (index, part) in self.pattern_parts.as_slice().iter().enumerate() {
            let result = match part {
                AddressPatternComponent::Tag(s) => match_literally(remainder, s.as_str()),
                AddressPatternComponent::WildcardSingle => match_wildcard_single(remainder),
                AddressPatternComponent::Wildcard(l) => {
                    // Check if this is the last pattern component
                    if index < self.pattern_parts.len() - 1 {
                        let next = &self.pattern_parts[index + 1];
                        match next {
                            // If the next component is a '/', there are no more components in the current part and it can be wholly consumed
                            AddressPatternComponent::Tag(s) if s == "/" => {
                                match_wildcard(remainder, *l, None)
                            }
                            _ => match_wildcard(remainder, *l, Some(next)),
                        }
                    } else {
                        match_wildcard(remainder, *l, None)
                    }
                }
                AddressPatternComponent::CharacterClass(cc) => match_character_class(remainder, cc),
                AddressPatternComponent::Choice(s) => match_choice(remainder, s),
            };

            match result {
                Ok((i, _)) => remainder = i,
                Err(_) => return Ok(false), // Component didn't match, goodbye
            };
        }

        // Address is only matched if it was consumed entirely
        if remainder.is_empty() {
            Ok(true)
        } else {
            Ok(false)
        }
    }
}

/// Check whether a character is an allowed address character
/// All printable ASCII characters except for a few special characters are allowed
fn is_address_character(x: char) -> bool {
    match x {
        ' ' | '#' | '*' | ',' | '/' | '?' | '[' | ']' | '{' | '}' => false,
        c => c > '\x20' && c < '\x7F',
    }
}

/// Parser to turn a choice like '{foo,bar}' into a vector containing the choices, like ["foo", "bar"]
fn pattern_choice(input: &str) -> IResult<&str, Vec<&str>> {
    delimited(
        char('{'),
        separated_list1(tag(","), take_while1(is_address_character)),
        char('}'),
    )(input)
}

/// Parser to recognize a character class like [!a-zA-Z] and return '!a-zA-Z'
fn pattern_character_class(input: &str) -> IResult<&str, &str> {
    let inner = pair(
        // It is important to read the leading negating '!' to make sure the rest of the parsed
        // character class isn't empty. E.g. '[!]' is not a valid character class.
        recognize(opt(tag("!"))),
        take_while1(is_address_character),
    );

    delimited(char('['), recognize(inner), char(']'))(input)
}

/// A characters class is defined by a set or range of characters that it matches.
/// For example, [a-z] matches all lowercase alphabetic letters. It can also contain multiple
/// ranges, like [a-zA-Z]. Instead of a range you can also directly provide the characters to match,
/// e.g. [abc123]. You can also combine this with ranges, like [a-z123].
/// If the first characters is an exclamation point, the match is negated, e.g. [!0-9] will match
/// anything except numbers.
#[derive(Debug)]
struct CharacterClass {
    pub negated: bool,
    pub characters: String,
}

/// Expand a character range like 'a-d' to all the letters contained in the range, e.g. 'abcd'
/// This is done by converting the characters to their ASCII values and then getting every ASCII
/// in between.
fn expand_character_range(first: char, second: char) -> String {
    let start = first as u8;
    let end = second as u8;

    let range = if start >= end {
        end..=start
    } else {
        start..=end
    };

    let mut out = String::from("");
    for c in range {
        // For funky ranges like [0-a], some illegal characters are contained
        if is_address_character(c as char) {
            out.push(c as char);
        }
    }
    out
}

/// Removes all duplicates from a string of characters
fn deduplicate_characters(input: String) -> String {
    let mut out: String = String::from("");
    for char in input.chars() {
        if !out.contains(char) {
            out.push(char);
        }
    }
    out
}

impl CharacterClass {
    pub fn new(s: &str) -> Self {
        let mut input = s;
        let negated;
        match char::<_, nom::error::Error<&str>>('!')(input) {
            Ok((i, _)) => {
                negated = true;
                input = i;
            }
            Err(_) => negated = false,
        }

        let characters = complete(many1(alt((
            // '!' besides at beginning has no special meaning, but is legal
            char::<_, nom::error::Error<&str>>('!').map(|_| String::from("")),
            // attempt to match a range like a-z or 0-9
            separated_pair(
                satisfy(is_address_character),
                char('-'),
                satisfy(is_address_character),
            )
            .map(|(first, second)| expand_character_range(first, second)),
            // Match characters literally
            satisfy(is_address_character).map(|x| x.to_string()),
            // Trailing dash
            char('-').map(|_| String::from("-")),
        ))))(input);

        match characters {
            Ok((_, o)) => CharacterClass {
                negated,
                characters: deduplicate_characters(o.concat()),
            },
            _ => {
                panic!("Invalid character class formatting {}", s)
            }
        }
    }
}

#[derive(Debug)]
enum AddressPatternComponent {
    Tag(String),
    Wildcard(usize),
    WildcardSingle,
    CharacterClass(CharacterClass),
    Choice(Vec<String>),
}

fn map_address_pattern_component(input: &str) -> IResult<&str, AddressPatternComponent> {
    alt((
        // Anything that's alphanumeric gets matched literally
        take_while1(is_address_character)
            .map(|s: &str| AddressPatternComponent::Tag(String::from(s))),
        // Slashes must be seperated into their own tag for the non-greedy implementation of wildcards
        char('/').map(|c: char| AddressPatternComponent::Tag(c.to_string())),
        tag("?").map(|_| AddressPatternComponent::WildcardSingle),
        // Combinations of wildcards are a bit tricky.
        // Multiple '*' wildcards in a row are equal to a single '*'.
        // A '*' wildcard followed by any number of '?' wildcards is also equal to '*' but must
        // match at least the same amount of characters as there are '?' wildcards in the combination.
        // For example, '*??' must match at least 2 characters.
        is_a("*?").map(|x: &str| AddressPatternComponent::Wildcard(x.matches('?').count())),
        pattern_choice.map(|choices: Vec<&str>| {
            AddressPatternComponent::Choice(choices.iter().map(|x| x.to_string()).collect())
        }),
        pattern_character_class
            .map(|s: &str| AddressPatternComponent::CharacterClass(CharacterClass::new(s))),
    ))(input)
}

fn match_literally<'a>(input: &'a str, pattern: &str) -> IResult<&'a str, &'a str> {
    tag(pattern)(input)
}

fn match_wildcard_single(input: &str) -> IResult<&str, &str> {
    // TODO: this has to be possible with a simpler parser?
    verify(take(1usize), |s: &str| s.chars().all(is_address_character))(input)
}

fn match_character_class<'a>(
    input: &'a str,
    character_class: &'a CharacterClass,
) -> IResult<&'a str, &'a str> {
    if character_class.negated {
        is_not(character_class.characters.as_str())(input)
    } else {
        is_a(character_class.characters.as_str())(input)
    }
}

/// Try all the tags in a choice element
/// Example choice element: '{foo,bar}'
/// It will get parsed into a vector containing the strings "foo" and "bar", which are then matched
fn match_choice<'a>(input: &'a str, choices: &[String]) -> IResult<&'a str, &'a str> {
    for choice in choices {
        if let Ok((i, o)) = tag::<_, _, nom::error::Error<&str>>(choice.as_str())(input) {
            return Ok((i, o));
        }
    }
    Err(nom::Err::Error(nom::error::Error::from_error_kind(
        input,
        ErrorKind::Tag,
    )))
}

/// Match Wildcard '*' by either consuming the rest of the part, or, if it's not the last component
/// in the part, by looking ahead and matching the next component
fn match_wildcard<'a>(
    input: &'a str,
    minimum_length: usize,
    next: Option<&AddressPatternComponent>,
) -> IResult<&'a str, &'a str> {
    match next {
        // No next component, consume all allowed characters until end or next '/'
        None => verify(take_while1(is_address_character), |s: &str| {
            s.len() >= minimum_length
        })(input),
        // There is another element in this part, so logic gets a bit more complicated
        Some(component) => {
            // Wildcards can only match within the current address part, discard the rest
            let address_part = match input.split_once('/') {
                Some((p, _)) => p,
                None => input,
            };

            // Attempt to find the latest matching occurrence of the next pattern component
            // This is a greedy wildcard implementation
            let mut longest: usize = 0;
            for i in 0..address_part.len() {
                let (_, substring) = input.split_at(i);
                let result: IResult<_, _, nom::error::Error<&str>> = match component {
                    AddressPatternComponent::Tag(s) => match_literally(substring, s.as_str()),
                    AddressPatternComponent::CharacterClass(cc) => {
                        match_character_class(substring, cc)
                    }
                    AddressPatternComponent::Choice(s) => match_choice(substring, s),
                    // These two cases are prevented from happening by map_address_pattern_component
                    AddressPatternComponent::WildcardSingle => {
                        panic!("Single wildcard ('?') must not follow wildcard ('*')")
                    }
                    AddressPatternComponent::Wildcard(_) => {
                        panic!("Double wildcards must be condensed into one")
                    }
                };

                if result.is_ok() {
                    longest = i
                }
            }
            verify(take(longest), |s: &str| s.len() >= minimum_length)(input)
        }
    }
}

/// Verify that an address is valid
///
/// # Examples
/// ```
/// use rosc::address::verify_address;
///
/// match verify_address("/oscillator/1") {
///     Ok(()) => println!("Address is valid"),
///     Err(e) => println!("Address is not valid")
/// }
/// ```
pub fn verify_address(input: &str) -> Result<(), OscError> {
    match all_consuming::<_, _, nom::error::Error<&str>, _>(many1(pair(
        tag("/"),
        take_while1(is_address_character),
    )))(input)
    {
        Ok(_) => Ok(()),
        Err(_) => Err(OscError::BadAddress("Invalid address".to_string())),
    }
}

/// Parse an address pattern's part until the next '/' or the end
fn address_pattern_part_parser(input: &str) -> IResult<&str, Vec<&str>> {
    many1::<_, _, nom::error::Error<&str>, _>(alt((
        take_while1(is_address_character),
        tag("?"),
        tag("*"),
        recognize(pattern_choice),
        pattern_character_class,
    )))(input)
}

/// Verify that an address pattern is valid
///
/// # Examples
/// ```
/// use rosc::address::verify_address_pattern;
///
/// match verify_address_pattern("/oscillator/[0-9]/*") {
///     Ok(()) => println!("Address is valid"),
///     Err(e) => println!("Address is not valid")
/// }
/// ```
pub fn verify_address_pattern(input: &str) -> Result<(), OscError> {
    match all_consuming(many1(
        // Each part must start with a '/'. This automatically also prevents a trailing '/'
        pair(tag("/"), address_pattern_part_parser.map(|x| x.concat())),
    ))(input)
    {
        Ok(_) => Ok(()),
        Err(_) => Err(OscError::BadAddress("Invalid address pattern".to_string())),
    }
}
<commit_msg>Simplify<commit_after>use crate::errors::OscError;

use alloc::string::{String, ToString};
use alloc::vec::Vec;
use nom::branch::alt;
use nom::bytes::complete::{is_a, is_not, tag, take, take_while1};
use nom::character::complete::{char, satisfy};
use nom::combinator::{all_consuming, complete, opt, recognize, verify};
use nom::error::{ErrorKind, ParseError};
use nom::multi::{many1, separated_list1};
use nom::sequence::{delimited, pair, separated_pair};
use nom::{IResult, Parser};

/// With a Matcher OSC method addresses can be [matched](Matcher::match_address) against an OSC address pattern.
/// Refer to the OSC specification for details about OSC address spaces: <http://opensoundcontrol.org/spec-1_0.html#osc-address-spaces-and-osc-addresses>
#[derive(Debug)]
pub struct Matcher {
    pub pattern: String,
    pattern_parts: Vec<AddressPatternComponent>,
}

impl Matcher {
    /// Instantiates a new `Matcher` with the given address pattern.
    /// An error will be returned if the given address pattern is invalid.
    ///
    /// Matcher should be instantiated once per pattern and reused because its construction requires parsing the address pattern which is computationally expensive.
    ///
    /// A valid address pattern begins with a `/` and contains at least a method name, e.g. `/tempo`.
    /// OSC defines a couple of rules that look like regular expression but are subtly different:
    ///
    /// - `?` matches a single character
    /// - `*` matches zero or more characters
    /// - `[a-z]` are basically regex [character classes](https://www.regular-expressions.info/charclass.html)
    /// - `{foo,bar}` is an alternative, matching either `foo` or `bar`
    /// - everything else is matched literally
    ///
    /// Refer to the OSC specification for details about address pattern matching: <osc-message-dispatching-and-pattern-matching>.
    ///
    /// # Examples
    ///
    /// ```
    /// use rosc::address::Matcher;
    ///
    /// Matcher::new("/tempo").expect("valid address");
    /// Matcher::new("").expect_err("address does not start with a slash");
    /// ```
    pub fn new(pattern: &str) -> Result<Self, OscError> {
        verify_address_pattern(pattern)?;
        let pattern_parts = match all_consuming(many1(map_address_pattern_component))(pattern) {
            Ok((_, parts)) => parts,
            // This should never happen because pattern is verified above
            Err(_) => panic!("Address pattern must be valid"),
        };

        Ok(Matcher {
            pattern: pattern.into(),
            pattern_parts,
        })
    }

    /// Match an OSC address against an address pattern.
    /// If the address matches the pattern the result will be `true`, otherwise `false`.
    /// An error is returned if the given OSC address is not valid.
    ///
    /// A valid OSC address begins with a `/` and contains at least a method name, e.g. `/tempo`.
    /// Despite OSC address patterns a plain address must not include any of the following characters `#*,/?[]{}`.
    ///
    /// # Examples
    ///
    /// ```
    /// use rosc::address::Matcher;
    ///
    /// let matcher = Matcher::new("/oscillator/[0-9]/{frequency,phase}").unwrap();
    /// assert!(matcher.match_address("/oscillator/1/frequency").unwrap());
    /// assert!(matcher.match_address("/oscillator/8/phase").unwrap());
    /// assert_eq!(matcher.match_address("/oscillator/4/detune").unwrap(), false);
    /// ```
    pub fn match_address(&self, address: &str) -> Result<bool, OscError> {
        verify_address(address)?; // TODO: Create an address struct so we don't have to re-check addresses every time we match

        // Trivial case
        if address == self.pattern {
            return Ok(true);
        }
        let mut remainder: &str = address;
        // Match the the address component by component
        for (index, part) in self.pattern_parts.as_slice().iter().enumerate() {
            let result = match part {
                AddressPatternComponent::Tag(s) => match_literally(remainder, s.as_str()),
                AddressPatternComponent::WildcardSingle => match_wildcard_single(remainder),
                AddressPatternComponent::Wildcard(l) => {
                    // Check if this is the last pattern component
                    if index < self.pattern_parts.len() - 1 {
                        let next = &self.pattern_parts[index + 1];
                        match next {
                            // If the next component is a '/', there are no more components in the current part and it can be wholly consumed
                            AddressPatternComponent::Tag(s) if s == "/" => {
                                match_wildcard(remainder, *l, None)
                            }
                            _ => match_wildcard(remainder, *l, Some(next)),
                        }
                    } else {
                        match_wildcard(remainder, *l, None)
                    }
                }
                AddressPatternComponent::CharacterClass(cc) => match_character_class(remainder, cc),
                AddressPatternComponent::Choice(s) => match_choice(remainder, s),
            };

            match result {
                Ok((i, _)) => remainder = i,
                Err(_) => return Ok(false), // Component didn't match, goodbye
            };
        }

        // Address is only matched if it was consumed entirely
        Ok(remainder.is_empty())
    }
}

/// Check whether a character is an allowed address character
/// All printable ASCII characters except for a few special characters are allowed
fn is_address_character(x: char) -> bool {
    match x {
        ' ' | '#' | '*' | ',' | '/' | '?' | '[' | ']' | '{' | '}' => false,
        c => c > '\x20' && c < '\x7F',
    }
}

/// Parser to turn a choice like '{foo,bar}' into a vector containing the choices, like ["foo", "bar"]
fn pattern_choice(input: &str) -> IResult<&str, Vec<&str>> {
    delimited(
        char('{'),
        separated_list1(tag(","), take_while1(is_address_character)),
        char('}'),
    )(input)
}

/// Parser to recognize a character class like [!a-zA-Z] and return '!a-zA-Z'
fn pattern_character_class(input: &str) -> IResult<&str, &str> {
    let inner = pair(
        // It is important to read the leading negating '!' to make sure the rest of the parsed
        // character class isn't empty. E.g. '[!]' is not a valid character class.
        recognize(opt(tag("!"))),
        take_while1(is_address_character),
    );

    delimited(char('['), recognize(inner), char(']'))(input)
}

/// A characters class is defined by a set or range of characters that it matches.
/// For example, [a-z] matches all lowercase alphabetic letters. It can also contain multiple
/// ranges, like [a-zA-Z]. Instead of a range you can also directly provide the characters to match,
/// e.g. [abc123]. You can also combine this with ranges, like [a-z123].
/// If the first characters is an exclamation point, the match is negated, e.g. [!0-9] will match
/// anything except numbers.
#[derive(Debug)]
struct CharacterClass {
    pub negated: bool,
    pub characters: String,
}

/// Expand a character range like 'a-d' to all the letters contained in the range, e.g. 'abcd'
/// This is done by converting the characters to their ASCII values and then getting every ASCII
/// in between.
fn expand_character_range(first: char, second: char) -> String {
    let start = first as u8;
    let end = second as u8;

    let range = if start >= end {
        end..=start
    } else {
        start..=end
    };

    let mut out = String::from("");
    for c in range {
        // For funky ranges like [0-a], some illegal characters are contained
        if is_address_character(c as char) {
            out.push(c as char);
        }
    }
    out
}

/// Removes all duplicates from a string of characters
fn deduplicate_characters(input: String) -> String {
    let mut out: String = String::from("");
    for char in input.chars() {
        if !out.contains(char) {
            out.push(char);
        }
    }
    out
}

impl CharacterClass {
    pub fn new(s: &str) -> Self {
        let mut input = s;
        let negated;
        match char::<_, nom::error::Error<&str>>('!')(input) {
            Ok((i, _)) => {
                negated = true;
                input = i;
            }
            Err(_) => negated = false,
        }

        let characters = complete(many1(alt((
            // '!' besides at beginning has no special meaning, but is legal
            char::<_, nom::error::Error<&str>>('!').map(|_| String::from("")),
            // attempt to match a range like a-z or 0-9
            separated_pair(
                satisfy(is_address_character),
                char('-'),
                satisfy(is_address_character),
            )
            .map(|(first, second)| expand_character_range(first, second)),
            // Match characters literally
            satisfy(is_address_character).map(|x| x.to_string()),
            // Trailing dash
            char('-').map(|_| String::from("-")),
        ))))(input);

        match characters {
            Ok((_, o)) => CharacterClass {
                negated,
                characters: deduplicate_characters(o.concat()),
            },
            _ => {
                panic!("Invalid character class formatting {}", s)
            }
        }
    }
}

#[derive(Debug)]
enum AddressPatternComponent {
    Tag(String),
    Wildcard(usize),
    WildcardSingle,
    CharacterClass(CharacterClass),
    Choice(Vec<String>),
}

fn map_address_pattern_component(input: &str) -> IResult<&str, AddressPatternComponent> {
    alt((
        // Anything that's alphanumeric gets matched literally
        take_while1(is_address_character)
            .map(|s: &str| AddressPatternComponent::Tag(String::from(s))),
        // Slashes must be seperated into their own tag for the non-greedy implementation of wildcards
        char('/').map(|c: char| AddressPatternComponent::Tag(c.to_string())),
        tag("?").map(|_| AddressPatternComponent::WildcardSingle),
        // Combinations of wildcards are a bit tricky.
        // Multiple '*' wildcards in a row are equal to a single '*'.
        // A '*' wildcard followed by any number of '?' wildcards is also equal to '*' but must
        // match at least the same amount of characters as there are '?' wildcards in the combination.
        // For example, '*??' must match at least 2 characters.
        is_a("*?").map(|x: &str| AddressPatternComponent::Wildcard(x.matches('?').count())),
        pattern_choice.map(|choices: Vec<&str>| {
            AddressPatternComponent::Choice(choices.iter().map(|x| x.to_string()).collect())
        }),
        pattern_character_class
            .map(|s: &str| AddressPatternComponent::CharacterClass(CharacterClass::new(s))),
    ))(input)
}

fn match_literally<'a>(input: &'a str, pattern: &str) -> IResult<&'a str, &'a str> {
    tag(pattern)(input)
}

fn match_wildcard_single(input: &str) -> IResult<&str, &str> {
    // TODO: this has to be possible with a simpler parser?
    verify(take(1usize), |s: &str| s.chars().all(is_address_character))(input)
}

fn match_character_class<'a>(
    input: &'a str,
    character_class: &'a CharacterClass,
) -> IResult<&'a str, &'a str> {
    if character_class.negated {
        is_not(character_class.characters.as_str())(input)
    } else {
        is_a(character_class.characters.as_str())(input)
    }
}

/// Try all the tags in a choice element
/// Example choice element: '{foo,bar}'
/// It will get parsed into a vector containing the strings "foo" and "bar", which are then matched
fn match_choice<'a>(input: &'a str, choices: &[String]) -> IResult<&'a str, &'a str> {
    for choice in choices {
        if let Ok((i, o)) = tag::<_, _, nom::error::Error<&str>>(choice.as_str())(input) {
            return Ok((i, o));
        }
    }
    Err(nom::Err::Error(nom::error::Error::from_error_kind(
        input,
        ErrorKind::Tag,
    )))
}

/// Match Wildcard '*' by either consuming the rest of the part, or, if it's not the last component
/// in the part, by looking ahead and matching the next component
fn match_wildcard<'a>(
    input: &'a str,
    minimum_length: usize,
    next: Option<&AddressPatternComponent>,
) -> IResult<&'a str, &'a str> {
    match next {
        // No next component, consume all allowed characters until end or next '/'
        None => verify(take_while1(is_address_character), |s: &str| {
            s.len() >= minimum_length
        })(input),
        // There is another element in this part, so logic gets a bit more complicated
        Some(component) => {
            // Wildcards can only match within the current address part, discard the rest
            let address_part = match input.split_once('/') {
                Some((p, _)) => p,
                None => input,
            };

            // Attempt to find the latest matching occurrence of the next pattern component
            // This is a greedy wildcard implementation
            let mut longest: usize = 0;
            for i in 0..address_part.len() {
                let (_, substring) = input.split_at(i);
                let result: IResult<_, _, nom::error::Error<&str>> = match component {
                    AddressPatternComponent::Tag(s) => match_literally(substring, s.as_str()),
                    AddressPatternComponent::CharacterClass(cc) => {
                        match_character_class(substring, cc)
                    }
                    AddressPatternComponent::Choice(s) => match_choice(substring, s),
                    // These two cases are prevented from happening by map_address_pattern_component
                    AddressPatternComponent::WildcardSingle => {
                        panic!("Single wildcard ('?') must not follow wildcard ('*')")
                    }
                    AddressPatternComponent::Wildcard(_) => {
                        panic!("Double wildcards must be condensed into one")
                    }
                };

                if result.is_ok() {
                    longest = i
                }
            }
            verify(take(longest), |s: &str| s.len() >= minimum_length)(input)
        }
    }
}

/// Verify that an address is valid
///
/// # Examples
/// ```
/// use rosc::address::verify_address;
///
/// match verify_address("/oscillator/1") {
///     Ok(()) => println!("Address is valid"),
///     Err(e) => println!("Address is not valid")
/// }
/// ```
pub fn verify_address(input: &str) -> Result<(), OscError> {
    match all_consuming::<_, _, nom::error::Error<&str>, _>(many1(pair(
        tag("/"),
        take_while1(is_address_character),
    )))(input)
    {
        Ok(_) => Ok(()),
        Err(_) => Err(OscError::BadAddress("Invalid address".to_string())),
    }
}

/// Parse an address pattern's part until the next '/' or the end
fn address_pattern_part_parser(input: &str) -> IResult<&str, Vec<&str>> {
    many1::<_, _, nom::error::Error<&str>, _>(alt((
        take_while1(is_address_character),
        tag("?"),
        tag("*"),
        recognize(pattern_choice),
        pattern_character_class,
    )))(input)
}

/// Verify that an address pattern is valid
///
/// # Examples
/// ```
/// use rosc::address::verify_address_pattern;
///
/// match verify_address_pattern("/oscillator/[0-9]/*") {
///     Ok(()) => println!("Address is valid"),
///     Err(e) => println!("Address is not valid")
/// }
/// ```
pub fn verify_address_pattern(input: &str) -> Result<(), OscError> {
    match all_consuming(many1(
        // Each part must start with a '/'. This automatically also prevents a trailing '/'
        pair(tag("/"), address_pattern_part_parser.map(|x| x.concat())),
    ))(input)
    {
        Ok(_) => Ok(()),
        Err(_) => Err(OscError::BadAddress("Invalid address pattern".to_string())),
    }
}
<|endoftext|>