<commit_before>#![cfg(not(windows))]

use screen::Key;
use screen::Key::*;
use std::io::{File, Open, Read, Write};
use libc::{c_ushort, c_int, c_ulong};
use std::os::unix::AsRawFd;
use std::io::process::{Command, InheritFd};
use std::io::process::ProcessExit::*;
use std::iter::repeat;
use std::cmp::min;
use ansi;

use std::thread::Thread;
use std::sync::mpsc::Receiver;
use std::sync::mpsc;

pub struct Screen {
    tty: Terminal,
    original_stty_state: Vec<u8>,
    pub height: u16,
    pub width: u16,
    pub visible_choices: u16,
    pub start_line: u16,
}

impl Screen {
    pub fn open_screen() -> Screen {
        let mut tty = Terminal::open_terminal();
        let current_stty_state = tty.stty(&["-g"]);
        tty.initialize();
        let (cols, rows) = tty.winsize().unwrap();
        let visible_choices = min(20, rows - 1);
        let start_line = rows - visible_choices - 1;
        Screen {
            tty: tty,
            original_stty_state: current_stty_state,
            height: rows,
            width: cols,
            visible_choices: visible_choices,
            start_line: start_line,
        }
    }

    fn restore_tty(&mut self) {
        self.tty.stty(&[String::from_utf8(self.original_stty_state.clone()).unwrap().as_slice()]);
    }

    pub fn move_cursor(&mut self, line: u16, column: u16) {
        self.tty.write(ansi::setpos(line, column).as_slice());
    }

    pub fn move_cursor_to_bottom(&mut self) {
        let end_line = self.start_line + self.visible_choices;
        self.move_cursor(end_line, 0);
        self.write("\n");
    }

    pub fn blank_screen(&mut self) {
        let start_line = self.start_line;
        self.move_cursor(start_line, 0);
        let blank_line = repeat(' ').take(self.width as usize).collect::<String>();
        for _ in range(0, self.height) {
            self.tty.write(blank_line.as_bytes());
        }
        self.move_cursor(start_line, 0);
    }

    pub fn show_cursor(&mut self) {
        self.tty.write(ansi::show_cursor().as_slice());
    }

    pub fn hide_cursor(&mut self) {
        self.tty.write(ansi::hide_cursor().as_slice());
    }

    pub fn write(&mut self, s: &str) {
        self.tty.write(s.as_bytes());
    }

    pub fn write_inverted(&mut self, s: &str) {
        self.tty.write(ansi::inverse().as_slice());
        self.tty.write(s.as_bytes());
        self.tty.write(ansi::reset().as_slice());
    }

    // Return all buffered keystrokes, or the next key if buffer is empty.
    pub fn get_buffered_keys(&mut self) -> Vec<Key> {
        let mut ret = Vec::new();
        while let Ok(byte) = self.tty.input.try_recv() {
            ret.push(Terminal::translate_byte(byte));
        }
        if ret.is_empty() {
            let byte = self.tty.input.recv().unwrap();
            ret.push(Terminal::translate_byte(byte));
        }
        ret
    }
}

impl Drop for Screen {
    fn drop(&mut self) {
        self.restore_tty();
    }
}

struct Terminal {
    input: Receiver<u8>,
    input_fd: i32,
    output: File,
}

impl Terminal {
    fn open_terminal() -> Terminal {
        let term_path = Path::new("/dev/tty");
        let mut input_file = File::open_mode(&term_path, Open, Read).unwrap();
        let output_file = File::open_mode(&term_path, Open, Write).unwrap();
        let input_fd = input_file.as_raw_fd();
        let (tx, rx) = mpsc::channel();
        Thread::spawn(move || {
            loop {
                tx.send(input_file.read_byte().unwrap()).unwrap();
            }
        });
        Terminal {
            input: rx,
            input_fd: input_fd,
            output: output_file
        }
    }

    fn initialize(&mut self) {
        self.stty(&["raw", "-echo", "cbreak"]);
    }

    fn stty(&mut self, args: &[&str]) -> Vec<u8> {
        let mut process = match Command::new("stty").args(args).stdin(InheritFd(self.input_fd)).spawn() {
            Ok(p) => p,
            Err(e) => panic!("Spawn failed: {}", e),
        };

        let exit = process.wait();

        if exit.unwrap() == ExitStatus(0) {
            let bytes = process.stdout.as_mut().unwrap().read_to_end().unwrap();
            let mut str = String::from_utf8(bytes).unwrap();

            // The output from `stty -g` may include a newline, which we have to strip off. Otherwise,
            // when we go to restore the tty, stty (on some platforms) will fail with an "invalid
            // argument" error.
            ::trim(&mut str);

            str.into_bytes()
        } else {
            panic!(String::from_utf8(process.stderr.as_mut().unwrap().read_to_end().unwrap()).unwrap());
        }
    }

    fn getchar(&mut self) -> Key {
        let byte = self.input.recv().unwrap();
        Terminal::translate_byte(byte)
    }

    fn translate_byte(byte: u8) -> Key {
        if byte == '\r' as u8 {
            Enter
        } else if byte == 127 {
            Backspace
        } else if byte & 96 == 0 {
            Control((byte + 96u8) as char)
        } else {
            Char(byte as char)
        }
    }

    fn write(&mut self, s: &[u8]) {
        self.output.write(s.as_slice()).unwrap();
    }

    fn writeln(&mut self, s: &str) {
        self.output.write_line(s).unwrap();
    }

    fn winsize(&self) -> Option<(u16, u16)> {
        extern { fn ioctl(fd: c_int, request: c_ulong, ...) -> c_int; }
        #[cfg(any(target_os = "macos", target_os = "freebsd"))]
        const TIOCGWINSZ: c_ulong = 0x40087468;

        #[cfg(any(target_os = "linux", target_os = "android"))]
        const TIOCGWINSZ: c_ulong = 0x00005413;

        #[repr(C)]
        struct TermSize {
            rows: c_ushort,
            cols: c_ushort,
            x: c_ushort,
            y: c_ushort,
        }

        let size = TermSize { rows: 0, cols: 0, x: 0, y: 0 };
        if unsafe { ioctl(self.output.as_raw_fd(), TIOCGWINSZ, &size) } == 0 {
            Some((size.cols as u16, size.rows as u16))
        } else {
            None
        }
    }
}

#[test]
fn winsize_test() {
    let term = Terminal::open_terminal();
    let (cols, rows) = term.winsize().expect("Failed to get window size!");
    // We don't know the window size a priori, but we can at least
    // assert that it is within some kind of sensible range.
    assert!(cols > 40);
    assert!(rows > 40);
    assert!(cols < 1000);
    assert!(rows < 1000);
}
<commit_msg>Fix Linux write behavior<commit_after>#![cfg(not(windows))]

use screen::Key;
use screen::Key::*;
use std::io::{File, Open, Read, Write};
use libc::{c_ushort, c_int, c_ulong};
use std::os::unix::AsRawFd;
use std::io::process::{Command, InheritFd};
use std::io::process::ProcessExit::*;
use std::iter::repeat;
use std::cmp::min;
use ansi;

use std::thread::Thread;
use std::sync::mpsc::Receiver;
use std::sync::mpsc;

pub struct Screen {
    tty: Terminal,
    original_stty_state: Vec<u8>,
    pub height: u16,
    pub width: u16,
    pub visible_choices: u16,
    pub start_line: u16,
}

impl Screen {
    pub fn open_screen() -> Screen {
        let mut tty = Terminal::open_terminal();
        let current_stty_state = tty.stty(&["-g"]);
        tty.initialize();
        let (cols, rows) = tty.winsize().unwrap();
        let visible_choices = min(20, rows - 1);
        let start_line = rows - visible_choices - 1;
        Screen {
            tty: tty,
            original_stty_state: current_stty_state,
            height: rows,
            width: cols,
            visible_choices: visible_choices,
            start_line: start_line,
        }
    }

    fn restore_tty(&mut self) {
        self.tty.stty(&[String::from_utf8(self.original_stty_state.clone()).unwrap().as_slice()]);
    }

    pub fn move_cursor(&mut self, line: u16, column: u16) {
        self.tty.write(ansi::setpos(line, column).as_slice());
    }

    pub fn move_cursor_to_bottom(&mut self) {
        let end_line = self.start_line + self.visible_choices;
        self.move_cursor(end_line, 0);
        self.write("\n");
    }

    pub fn blank_screen(&mut self) {
        let start_line = self.start_line;
        self.move_cursor(start_line, 0);
        let blank_line = repeat(' ').take(self.width as usize).collect::<String>();
        for _ in range(0, self.height) {
            self.tty.write(blank_line.as_bytes());
        }
        self.move_cursor(start_line, 0);
    }

    pub fn show_cursor(&mut self) {
        self.tty.write(ansi::show_cursor().as_slice());
    }

    pub fn hide_cursor(&mut self) {
        self.tty.write(ansi::hide_cursor().as_slice());
    }

    pub fn write(&mut self, s: &str) {
        self.tty.write(s.as_bytes());
    }

    pub fn write_inverted(&mut self, s: &str) {
        self.tty.write(ansi::inverse().as_slice());
        self.tty.write(s.as_bytes());
        self.tty.write(ansi::reset().as_slice());
    }

    // Return all buffered keystrokes, or the next key if buffer is empty.
    pub fn get_buffered_keys(&mut self) -> Vec<Key> {
        let mut ret = Vec::new();
        while let Ok(byte) = self.tty.input.try_recv() {
            ret.push(Terminal::translate_byte(byte));
        }
        if ret.is_empty() {
            let byte = self.tty.input.recv().unwrap();
            ret.push(Terminal::translate_byte(byte));
        }
        ret
    }
}

impl Drop for Screen {
    fn drop(&mut self) {
        self.restore_tty();
    }
}

struct Terminal {
    input: Receiver<u8>,
    input_fd: i32,
    output: File,
}

impl Terminal {
    fn open_terminal() -> Terminal {
        let term_path = Path::new("/dev/tty");
        let mut input_file = File::open_mode(&term_path, Open, Read).unwrap();
        let output_file = File::open_mode(&term_path, Open, Write).unwrap();
        let input_fd = input_file.as_raw_fd();
        let (tx, rx) = mpsc::channel();
        Thread::spawn(move || {
            loop {
                tx.send(input_file.read_byte().unwrap()).unwrap();
            }
        });
        Terminal {
            input: rx,
            input_fd: input_fd,
            output: output_file
        }
    }

    fn initialize(&mut self) {
        self.stty(&["raw", "-echo", "cbreak", "opost"]);
    }

    fn stty(&mut self, args: &[&str]) -> Vec<u8> {
        let mut process = match Command::new("stty").args(args).stdin(InheritFd(self.input_fd)).spawn() {
            Ok(p) => p,
            Err(e) => panic!("Spawn failed: {}", e),
        };

        let exit = process.wait();

        if exit.unwrap() == ExitStatus(0) {
            let bytes = process.stdout.as_mut().unwrap().read_to_end().unwrap();
            let mut str = String::from_utf8(bytes).unwrap();

            // The output from `stty -g` may include a newline, which we have to strip off. Otherwise,
            // when we go to restore the tty, stty (on some platforms) will fail with an "invalid
            // argument" error.
            ::trim(&mut str);

            str.into_bytes()
        } else {
            panic!(String::from_utf8(process.stderr.as_mut().unwrap().read_to_end().unwrap()).unwrap());
        }
    }

    fn getchar(&mut self) -> Key {
        let byte = self.input.recv().unwrap();
        Terminal::translate_byte(byte)
    }

    fn translate_byte(byte: u8) -> Key {
        if byte == '\r' as u8 {
            Enter
        } else if byte == 127 {
            Backspace
        } else if byte & 96 == 0 {
            Control((byte + 96u8) as char)
        } else {
            Char(byte as char)
        }
    }

    fn write(&mut self, s: &[u8]) {
        self.output.write(s.as_slice()).unwrap();
    }

    fn writeln(&mut self, s: &str) {
        self.output.write_line(s).unwrap();
    }

    fn winsize(&self) -> Option<(u16, u16)> {
        extern { fn ioctl(fd: c_int, request: c_ulong, ...) -> c_int; }
        #[cfg(any(target_os = "macos", target_os = "freebsd"))]
        const TIOCGWINSZ: c_ulong = 0x40087468;

        #[cfg(any(target_os = "linux", target_os = "android"))]
        const TIOCGWINSZ: c_ulong = 0x00005413;

        #[repr(C)]
        struct TermSize {
            rows: c_ushort,
            cols: c_ushort,
            x: c_ushort,
            y: c_ushort,
        }

        let size = TermSize { rows: 0, cols: 0, x: 0, y: 0 };
        if unsafe { ioctl(self.output.as_raw_fd(), TIOCGWINSZ, &size) } == 0 {
            Some((size.cols as u16, size.rows as u16))
        } else {
            None
        }
    }
}

#[test]
fn winsize_test() {
    let term = Terminal::open_terminal();
    let (cols, rows) = term.winsize().expect("Failed to get window size!");
    // We don't know the window size a priori, but we can at least
    // assert that it is within some kind of sensible range.
    assert!(cols > 40);
    assert!(rows > 40);
    assert!(cols < 1000);
    assert!(rows < 1000);
}
<|endoftext|>


<commit_before>extern crate sdl2;

use std::{mem, slice};

use super::{FONT, Color};
use super::event::*;

/// A window
#[allow(dead_code)]
pub struct Window {
    /// The x coordinate of the window
    x: i32,
    /// The y coordinate of the window
    y: i32,
    /// The width of the window
    w: u32,
    /// The height of the window
    h: u32,
    /// The title of the window
    t: String,
    /// True if the window should not wait for events
    async: bool,
    /// SDL2 Context
    ctx: sdl2::Sdl,
    /// Video Context
    video_ctx: sdl2::VideoSubsystem,
    /// Event Pump
    event_pump: sdl2::EventPump,
    /// The inner renderer
    inner: sdl2::render::Renderer<'static>,
}

impl Window {
    /// Create a new window
    pub fn new(x: i32, y: i32, w: u32, h: u32, title: &str) -> Option<Box<Self>> {
        Window::new_flags(x, y, w, h, title, false)
    }

    /// Create a new window with flags
    pub fn new_flags(x: i32, y: i32, w: u32, h: u32, title: &str, async: bool) -> Option<Box<Self>> {
        let ctx = sdl2::init().unwrap();
        let video_ctx = ctx.video().unwrap();
        let event_pump = ctx.event_pump().unwrap();

        let mut builder = video_ctx.window(title, w, h);

        if x >= 0 || y >= 0 {
            builder.position(x, y);
        }

        match builder.build() {
            Ok(window) => Some(Box::new(Window {
                x: x,
                y: y,
                w: w,
                h: h,
                t: title.to_string(),
                async: async,
                ctx: ctx,
                video_ctx: video_ctx,
                event_pump: event_pump,
                inner: window.renderer().build().unwrap(),
            })),
            Err(_) => None
        }
    }

    pub fn sync_path(&mut self) {
        if let Some(window) = self.inner.window() {
            self.x = window.position().0;
            self.x = window.position().1;
            self.w = window.size().0;
            self.h = window.size().1;
            self.t = window.title().to_string();
        }
    }

    /// Get x
    // TODO: Sync with window movements
    pub fn x(&self) -> i32 {
        self.x
    }

    /// Get y
    // TODO: Sync with window movements
    pub fn y(&self) -> i32 {
        self.y
    }

    /// Get width
    pub fn width(&self) -> u32 {
        self.w
    }

    /// Get height
    pub fn height(&self) -> u32 {
        self.h
    }

    /// Get title
    pub fn title(&self) -> String {
        self.t.clone()
    }

    /// Set title
    pub fn set_title(&mut self, _: &str) {
        // TODO
    }

    pub fn data(&self) -> &[Color] {
        let bytes = self.inner.surface().unwrap().without_lock().unwrap();
        unsafe { slice::from_raw_parts(bytes.as_ptr() as *const Color, bytes.len()/mem::size_of::<Color>()) }
    }

    pub fn data_mut(&mut self) -> &mut [Color] {
        let bytes = self.inner.surface_mut().unwrap().without_lock_mut().unwrap();
        unsafe { slice::from_raw_parts_mut(bytes.as_mut_ptr() as *mut Color, bytes.len()/mem::size_of::<Color>()) }
    }

    /// Draw a pixel
    pub fn pixel(&mut self, x: i32, y: i32, color: Color) {
        self.inner.set_blend_mode(sdl2::render::BlendMode::Blend);
        self.inner.set_draw_color(sdl2::pixels::Color::RGBA((color.data >> 16) as u8, (color.data >> 8) as u8, color.data as u8, (color.data >> 24) as u8));
        self.inner.draw_point(sdl2::rect::Point::new(x, y));
    }

    /// Draw a character, using the loaded font
    pub fn char(&mut self, x: i32, y: i32, c: char, color: Color) {
        self.inner.set_blend_mode(sdl2::render::BlendMode::Blend);
        self.inner.set_draw_color(sdl2::pixels::Color::RGBA((color.data >> 16) as u8, (color.data >> 8) as u8, color.data as u8, (color.data >> 24) as u8));

        let mut offset = (c as usize) * 16;
        for row in 0..16 {
            let row_data;
            if offset < FONT.len() {
                row_data = FONT[offset];
            } else {
                row_data = 0;
            }

            for col in 0..8 {
                let pixel = (row_data >> (7 - col)) & 1;
                if pixel > 0 {
                    self.inner.draw_point(sdl2::rect::Point::new(x + col as i32, y + row as i32));
                }
            }
            offset += 1;
        }
    }

    // TODO move, resize, set_title

    /// Set entire window to a color
    // TODO: Improve speed
    #[allow(unused_variables)]
    pub fn set(&mut self, color: Color) {
        if let Some(rect) = sdl2::rect::Rect::new(0, 0, self.w, self.h).unwrap_or(None) {
            self.inner.set_blend_mode(sdl2::render::BlendMode::None);
            self.inner.set_draw_color(sdl2::pixels::Color::RGBA((color.data >> 16) as u8, (color.data >> 8) as u8, color.data as u8, (color.data >> 24) as u8));
            self.inner.fill_rect(rect);
        }
    }

    /// Draw rectangle
    // TODO: Improve speed
    #[allow(unused_variables)]
    pub fn rect(&mut self, start_x: i32, start_y: i32, w: u32, h: u32, color: Color) {
        if let Some(rect) = sdl2::rect::Rect::new(start_x, start_y, w, h).unwrap_or(None) {
            self.inner.set_blend_mode(sdl2::render::BlendMode::Blend);
            self.inner.set_draw_color(sdl2::pixels::Color::RGBA((color.data >> 16) as u8, (color.data >> 8) as u8, color.data as u8, (color.data >> 24) as u8));
            self.inner.fill_rect(rect);
        }
    }

    /// Display an image
    // TODO: Improve speed
    pub fn image(&mut self, start_x: i32, start_y: i32, w: u32, h: u32, data: &[Color]) {
        let mut i = 0;
        for y in start_y..start_y + h as i32 {
            for x in start_x..start_x + w as i32 {
                if i < data.len() {
                    self.pixel(x, y, data[i])
                }
                i += 1;
            }
        }
    }

    fn convert_keycode(&self, keycode_option: Option<sdl2::keyboard::Keycode>) -> Option<(char, u8)> {
        if let Some(keycode) = keycode_option {
            match keycode {
                sdl2::keyboard::Keycode::Return => Some(('\n', 0)),

                sdl2::keyboard::Keycode::Escape => Some(('\x1B', K_ESC)),
                sdl2::keyboard::Keycode::Backspace => Some(('\0', K_BKSP)),
                sdl2::keyboard::Keycode::Tab => Some(('\t', K_TAB)),
                sdl2::keyboard::Keycode::LCtrl => Some(('\0', K_CTRL)),
                sdl2::keyboard::Keycode::RCtrl => Some(('\0', K_CTRL)),
                sdl2::keyboard::Keycode::LAlt => Some(('\0', K_ALT)),
                sdl2::keyboard::Keycode::RAlt => Some(('\0', K_ALT)),
                sdl2::keyboard::Keycode::F1 => Some(('\0', K_F1)),
                sdl2::keyboard::Keycode::F2 => Some(('\0', K_F2)),
                sdl2::keyboard::Keycode::F3 => Some(('\0', K_F3)),
                sdl2::keyboard::Keycode::F4 => Some(('\0', K_F4)),
                sdl2::keyboard::Keycode::F5 => Some(('\0', K_F5)),
                sdl2::keyboard::Keycode::F6 => Some(('\0', K_F6)),
                sdl2::keyboard::Keycode::F7 => Some(('\0', K_F7)),
                sdl2::keyboard::Keycode::F8 => Some(('\0', K_F8)),
                sdl2::keyboard::Keycode::F9 => Some(('\0', K_F9)),
                sdl2::keyboard::Keycode::F10 => Some(('\0', K_F10)),
                sdl2::keyboard::Keycode::Home => Some(('\0', K_HOME)),
                sdl2::keyboard::Keycode::Up => Some(('\0', K_UP)),
                sdl2::keyboard::Keycode::PageUp => Some(('\0', K_PGUP)),
                sdl2::keyboard::Keycode::Left => Some(('\0', K_LEFT)),
                sdl2::keyboard::Keycode::Right => Some(('\0', K_RIGHT)),
                sdl2::keyboard::Keycode::End => Some(('\0', K_END)),
                sdl2::keyboard::Keycode::Down => Some(('\0', K_DOWN)),
                sdl2::keyboard::Keycode::PageDown => Some(('\0', K_PGDN)),
                sdl2::keyboard::Keycode::Delete => Some(('\0', K_DEL)),
                sdl2::keyboard::Keycode::F11 => Some(('\0', K_F11)),
                sdl2::keyboard::Keycode::F12 => Some(('\0', K_F12)),
                sdl2::keyboard::Keycode::LShift => Some(('\0', K_LEFT_SHIFT)),
                sdl2::keyboard::Keycode::RShift => Some(('\0', K_RIGHT_SHIFT)),
                _ => None
            }
        } else {
            None
        }
    }

    fn convert_event(&self, event: sdl2::event::Event) -> Vec<Event> {
        let mut events = Vec::new();

        let mouse_event = || -> Event {
            let mouse = self.ctx.mouse().mouse_state();
            MouseEvent {
                x: mouse.1,
                y: mouse.2,
                left_button: mouse.0.left(),
                middle_button: mouse.0.middle(),
                right_button: mouse.0.right()
            }.to_event()
        };

        match event {
            sdl2::event::Event::MouseMotion { .. } => events.push(mouse_event()),
            sdl2::event::Event::MouseButtonDown { .. } => events.push(mouse_event()),
            sdl2::event::Event::MouseButtonUp { .. } => events.push(mouse_event()),
            sdl2::event::Event::KeyDown { keycode, .. } => if let Some(code) = self.convert_keycode(keycode) {
                events.push(KeyEvent {
                    character: code.0,
                    scancode: code.1,
                    pressed: true
                }.to_event());
            },
            sdl2::event::Event::KeyUp { keycode, .. } => if let Some(code) = self.convert_keycode(keycode) {
                events.push(KeyEvent {
                    character: code.0,
                    scancode: code.1,
                    pressed: false
                }.to_event());
            },
            sdl2::event::Event::TextInput { text, .. } => for c in text.chars() {
                events.push(KeyEvent {
                    character: c,
                    scancode: 0,
                    pressed: true
                }.to_event());
                events.push(KeyEvent {
                    character: c,
                    scancode: 0,
                    pressed: false
                }.to_event());
            },
            sdl2::event::Event::Quit { .. } => events.push(QuitEvent.to_event()),
            _ => (),
        }

        events
    }

    /// Blocking iterator over events
    pub fn events(&mut self) -> EventIter {
        let mut iter = EventIter {
            events: [Event::new(); 128],
            i: 0,
            count: 0,
        };

        if ! self.async {
            let event = self.event_pump.wait_event();
            for converted_event in self.convert_event(event) {
                if iter.count < iter.events.len() {
                    iter.events[iter.count] = converted_event;
                    iter.count += 1;
                } else {
                    break;
                }
            }
        }

        while let Some(event) = self.event_pump.poll_event() {
            for converted_event in self.convert_event(event) {
                if iter.count < iter.events.len() {
                    iter.events[iter.count] = converted_event;
                    iter.count += 1;
                } else {
                    break;
                }
            }
            if iter.count + 2 < iter.events.len() {
                break;
            }
        }

        iter
    }

    /// Flip the window buffer
    pub fn sync(&mut self) -> bool {
        self.inner.present();
        true
    }
}

/// Event iterator
pub struct EventIter {
    events: [Event; 128],
    i: usize,
    count: usize,
}

impl Iterator for EventIter {
    type Item = Event;
    fn next(&mut self) -> Option<Event> {
        if self.i < self.count {
            if let Some(event) = self.events.get(self.i) {
                self.i += 1;
                Some(*event)
            } else {
                None
            }
        } else {
            None
        }
    }
}
<commit_msg>Added line drawing function to sdl2 part of the code<commit_after>extern crate sdl2;

use std::{mem, slice};

use super::{FONT, Color};
use super::event::*;

/// A window
#[allow(dead_code)]
pub struct Window {
    /// The x coordinate of the window
    x: i32,
    /// The y coordinate of the window
    y: i32,
    /// The width of the window
    w: u32,
    /// The height of the window
    h: u32,
    /// The title of the window
    t: String,
    /// True if the window should not wait for events
    async: bool,
    /// SDL2 Context
    ctx: sdl2::Sdl,
    /// Video Context
    video_ctx: sdl2::VideoSubsystem,
    /// Event Pump
    event_pump: sdl2::EventPump,
    /// The inner renderer
    inner: sdl2::render::Renderer<'static>,
}

impl Window {
    /// Create a new window
    pub fn new(x: i32, y: i32, w: u32, h: u32, title: &str) -> Option<Box<Self>> {
        Window::new_flags(x, y, w, h, title, false)
    }

    /// Create a new window with flags
    pub fn new_flags(x: i32, y: i32, w: u32, h: u32, title: &str, async: bool) -> Option<Box<Self>> {
        let ctx = sdl2::init().unwrap();
        let video_ctx = ctx.video().unwrap();
        let event_pump = ctx.event_pump().unwrap();

        let mut builder = video_ctx.window(title, w, h);

        if x >= 0 || y >= 0 {
            builder.position(x, y);
        }

        match builder.build() {
            Ok(window) => Some(Box::new(Window {
                x: x,
                y: y,
                w: w,
                h: h,
                t: title.to_string(),
                async: async,
                ctx: ctx,
                video_ctx: video_ctx,
                event_pump: event_pump,
                inner: window.renderer().build().unwrap(),
            })),
            Err(_) => None
        }
    }

    pub fn sync_path(&mut self) {
        if let Some(window) = self.inner.window() {
            self.x = window.position().0;
            self.x = window.position().1;
            self.w = window.size().0;
            self.h = window.size().1;
            self.t = window.title().to_string();
        }
    }

    /// Get x
    // TODO: Sync with window movements
    pub fn x(&self) -> i32 {
        self.x
    }

    /// Get y
    // TODO: Sync with window movements
    pub fn y(&self) -> i32 {
        self.y
    }

    /// Get width
    pub fn width(&self) -> u32 {
        self.w
    }

    /// Get height
    pub fn height(&self) -> u32 {
        self.h
    }

    /// Get title
    pub fn title(&self) -> String {
        self.t.clone()
    }

    /// Set title
    pub fn set_title(&mut self, _: &str) {
        // TODO
    }

    pub fn data(&self) -> &[Color] {
        let bytes = self.inner.surface().unwrap().without_lock().unwrap();
        unsafe { slice::from_raw_parts(bytes.as_ptr() as *const Color, bytes.len()/mem::size_of::<Color>()) }
    }

    pub fn data_mut(&mut self) -> &mut [Color] {
        let bytes = self.inner.surface_mut().unwrap().without_lock_mut().unwrap();
        unsafe { slice::from_raw_parts_mut(bytes.as_mut_ptr() as *mut Color, bytes.len()/mem::size_of::<Color>()) }
    }

    /// Draw a pixel
    pub fn pixel(&mut self, x: i32, y: i32, color: Color) {
        self.inner.set_blend_mode(sdl2::render::BlendMode::Blend);
        self.inner.set_draw_color(sdl2::pixels::Color::RGBA((color.data >> 16) as u8, (color.data >> 8) as u8, color.data as u8, (color.data >> 24) as u8));
        self.inner.draw_point(sdl2::rect::Point::new(x, y));
    }

    /// Draw a line
    pub fn line(&mut self, x1: i32, y1: i32, x2: i32, y2: i32, color: Color) {
        self.inner.set_blend_mode(sdl2::render::BlendMode::Blend);
        self.inner.set_draw_color(sdl2::pixels::Color::RGBA((color.data >> 16) as u8, (color.data >> 8) as u8, color.data as u8, (color.data >> 24) as u8));
        self.inner.draw_line(sdl2::rect::Point::new(x1, y1), sdl2::rect::Point::new(x2, y2));
    }

    /// Draw a character, using the loaded font
    pub fn char(&mut self, x: i32, y: i32, c: char, color: Color) {
        self.inner.set_blend_mode(sdl2::render::BlendMode::Blend);
        self.inner.set_draw_color(sdl2::pixels::Color::RGBA((color.data >> 16) as u8, (color.data >> 8) as u8, color.data as u8, (color.data >> 24) as u8));

        let mut offset = (c as usize) * 16;
        for row in 0..16 {
            let row_data;
            if offset < FONT.len() {
                row_data = FONT[offset];
            } else {
                row_data = 0;
            }

            for col in 0..8 {
                let pixel = (row_data >> (7 - col)) & 1;
                if pixel > 0 {
                    self.inner.draw_point(sdl2::rect::Point::new(x + col as i32, y + row as i32));
                }
            }
            offset += 1;
        }
    }

    // TODO move, resize, set_title

    /// Set entire window to a color
    // TODO: Improve speed
    #[allow(unused_variables)]
    pub fn set(&mut self, color: Color) {
        if let Some(rect) = sdl2::rect::Rect::new(0, 0, self.w, self.h).unwrap_or(None) {
            self.inner.set_blend_mode(sdl2::render::BlendMode::None);
            self.inner.set_draw_color(sdl2::pixels::Color::RGBA((color.data >> 16) as u8, (color.data >> 8) as u8, color.data as u8, (color.data >> 24) as u8));
            self.inner.fill_rect(rect);
        }
    }

    /// Draw rectangle
    // TODO: Improve speed
    #[allow(unused_variables)]
    pub fn rect(&mut self, start_x: i32, start_y: i32, w: u32, h: u32, color: Color) {
        if let Some(rect) = sdl2::rect::Rect::new(start_x, start_y, w, h).unwrap_or(None) {
            self.inner.set_blend_mode(sdl2::render::BlendMode::Blend);
            self.inner.set_draw_color(sdl2::pixels::Color::RGBA((color.data >> 16) as u8, (color.data >> 8) as u8, color.data as u8, (color.data >> 24) as u8));
            self.inner.fill_rect(rect);
        }
    }

    /// Display an image
    // TODO: Improve speed
    pub fn image(&mut self, start_x: i32, start_y: i32, w: u32, h: u32, data: &[Color]) {
        let mut i = 0;
        for y in start_y..start_y + h as i32 {
            for x in start_x..start_x + w as i32 {
                if i < data.len() {
                    self.pixel(x, y, data[i])
                }
                i += 1;
            }
        }
    }

    fn convert_keycode(&self, keycode_option: Option<sdl2::keyboard::Keycode>) -> Option<(char, u8)> {
        if let Some(keycode) = keycode_option {
            match keycode {
                sdl2::keyboard::Keycode::Return => Some(('\n', 0)),

                sdl2::keyboard::Keycode::Escape => Some(('\x1B', K_ESC)),
                sdl2::keyboard::Keycode::Backspace => Some(('\0', K_BKSP)),
                sdl2::keyboard::Keycode::Tab => Some(('\t', K_TAB)),
                sdl2::keyboard::Keycode::LCtrl => Some(('\0', K_CTRL)),
                sdl2::keyboard::Keycode::RCtrl => Some(('\0', K_CTRL)),
                sdl2::keyboard::Keycode::LAlt => Some(('\0', K_ALT)),
                sdl2::keyboard::Keycode::RAlt => Some(('\0', K_ALT)),
                sdl2::keyboard::Keycode::F1 => Some(('\0', K_F1)),
                sdl2::keyboard::Keycode::F2 => Some(('\0', K_F2)),
                sdl2::keyboard::Keycode::F3 => Some(('\0', K_F3)),
                sdl2::keyboard::Keycode::F4 => Some(('\0', K_F4)),
                sdl2::keyboard::Keycode::F5 => Some(('\0', K_F5)),
                sdl2::keyboard::Keycode::F6 => Some(('\0', K_F6)),
                sdl2::keyboard::Keycode::F7 => Some(('\0', K_F7)),
                sdl2::keyboard::Keycode::F8 => Some(('\0', K_F8)),
                sdl2::keyboard::Keycode::F9 => Some(('\0', K_F9)),
                sdl2::keyboard::Keycode::F10 => Some(('\0', K_F10)),
                sdl2::keyboard::Keycode::Home => Some(('\0', K_HOME)),
                sdl2::keyboard::Keycode::Up => Some(('\0', K_UP)),
                sdl2::keyboard::Keycode::PageUp => Some(('\0', K_PGUP)),
                sdl2::keyboard::Keycode::Left => Some(('\0', K_LEFT)),
                sdl2::keyboard::Keycode::Right => Some(('\0', K_RIGHT)),
                sdl2::keyboard::Keycode::End => Some(('\0', K_END)),
                sdl2::keyboard::Keycode::Down => Some(('\0', K_DOWN)),
                sdl2::keyboard::Keycode::PageDown => Some(('\0', K_PGDN)),
                sdl2::keyboard::Keycode::Delete => Some(('\0', K_DEL)),
                sdl2::keyboard::Keycode::F11 => Some(('\0', K_F11)),
                sdl2::keyboard::Keycode::F12 => Some(('\0', K_F12)),
                sdl2::keyboard::Keycode::LShift => Some(('\0', K_LEFT_SHIFT)),
                sdl2::keyboard::Keycode::RShift => Some(('\0', K_RIGHT_SHIFT)),
                _ => None
            }
        } else {
            None
        }
    }

    fn convert_event(&self, event: sdl2::event::Event) -> Vec<Event> {
        let mut events = Vec::new();

        let mouse_event = || -> Event {
            let mouse = self.ctx.mouse().mouse_state();
            MouseEvent {
                x: mouse.1,
                y: mouse.2,
                left_button: mouse.0.left(),
                middle_button: mouse.0.middle(),
                right_button: mouse.0.right()
            }.to_event()
        };

        match event {
            sdl2::event::Event::MouseMotion { .. } => events.push(mouse_event()),
            sdl2::event::Event::MouseButtonDown { .. } => events.push(mouse_event()),
            sdl2::event::Event::MouseButtonUp { .. } => events.push(mouse_event()),
            sdl2::event::Event::KeyDown { keycode, .. } => if let Some(code) = self.convert_keycode(keycode) {
                events.push(KeyEvent {
                    character: code.0,
                    scancode: code.1,
                    pressed: true
                }.to_event());
            },
            sdl2::event::Event::KeyUp { keycode, .. } => if let Some(code) = self.convert_keycode(keycode) {
                events.push(KeyEvent {
                    character: code.0,
                    scancode: code.1,
                    pressed: false
                }.to_event());
            },
            sdl2::event::Event::TextInput { text, .. } => for c in text.chars() {
                events.push(KeyEvent {
                    character: c,
                    scancode: 0,
                    pressed: true
                }.to_event());
                events.push(KeyEvent {
                    character: c,
                    scancode: 0,
                    pressed: false
                }.to_event());
            },
            sdl2::event::Event::Quit { .. } => events.push(QuitEvent.to_event()),
            _ => (),
        }

        events
    }

    /// Blocking iterator over events
    pub fn events(&mut self) -> EventIter {
        let mut iter = EventIter {
            events: [Event::new(); 128],
            i: 0,
            count: 0,
        };

        if ! self.async {
            let event = self.event_pump.wait_event();
            for converted_event in self.convert_event(event) {
                if iter.count < iter.events.len() {
                    iter.events[iter.count] = converted_event;
                    iter.count += 1;
                } else {
                    break;
                }
            }
        }

        while let Some(event) = self.event_pump.poll_event() {
            for converted_event in self.convert_event(event) {
                if iter.count < iter.events.len() {
                    iter.events[iter.count] = converted_event;
                    iter.count += 1;
                } else {
                    break;
                }
            }
            if iter.count + 2 < iter.events.len() {
                break;
            }
        }

        iter
    }

    /// Flip the window buffer
    pub fn sync(&mut self) -> bool {
        self.inner.present();
        true
    }
}

/// Event iterator
pub struct EventIter {
    events: [Event; 128],
    i: usize,
    count: usize,
}

impl Iterator for EventIter {
    type Item = Event;
    fn next(&mut self) -> Option<Event> {
        if self.i < self.count {
            if let Some(event) = self.events.get(self.i) {
                self.i += 1;
                Some(*event)
            } else {
                None
            }
        } else {
            None
        }
    }
}
<|endoftext|>


<commit_before>use crate::{CompileError, Config, Syntax};

use std::path::{Path, PathBuf};
use std::str::FromStr;

use mime::Mime;
use quote::ToTokens;

pub struct TemplateInput<'a> {
    pub ast: &'a syn::DeriveInput,
    pub config: &'a Config<'a>,
    pub syntax: &'a Syntax<'a>,
    pub source: Source,
    pub print: Print,
    pub escaper: &'a str,
    pub ext: Option<String>,
    pub mime_type: String,
    pub parent: Option<&'a syn::Type>,
    pub path: PathBuf,
}

impl TemplateInput<'_> {
    /// Extract the template metadata from the `DeriveInput` structure. This
    /// mostly recovers the data for the `TemplateInput` fields from the
    /// `template()` attribute list fields; it also finds the of the `_parent`
    /// field, if any.
    pub fn new<'n>(
        ast: &'n syn::DeriveInput,
        config: &'n Config<'_>,
    ) -> Result<TemplateInput<'n>, CompileError> {
        // Check that an attribute called `template()` exists once and that it is
        // the proper type (list).
        let mut template_args = None;
        for attr in &ast.attrs {
            if attr.path.is_ident("template") {
                if template_args.is_some() {
                    return Err(CompileError::Static("duplicated 'template' attribute"));
                }

                match attr.parse_meta() {
                    Ok(syn::Meta::List(syn::MetaList { nested, .. })) => {
                        template_args = Some(nested);
                    }
                    Ok(_) => return Err("'template' attribute must be a list".into()),
                    Err(e) => return Err(format!("unable to parse attribute: {}", e).into()),
                }
            }
        }
        let template_args =
            template_args.ok_or(CompileError::Static("no attribute 'template' found"))?;

        // Loop over the meta attributes and find everything that we
        // understand. Raise panics if something is not right.
        // `source` contains an enum that can represent `path` or `source`.
        let mut source = None;
        let mut print = Print::None;
        let mut escaping = None;
        let mut ext = None;
        let mut syntax = None;
        for item in template_args {
            let pair = match item {
                syn::NestedMeta::Meta(syn::Meta::NameValue(ref pair)) => pair,
                _ => {
                    return Err(format!(
                        "unsupported attribute argument {:?}",
                        item.to_token_stream()
                    )
                    .into())
                }
            };

            if pair.path.is_ident("path") {
                if let syn::Lit::Str(ref s) = pair.lit {
                    if source.is_some() {
                        return Err("must specify 'source' or 'path', not both".into());
                    }
                    source = Some(Source::Path(s.value()));
                } else {
                    return Err("template path must be string literal".into());
                }
            } else if pair.path.is_ident("source") {
                if let syn::Lit::Str(ref s) = pair.lit {
                    if source.is_some() {
                        return Err("must specify 'source' or 'path', not both".into());
                    }
                    source = Some(Source::Source(s.value()));
                } else {
                    return Err("template source must be string literal".into());
                }
            } else if pair.path.is_ident("print") {
                if let syn::Lit::Str(ref s) = pair.lit {
                    print = s.value().parse()?;
                } else {
                    return Err("print value must be string literal".into());
                }
            } else if pair.path.is_ident("escape") {
                if let syn::Lit::Str(ref s) = pair.lit {
                    escaping = Some(s.value());
                } else {
                    return Err("escape value must be string literal".into());
                }
            } else if pair.path.is_ident("ext") {
                if let syn::Lit::Str(ref s) = pair.lit {
                    ext = Some(s.value());
                } else {
                    return Err("ext value must be string literal".into());
                }
            } else if pair.path.is_ident("syntax") {
                if let syn::Lit::Str(ref s) = pair.lit {
                    syntax = Some(s.value())
                } else {
                    return Err("syntax value must be string literal".into());
                }
            } else {
                return Err(format!(
                    "unsupported attribute key '{}' found",
                    pair.path.to_token_stream()
                )
                .into());
            }
        }

        // Validate the `source` and `ext` value together, since they are
        // related. In case `source` was used instead of `path`, the value
        // of `ext` is merged into a synthetic `path` value here.
        let source = source.expect("template path or source not found in attributes");
        let path = match (&source, &ext) {
            (&Source::Path(ref path), _) => config.find_template(path, None)?,
            (&Source::Source(_), Some(ext)) => PathBuf::from(format!("{}.{}", ast.ident, ext)),
            (&Source::Source(_), None) => {
                return Err("must include 'ext' attribute when using 'source' attribute".into())
            }
        };

        // Check to see if a `_parent` field was defined on the context
        // struct, and store the type for it for use in the code generator.
        let parent = match ast.data {
            syn::Data::Struct(syn::DataStruct {
                fields: syn::Fields::Named(ref fields),
                ..
            }) => fields
                .named
                .iter()
                .find(|f| f.ident.as_ref().filter(|name| *name == "_parent").is_some())
                .map(|f| &f.ty),
            _ => None,
        };

        if parent.is_some() {
            eprint!(
                "   --> in struct {}\n   = use of deprecated field '_parent'\n",
                ast.ident
            );
        }

        // Validate syntax
        let syntax = syntax.map_or_else(
            || Ok(config.syntaxes.get(config.default_syntax).unwrap()),
            |s| {
                config.syntaxes.get(&s).ok_or_else(|| {
                    CompileError::String(format!("attribute syntax {} not exist", s))
                })
            },
        )?;

        // Match extension against defined output formats

        let escaping = escaping.unwrap_or_else(|| {
            path.extension()
                .map(|s| s.to_str().unwrap())
                .unwrap_or("")
                .to_string()
        });

        let mut escaper = None;
        for (extensions, path) in &config.escapers {
            if extensions.contains(&escaping) {
                escaper = Some(path);
                break;
            }
        }

        let escaper = escaper.ok_or_else(|| {
            CompileError::String(format!("no escaper defined for extension '{}'", escaping,))
        })?;

        let mime_type =
            extension_to_mime_type(ext_default_to_path(ext.as_deref(), &path).unwrap_or("txt"))
                .to_string();

        Ok(TemplateInput {
            ast,
            config,
            syntax,
            source,
            print,
            escaper,
            ext,
            mime_type,
            parent,
            path,
        })
    }

    #[inline]
    pub fn extension(&self) -> Option<&str> {
        ext_default_to_path(self.ext.as_deref(), &self.path)
    }
}

#[inline]
pub fn ext_default_to_path<'a>(ext: Option<&'a str>, path: &'a Path) -> Option<&'a str> {
    ext.or_else(|| extension(path))
}

fn extension(path: &Path) -> Option<&str> {
    let ext = path.extension().map(|s| s.to_str().unwrap())?;

    const JINJA_EXTENSIONS: [&str; 3] = ["j2", "jinja", "jinja2"];
    if JINJA_EXTENSIONS.contains(&ext) {
        Path::new(path.file_stem().unwrap())
            .extension()
            .map(|s| s.to_str().unwrap())
            .or(Some(ext))
    } else {
        Some(ext)
    }
}

pub enum Source {
    Path(String),
    Source(String),
}

#[derive(PartialEq)]
pub enum Print {
    All,
    Ast,
    Code,
    None,
}

impl FromStr for Print {
    type Err = CompileError;

    fn from_str(s: &str) -> Result<Print, Self::Err> {
        use self::Print::*;
        Ok(match s {
            "all" => All,
            "ast" => Ast,
            "code" => Code,
            "none" => None,
            v => return Err(format!("invalid value for print option: {}", v,).into()),
        })
    }
}

#[doc(hidden)]
pub fn extension_to_mime_type(ext: &str) -> Mime {
    let basic_type = mime_guess::from_ext(ext).first_or_octet_stream();
    for (simple, utf_8) in &TEXT_TYPES {
        if &basic_type == simple {
            return utf_8.clone();
        }
    }
    basic_type
}

const TEXT_TYPES: [(Mime, Mime); 6] = [
    (mime::TEXT_PLAIN, mime::TEXT_PLAIN_UTF_8),
    (mime::TEXT_HTML, mime::TEXT_HTML_UTF_8),
    (mime::TEXT_CSS, mime::TEXT_CSS_UTF_8),
    (mime::TEXT_CSV, mime::TEXT_CSV_UTF_8),
    (
        mime::TEXT_TAB_SEPARATED_VALUES,
        mime::TEXT_TAB_SEPARATED_VALUES_UTF_8,
    ),
    (
        mime::APPLICATION_JAVASCRIPT,
        mime::APPLICATION_JAVASCRIPT_UTF_8,
    ),
];

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

    #[test]
    fn test_ext() {
        assert_eq!(extension(Path::new("foo-bar.txt")), Some("txt"));
        assert_eq!(extension(Path::new("foo-bar.html")), Some("html"));
        assert_eq!(extension(Path::new("foo-bar.unknown")), Some("unknown"));

        assert_eq!(extension(Path::new("foo/bar/baz.txt")), Some("txt"));
        assert_eq!(extension(Path::new("foo/bar/baz.html")), Some("html"));
        assert_eq!(extension(Path::new("foo/bar/baz.unknown")), Some("unknown"));
    }

    #[test]
    fn test_double_ext() {
        assert_eq!(extension(Path::new("foo-bar.html.txt")), Some("txt"));
        assert_eq!(extension(Path::new("foo-bar.txt.html")), Some("html"));
        assert_eq!(extension(Path::new("foo-bar.txt.unknown")), Some("unknown"));

        assert_eq!(extension(Path::new("foo/bar/baz.html.txt")), Some("txt"));
        assert_eq!(extension(Path::new("foo/bar/baz.txt.html")), Some("html"));
        assert_eq!(
            extension(Path::new("foo/bar/baz.txt.unknown")),
            Some("unknown")
        );
    }

    #[test]
    fn test_skip_jinja_ext() {
        assert_eq!(extension(Path::new("foo-bar.html.j2")), Some("html"));
        assert_eq!(extension(Path::new("foo-bar.html.jinja")), Some("html"));
        assert_eq!(extension(Path::new("foo-bar.html.jinja2")), Some("html"));

        assert_eq!(extension(Path::new("foo/bar/baz.txt.j2")), Some("txt"));
        assert_eq!(extension(Path::new("foo/bar/baz.txt.jinja")), Some("txt"));
        assert_eq!(extension(Path::new("foo/bar/baz.txt.jinja2")), Some("txt"));
    }

    #[test]
    fn test_only_jinja_ext() {
        assert_eq!(extension(Path::new("foo-bar.j2")), Some("j2"));
        assert_eq!(extension(Path::new("foo-bar.jinja")), Some("jinja"));
        assert_eq!(extension(Path::new("foo-bar.jinja2")), Some("jinja2"));
    }
}
<commit_msg>Update comment in TemplateInput::new()<commit_after>use crate::{CompileError, Config, Syntax};

use std::path::{Path, PathBuf};
use std::str::FromStr;

use mime::Mime;
use quote::ToTokens;

pub struct TemplateInput<'a> {
    pub ast: &'a syn::DeriveInput,
    pub config: &'a Config<'a>,
    pub syntax: &'a Syntax<'a>,
    pub source: Source,
    pub print: Print,
    pub escaper: &'a str,
    pub ext: Option<String>,
    pub mime_type: String,
    pub parent: Option<&'a syn::Type>,
    pub path: PathBuf,
}

impl TemplateInput<'_> {
    /// Extract the template metadata from the `DeriveInput` structure. This
    /// mostly recovers the data for the `TemplateInput` fields from the
    /// `template()` attribute list fields; it also finds the of the `_parent`
    /// field, if any.
    pub fn new<'n>(
        ast: &'n syn::DeriveInput,
        config: &'n Config<'_>,
    ) -> Result<TemplateInput<'n>, CompileError> {
        // Check that an attribute called `template()` exists once and that it is
        // the proper type (list).
        let mut template_args = None;
        for attr in &ast.attrs {
            if attr.path.is_ident("template") {
                if template_args.is_some() {
                    return Err(CompileError::Static("duplicated 'template' attribute"));
                }

                match attr.parse_meta() {
                    Ok(syn::Meta::List(syn::MetaList { nested, .. })) => {
                        template_args = Some(nested);
                    }
                    Ok(_) => return Err("'template' attribute must be a list".into()),
                    Err(e) => return Err(format!("unable to parse attribute: {}", e).into()),
                }
            }
        }
        let template_args =
            template_args.ok_or(CompileError::Static("no attribute 'template' found"))?;

        // Loop over the meta attributes and find everything that we
        // understand. Return a CompileError if something is not right.
        // `source` contains an enum that can represent `path` or `source`.
        let mut source = None;
        let mut print = Print::None;
        let mut escaping = None;
        let mut ext = None;
        let mut syntax = None;
        for item in template_args {
            let pair = match item {
                syn::NestedMeta::Meta(syn::Meta::NameValue(ref pair)) => pair,
                _ => {
                    return Err(format!(
                        "unsupported attribute argument {:?}",
                        item.to_token_stream()
                    )
                    .into())
                }
            };

            if pair.path.is_ident("path") {
                if let syn::Lit::Str(ref s) = pair.lit {
                    if source.is_some() {
                        return Err("must specify 'source' or 'path', not both".into());
                    }
                    source = Some(Source::Path(s.value()));
                } else {
                    return Err("template path must be string literal".into());
                }
            } else if pair.path.is_ident("source") {
                if let syn::Lit::Str(ref s) = pair.lit {
                    if source.is_some() {
                        return Err("must specify 'source' or 'path', not both".into());
                    }
                    source = Some(Source::Source(s.value()));
                } else {
                    return Err("template source must be string literal".into());
                }
            } else if pair.path.is_ident("print") {
                if let syn::Lit::Str(ref s) = pair.lit {
                    print = s.value().parse()?;
                } else {
                    return Err("print value must be string literal".into());
                }
            } else if pair.path.is_ident("escape") {
                if let syn::Lit::Str(ref s) = pair.lit {
                    escaping = Some(s.value());
                } else {
                    return Err("escape value must be string literal".into());
                }
            } else if pair.path.is_ident("ext") {
                if let syn::Lit::Str(ref s) = pair.lit {
                    ext = Some(s.value());
                } else {
                    return Err("ext value must be string literal".into());
                }
            } else if pair.path.is_ident("syntax") {
                if let syn::Lit::Str(ref s) = pair.lit {
                    syntax = Some(s.value())
                } else {
                    return Err("syntax value must be string literal".into());
                }
            } else {
                return Err(format!(
                    "unsupported attribute key '{}' found",
                    pair.path.to_token_stream()
                )
                .into());
            }
        }

        // Validate the `source` and `ext` value together, since they are
        // related. In case `source` was used instead of `path`, the value
        // of `ext` is merged into a synthetic `path` value here.
        let source = source.expect("template path or source not found in attributes");
        let path = match (&source, &ext) {
            (&Source::Path(ref path), _) => config.find_template(path, None)?,
            (&Source::Source(_), Some(ext)) => PathBuf::from(format!("{}.{}", ast.ident, ext)),
            (&Source::Source(_), None) => {
                return Err("must include 'ext' attribute when using 'source' attribute".into())
            }
        };

        // Check to see if a `_parent` field was defined on the context
        // struct, and store the type for it for use in the code generator.
        let parent = match ast.data {
            syn::Data::Struct(syn::DataStruct {
                fields: syn::Fields::Named(ref fields),
                ..
            }) => fields
                .named
                .iter()
                .find(|f| f.ident.as_ref().filter(|name| *name == "_parent").is_some())
                .map(|f| &f.ty),
            _ => None,
        };

        if parent.is_some() {
            eprint!(
                "   --> in struct {}\n   = use of deprecated field '_parent'\n",
                ast.ident
            );
        }

        // Validate syntax
        let syntax = syntax.map_or_else(
            || Ok(config.syntaxes.get(config.default_syntax).unwrap()),
            |s| {
                config.syntaxes.get(&s).ok_or_else(|| {
                    CompileError::String(format!("attribute syntax {} not exist", s))
                })
            },
        )?;

        // Match extension against defined output formats

        let escaping = escaping.unwrap_or_else(|| {
            path.extension()
                .map(|s| s.to_str().unwrap())
                .unwrap_or("")
                .to_string()
        });

        let mut escaper = None;
        for (extensions, path) in &config.escapers {
            if extensions.contains(&escaping) {
                escaper = Some(path);
                break;
            }
        }

        let escaper = escaper.ok_or_else(|| {
            CompileError::String(format!("no escaper defined for extension '{}'", escaping,))
        })?;

        let mime_type =
            extension_to_mime_type(ext_default_to_path(ext.as_deref(), &path).unwrap_or("txt"))
                .to_string();

        Ok(TemplateInput {
            ast,
            config,
            syntax,
            source,
            print,
            escaper,
            ext,
            mime_type,
            parent,
            path,
        })
    }

    #[inline]
    pub fn extension(&self) -> Option<&str> {
        ext_default_to_path(self.ext.as_deref(), &self.path)
    }
}

#[inline]
pub fn ext_default_to_path<'a>(ext: Option<&'a str>, path: &'a Path) -> Option<&'a str> {
    ext.or_else(|| extension(path))
}

fn extension(path: &Path) -> Option<&str> {
    let ext = path.extension().map(|s| s.to_str().unwrap())?;

    const JINJA_EXTENSIONS: [&str; 3] = ["j2", "jinja", "jinja2"];
    if JINJA_EXTENSIONS.contains(&ext) {
        Path::new(path.file_stem().unwrap())
            .extension()
            .map(|s| s.to_str().unwrap())
            .or(Some(ext))
    } else {
        Some(ext)
    }
}

pub enum Source {
    Path(String),
    Source(String),
}

#[derive(PartialEq)]
pub enum Print {
    All,
    Ast,
    Code,
    None,
}

impl FromStr for Print {
    type Err = CompileError;

    fn from_str(s: &str) -> Result<Print, Self::Err> {
        use self::Print::*;
        Ok(match s {
            "all" => All,
            "ast" => Ast,
            "code" => Code,
            "none" => None,
            v => return Err(format!("invalid value for print option: {}", v,).into()),
        })
    }
}

#[doc(hidden)]
pub fn extension_to_mime_type(ext: &str) -> Mime {
    let basic_type = mime_guess::from_ext(ext).first_or_octet_stream();
    for (simple, utf_8) in &TEXT_TYPES {
        if &basic_type == simple {
            return utf_8.clone();
        }
    }
    basic_type
}

const TEXT_TYPES: [(Mime, Mime); 6] = [
    (mime::TEXT_PLAIN, mime::TEXT_PLAIN_UTF_8),
    (mime::TEXT_HTML, mime::TEXT_HTML_UTF_8),
    (mime::TEXT_CSS, mime::TEXT_CSS_UTF_8),
    (mime::TEXT_CSV, mime::TEXT_CSV_UTF_8),
    (
        mime::TEXT_TAB_SEPARATED_VALUES,
        mime::TEXT_TAB_SEPARATED_VALUES_UTF_8,
    ),
    (
        mime::APPLICATION_JAVASCRIPT,
        mime::APPLICATION_JAVASCRIPT_UTF_8,
    ),
];

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

    #[test]
    fn test_ext() {
        assert_eq!(extension(Path::new("foo-bar.txt")), Some("txt"));
        assert_eq!(extension(Path::new("foo-bar.html")), Some("html"));
        assert_eq!(extension(Path::new("foo-bar.unknown")), Some("unknown"));

        assert_eq!(extension(Path::new("foo/bar/baz.txt")), Some("txt"));
        assert_eq!(extension(Path::new("foo/bar/baz.html")), Some("html"));
        assert_eq!(extension(Path::new("foo/bar/baz.unknown")), Some("unknown"));
    }

    #[test]
    fn test_double_ext() {
        assert_eq!(extension(Path::new("foo-bar.html.txt")), Some("txt"));
        assert_eq!(extension(Path::new("foo-bar.txt.html")), Some("html"));
        assert_eq!(extension(Path::new("foo-bar.txt.unknown")), Some("unknown"));

        assert_eq!(extension(Path::new("foo/bar/baz.html.txt")), Some("txt"));
        assert_eq!(extension(Path::new("foo/bar/baz.txt.html")), Some("html"));
        assert_eq!(
            extension(Path::new("foo/bar/baz.txt.unknown")),
            Some("unknown")
        );
    }

    #[test]
    fn test_skip_jinja_ext() {
        assert_eq!(extension(Path::new("foo-bar.html.j2")), Some("html"));
        assert_eq!(extension(Path::new("foo-bar.html.jinja")), Some("html"));
        assert_eq!(extension(Path::new("foo-bar.html.jinja2")), Some("html"));

        assert_eq!(extension(Path::new("foo/bar/baz.txt.j2")), Some("txt"));
        assert_eq!(extension(Path::new("foo/bar/baz.txt.jinja")), Some("txt"));
        assert_eq!(extension(Path::new("foo/bar/baz.txt.jinja2")), Some("txt"));
    }

    #[test]
    fn test_only_jinja_ext() {
        assert_eq!(extension(Path::new("foo-bar.j2")), Some("j2"));
        assert_eq!(extension(Path::new("foo-bar.jinja")), Some("jinja"));
        assert_eq!(extension(Path::new("foo-bar.jinja2")), Some("jinja2"));
    }
}
<|endoftext|>


<commit_before>use super::mbuf::MBuf;
use super::interface::Result;
use super::interface::ZCSIError;
use super::super::headers::MacAddress;
use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};

// External DPDK calls
#[link(name = "zcsi")]
extern "C" {
    fn init_pmd_port(port: i32,
                     rxqs: i32,
                     txqs: i32,
                     rx_cores: *const i32,
                     tx_cores: *const i32,
                     nrxd: i32,
                     ntxd: i32,
                     loopback: i32,
                     tso: i32,
                     csumoffload: i32)
                     -> i32;
    fn free_pmd_port(port: i32) -> i32;
    fn recv_pkts(port: i32, qid: i32, pkts: *mut *mut MBuf, len: i32) -> i32;
    fn send_pkts(port: i32, qid: i32, pkts: *mut *mut MBuf, len: i32) -> i32;
    fn num_pmd_ports() -> i32;
    fn rte_eth_macaddr_get(port: i32, address: *mut MacAddress);
    fn init_bess_eth_ring(ifname: *const u8, core: i32) -> i32;
    fn init_ovs_eth_ring(iface: i32, core: i32) -> i32;
    fn find_port_with_pci_address(pciaddr: *const u8) -> i32;
}

// Make this into an input parameter
const NUM_RXD: i32 = 128;
const NUM_TXD: i32 = 128;

struct PmdStats {
    pub stats: AtomicUsize,
    _pad: [u64; 7],
}

impl PmdStats {
    pub fn new() -> PmdStats {
        PmdStats {
            stats: AtomicUsize::new(0),
            _pad: Default::default(),
        }
    }
}

pub struct PmdPort {
    connected: bool,
    should_close: bool,
    port: i32,
    rxqs: i32,
    txqs: i32,
    stats_rx: Vec<Arc<PmdStats>>,
    stats_tx: Vec<Arc<PmdStats>>,
    _pad: [u64; 4],
}

#[derive(Clone)]
#[repr(C)]
pub struct PortQueue {
    // The Arc cost here should not affect anything, since we are really not doing anything to make it go in and out of
    // scope.
    _pad0: i32,
    pub port: Arc<PmdPort>,
    stats_rx: Arc<PmdStats>,
    stats_tx: Arc<PmdStats>,
    port_id: i32,
    txq: i32,
    rxq: i32,
    _pad1: [u64; 4],
}

impl Drop for PmdPort {
    fn drop(&mut self) {
        if self.connected && self.should_close {
            unsafe {
                free_pmd_port(self.port);
            }
        }
    }
}

/// Represents a single RX/TX queue pair for a port. This is what is needed to send or receive traffic.
impl PortQueue {
    /// Send a batch of packets out this PortQueue. Note this method is internal to ZCSI (should not be directly
    /// called).
    #[inline]
    pub fn send(&mut self, pkts: *mut *mut MBuf, to_send: i32) -> Result<u32> {
        let txq = self.txq;
        self.send_queue(txq, pkts, to_send)
    }

    /// Receive a batch of packets out this PortQueue. Note this method is internal to ZCSI (should not be directly
    /// called).
    #[inline]
    pub fn recv(&self, pkts: *mut *mut MBuf, to_recv: i32) -> Result<u32> {
        let rxq = self.rxq;
        self.recv_queue(rxq, pkts, to_recv)
    }

    #[inline]
    fn send_queue(&mut self, queue: i32, pkts: *mut *mut MBuf, to_send: i32) -> Result<u32> {
        unsafe {
            let sent = send_pkts(self.port_id, queue, pkts, to_send);
            // let update = self.stats_tx.stats.load(Ordering::Relaxed) + sent as usize;
            // self.stats_tx.stats.store(update, Ordering::Relaxed);
            Ok(sent as u32)
        }
    }

    #[inline]
    fn recv_queue(&self, queue: i32, pkts: *mut *mut MBuf, to_recv: i32) -> Result<u32> {
        unsafe {
            let recv = recv_pkts(self.port_id, queue, pkts, to_recv);
            // let update = self.stats_rx.stats.load(Ordering::Relaxed) + recv as usize;
            // self.stats_rx.stats.store(update, Ordering::Relaxed);
            Ok(recv as u32)
        }
    }

    pub fn txq(&self) -> i32 {
        self.txq
    }

    pub fn rxq(&self) -> i32 {
        self.rxq
    }
}

// Utility function to go from Rust bools to C ints. Allowing match bools since this looks nicer to me.
#[cfg_attr(feature = "dev", allow(match_bool))]
fn i32_from_bool(x: bool) -> i32 {
    match x {
        true => 1,
        false => 0,
    }
}

impl PmdPort {
    /// Determine the number of ports in a system.
    pub fn num_pmd_ports() -> i32 {
        unsafe { num_pmd_ports() }
    }

    pub fn find_port_id(pcie: &str) -> i32 {
        unsafe { find_port_with_pci_address(pcie.as_ptr()) }
    }

    pub fn rxqs(&self) -> i32 {
        self.rxqs
    }

    pub fn txqs(&self) -> i32 {
        self.txqs
    }

    pub fn new_queue_pair(port: &Arc<PmdPort>, rxq: i32, txq: i32) -> Result<PortQueue> {
        if rxq > port.rxqs || rxq < 0 {
            Err(ZCSIError::BadRxQueue)
        } else if txq > port.txqs || txq < 0 {
            Err(ZCSIError::BadTxQueue)
        } else {
            Ok(PortQueue {
                port: port.clone(),
                port_id: port.port,
                txq: txq,
                rxq: rxq,
                stats_rx: port.stats_rx[rxq as usize].clone(),
                stats_tx: port.stats_tx[txq as usize].clone(),
                _pad0: Default::default(),
                _pad1: Default::default(),
            })
        }
    }

    /// Current port ID.
    #[inline]
    pub fn name(&self) -> i32 {
        self.port
    }

    /// Get stats for an RX/TX queue pair.
    pub fn stats(&self, queue: i32) -> (usize, usize) {
        let idx = queue as usize;
        (self.stats_rx[idx].stats.load(Ordering::Relaxed), self.stats_tx[idx].stats.load(Ordering::Relaxed))
    }

    /// Create a PMD port with a given number of RX and TXQs.
    pub fn new(port: i32,
               rxqs: i32,
               txqs: i32,
               rx_cores: &[i32],
               tx_cores: &[i32],
               nrxd: i32,
               ntxd: i32,
               loopback: bool,
               tso: bool,
               csumoffload: bool)
               -> Result<Arc<PmdPort>> {

        let loopbackv = i32_from_bool(loopback);
        let tsov = i32_from_bool(tso);
        let csumoffloadv = i32_from_bool(csumoffload);

        if ((txqs as usize) == tx_cores.len()) && ((rxqs as usize) == rx_cores.len()) {
            let ret = unsafe {
                init_pmd_port(port,
                              rxqs,
                              txqs,
                              rx_cores.as_ptr(),
                              tx_cores.as_ptr(),
                              nrxd,
                              ntxd,
                              loopbackv,
                              tsov,
                              csumoffloadv)
            };
            if ret == 0 {
                Ok(Arc::new(PmdPort {
                    connected: true,
                    port: port,
                    rxqs: rxqs,
                    txqs: txqs,
                    should_close: true,
                    stats_rx: (0..rxqs).map(|_| Arc::new(PmdStats::new())).collect(),
                    stats_tx: (0..txqs).map(|_| Arc::new(PmdStats::new())).collect(),
                    _pad: Default::default(),
                }))
            } else {
                Err(ZCSIError::FailedToInitializePort)
            }
        } else {
            Err(ZCSIError::FailedToInitializePort)
        }
    }

    pub fn new_with_one_queue(port: i32,
                              rxcore: i32,
                              txcore: i32,
                              nrxd: i32,
                              ntxd: i32,
                              loopback: bool,
                              tso: bool,
                              csumoffload: bool)
                              -> Result<Arc<PmdPort>> {
        let rx_cores = vec![rxcore];
        let tx_cores = vec![txcore];
        PmdPort::new(port,
                     1,
                     1,
                     &rx_cores[..],
                     &tx_cores[..],
                     nrxd,
                     ntxd,
                     loopback,
                     tso,
                     csumoffload)
    }

    pub fn new_loopback_port(port: i32, core: i32) -> Result<Arc<PmdPort>> {
        PmdPort::new_with_one_queue(port, core, core, NUM_RXD, NUM_TXD, true, false, false)
    }

    pub fn new_simple_port(port: i32, core: i32) -> Result<Arc<PmdPort>> {
        PmdPort::new_with_one_queue(port, core, core, NUM_RXD, NUM_TXD, false, false, false)
    }

    pub fn new_mq_port_with_descriptors(port: i32,
                                        rxqs: i32,
                                        txqs: i32,
                                        rx_cores: &[i32],
                                        tx_cores: &[i32],
                                        rxd: i32,
                                        txd: i32)
                                        -> Result<Arc<PmdPort>> {
        PmdPort::new(port,
                     rxqs,
                     txqs,
                     &rx_cores[..],
                     &tx_cores[..],
                     rxd,
                     txd,
                     false,
                     false,
                     false)
    }

    pub fn new_mq_port(port: i32, rxqs: i32, txqs: i32, rx_cores: &[i32], tx_cores: &[i32]) -> Result<Arc<PmdPort>> {
        PmdPort::new_mq_port_with_descriptors(port,
                                              rxqs,
                                              txqs,
                                              &rx_cores[..],
                                              &tx_cores[..],
                                              NUM_RXD,
                                              NUM_TXD)
    }

    /// Create a new port that can talk to BESS.
    fn new_bess_port(name: &str, core: i32) -> Result<Arc<PmdPort>> {
        // This call returns the port number
        let port = unsafe {
            // This bit should not be required, but is an unfortunate problem with DPDK today.
            init_bess_eth_ring(name.as_ptr(), core)
        };
        // FIXME: Can we really not close?
        if port >= 0 {
            Ok(Arc::new(PmdPort {
                connected: true,
                port: port,
                rxqs: 1,
                txqs: 1,
                should_close: false,
                stats_rx: vec![Arc::new(PmdStats::new())],
                stats_tx: vec![Arc::new(PmdStats::new())],
                _pad: Default::default(),
            }))
        } else {
            Err(ZCSIError::FailedToInitializePort)
        }
    }

    fn new_ovs_port(name: &str, core: i32) -> Result<Arc<PmdPort>> {
        match name.parse() {
            Ok(iface) => {
                // This call returns the port number
                let port = unsafe { init_ovs_eth_ring(iface, core) };
                if port >= 0 {
                    Ok(Arc::new(PmdPort {
                        connected: true,
                        port: port,
                        rxqs: 1,
                        txqs: 1,
                        should_close: false,
                        stats_rx: vec![Arc::new(PmdStats::new())],
                        stats_tx: vec![Arc::new(PmdStats::new())],
                        _pad: Default::default(),
                    }))
                } else {
                    Err(ZCSIError::FailedToInitializePort)
                }
            }
            _ => Err(ZCSIError::BadVdev),
        }
    }

    pub fn new_vdev(name: &str, core: i32) -> Result<Arc<PmdPort>> {
        let parts: Vec<_> = name.split(':').collect();
        if parts.len() == 2 {
            match parts[0] {
                "bess" => PmdPort::new_bess_port(parts[1], core),
                "ovs" => PmdPort::new_ovs_port(parts[1], core),
                _ => Err(ZCSIError::BadVdev),
            }
        } else {
            Err(ZCSIError::BadVdev)
        }
    }

    pub fn null_port() -> Result<Arc<PmdPort>> {
        Ok(Arc::new(PmdPort {
            connected: false,
            port: 0,
            rxqs: 0,
            txqs: 0,
            should_close: false,
            stats_rx: vec![Arc::new(PmdStats::new())],
            stats_tx: vec![Arc::new(PmdStats::new())],
            _pad: Default::default(),
        }))
    }

    #[inline]
    pub fn mac_address(&self) -> MacAddress {
        let mut address = MacAddress { addr: [0; 6] };
        unsafe {
            rte_eth_macaddr_get(self.port, &mut address as *mut MacAddress);
            address
        }
    }
}
<commit_msg>Reenable statistics<commit_after>use super::mbuf::MBuf;
use super::interface::Result;
use super::interface::ZCSIError;
use super::super::headers::MacAddress;
use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};

// External DPDK calls
#[link(name = "zcsi")]
extern "C" {
    fn init_pmd_port(port: i32,
                     rxqs: i32,
                     txqs: i32,
                     rx_cores: *const i32,
                     tx_cores: *const i32,
                     nrxd: i32,
                     ntxd: i32,
                     loopback: i32,
                     tso: i32,
                     csumoffload: i32)
                     -> i32;
    fn free_pmd_port(port: i32) -> i32;
    fn recv_pkts(port: i32, qid: i32, pkts: *mut *mut MBuf, len: i32) -> i32;
    fn send_pkts(port: i32, qid: i32, pkts: *mut *mut MBuf, len: i32) -> i32;
    fn num_pmd_ports() -> i32;
    fn rte_eth_macaddr_get(port: i32, address: *mut MacAddress);
    fn init_bess_eth_ring(ifname: *const u8, core: i32) -> i32;
    fn init_ovs_eth_ring(iface: i32, core: i32) -> i32;
    fn find_port_with_pci_address(pciaddr: *const u8) -> i32;
}

// Make this into an input parameter
const NUM_RXD: i32 = 128;
const NUM_TXD: i32 = 128;

struct PmdStats {
    pub stats: AtomicUsize,
    _pad: [u64; 7],
}

impl PmdStats {
    pub fn new() -> PmdStats {
        PmdStats {
            stats: AtomicUsize::new(0),
            _pad: Default::default(),
        }
    }
}

pub struct PmdPort {
    connected: bool,
    should_close: bool,
    port: i32,
    rxqs: i32,
    txqs: i32,
    stats_rx: Vec<Arc<PmdStats>>,
    stats_tx: Vec<Arc<PmdStats>>,
}

#[derive(Clone)]
#[repr(C)]
pub struct PortQueue {
    // The Arc cost here should not affect anything, since we are really not doing anything to make it go in and out of
    // scope.
    pub port: Arc<PmdPort>,
    stats_rx: Arc<PmdStats>,
    stats_tx: Arc<PmdStats>,
    port_id: i32,
    txq: i32,
    rxq: i32,
    _pad0: i32,
    _pad1: [u64; 3],
}

impl Drop for PmdPort {
    fn drop(&mut self) {
        if self.connected && self.should_close {
            unsafe {
                free_pmd_port(self.port);
            }
        }
    }
}

/// Represents a single RX/TX queue pair for a port. This is what is needed to send or receive traffic.
impl PortQueue {
    /// Send a batch of packets out this PortQueue. Note this method is internal to ZCSI (should not be directly
    /// called).
    #[inline]
    pub fn send(&mut self, pkts: *mut *mut MBuf, to_send: i32) -> Result<u32> {
        let txq = self.txq;
        self.send_queue(txq, pkts, to_send)
    }

    /// Receive a batch of packets out this PortQueue. Note this method is internal to ZCSI (should not be directly
    /// called).
    #[inline]
    pub fn recv(&self, pkts: *mut *mut MBuf, to_recv: i32) -> Result<u32> {
        let rxq = self.rxq;
        self.recv_queue(rxq, pkts, to_recv)
    }

    #[inline]
    fn send_queue(&mut self, queue: i32, pkts: *mut *mut MBuf, to_send: i32) -> Result<u32> {
        unsafe {
            let sent = send_pkts(self.port_id, queue, pkts, to_send);
            let update = self.stats_tx.stats.load(Ordering::Relaxed) + sent as usize;
            self.stats_tx.stats.store(update, Ordering::Relaxed);
            Ok(sent as u32)
        }
    }

    #[inline]
    fn recv_queue(&self, queue: i32, pkts: *mut *mut MBuf, to_recv: i32) -> Result<u32> {
        unsafe {
            let recv = recv_pkts(self.port_id, queue, pkts, to_recv);
            let update = self.stats_rx.stats.load(Ordering::Relaxed) + recv as usize;
            self.stats_rx.stats.store(update, Ordering::Relaxed);
            Ok(recv as u32)
        }
    }

    pub fn txq(&self) -> i32 {
        self.txq
    }

    pub fn rxq(&self) -> i32 {
        self.rxq
    }
}

// Utility function to go from Rust bools to C ints. Allowing match bools since this looks nicer to me.
#[cfg_attr(feature = "dev", allow(match_bool))]
fn i32_from_bool(x: bool) -> i32 {
    match x {
        true => 1,
        false => 0,
    }
}

impl PmdPort {
    /// Determine the number of ports in a system.
    pub fn num_pmd_ports() -> i32 {
        unsafe { num_pmd_ports() }
    }

    pub fn find_port_id(pcie: &str) -> i32 {
        unsafe { find_port_with_pci_address(pcie.as_ptr()) }
    }

    pub fn rxqs(&self) -> i32 {
        self.rxqs
    }

    pub fn txqs(&self) -> i32 {
        self.txqs
    }

    pub fn new_queue_pair(port: &Arc<PmdPort>, rxq: i32, txq: i32) -> Result<PortQueue> {
        if rxq > port.rxqs || rxq < 0 {
            Err(ZCSIError::BadRxQueue)
        } else if txq > port.txqs || txq < 0 {
            Err(ZCSIError::BadTxQueue)
        } else {
            Ok(PortQueue {
                port: port.clone(),
                port_id: port.port,
                txq: txq,
                rxq: rxq,
                stats_rx: port.stats_rx[rxq as usize].clone(),
                stats_tx: port.stats_tx[txq as usize].clone(),
                _pad0: Default::default(),
                _pad1: Default::default(),
            })
        }
    }

    /// Current port ID.
    #[inline]
    pub fn name(&self) -> i32 {
        self.port
    }

    /// Get stats for an RX/TX queue pair.
    pub fn stats(&self, queue: i32) -> (usize, usize) {
        let idx = queue as usize;
        (self.stats_rx[idx].stats.load(Ordering::Relaxed), self.stats_tx[idx].stats.load(Ordering::Relaxed))
    }

    /// Create a PMD port with a given number of RX and TXQs.
    pub fn new(port: i32,
               rxqs: i32,
               txqs: i32,
               rx_cores: &[i32],
               tx_cores: &[i32],
               nrxd: i32,
               ntxd: i32,
               loopback: bool,
               tso: bool,
               csumoffload: bool)
               -> Result<Arc<PmdPort>> {

        let loopbackv = i32_from_bool(loopback);
        let tsov = i32_from_bool(tso);
        let csumoffloadv = i32_from_bool(csumoffload);

        if ((txqs as usize) == tx_cores.len()) && ((rxqs as usize) == rx_cores.len()) {
            let ret = unsafe {
                init_pmd_port(port,
                              rxqs,
                              txqs,
                              rx_cores.as_ptr(),
                              tx_cores.as_ptr(),
                              nrxd,
                              ntxd,
                              loopbackv,
                              tsov,
                              csumoffloadv)
            };
            if ret == 0 {
                Ok(Arc::new(PmdPort {
                    connected: true,
                    port: port,
                    rxqs: rxqs,
                    txqs: txqs,
                    should_close: true,
                    stats_rx: (0..rxqs).map(|_| Arc::new(PmdStats::new())).collect(),
                    stats_tx: (0..txqs).map(|_| Arc::new(PmdStats::new())).collect(),
                }))
            } else {
                Err(ZCSIError::FailedToInitializePort)
            }
        } else {
            Err(ZCSIError::FailedToInitializePort)
        }
    }

    pub fn new_with_one_queue(port: i32,
                              rxcore: i32,
                              txcore: i32,
                              nrxd: i32,
                              ntxd: i32,
                              loopback: bool,
                              tso: bool,
                              csumoffload: bool)
                              -> Result<Arc<PmdPort>> {
        let rx_cores = vec![rxcore];
        let tx_cores = vec![txcore];
        PmdPort::new(port,
                     1,
                     1,
                     &rx_cores[..],
                     &tx_cores[..],
                     nrxd,
                     ntxd,
                     loopback,
                     tso,
                     csumoffload)
    }

    pub fn new_loopback_port(port: i32, core: i32) -> Result<Arc<PmdPort>> {
        PmdPort::new_with_one_queue(port, core, core, NUM_RXD, NUM_TXD, true, false, false)
    }

    pub fn new_simple_port(port: i32, core: i32) -> Result<Arc<PmdPort>> {
        PmdPort::new_with_one_queue(port, core, core, NUM_RXD, NUM_TXD, false, false, false)
    }

    pub fn new_mq_port_with_descriptors(port: i32,
                                        rxqs: i32,
                                        txqs: i32,
                                        rx_cores: &[i32],
                                        tx_cores: &[i32],
                                        rxd: i32,
                                        txd: i32)
                                        -> Result<Arc<PmdPort>> {
        PmdPort::new(port,
                     rxqs,
                     txqs,
                     &rx_cores[..],
                     &tx_cores[..],
                     rxd,
                     txd,
                     false,
                     false,
                     false)
    }

    pub fn new_mq_port(port: i32, rxqs: i32, txqs: i32, rx_cores: &[i32], tx_cores: &[i32]) -> Result<Arc<PmdPort>> {
        PmdPort::new_mq_port_with_descriptors(port,
                                              rxqs,
                                              txqs,
                                              &rx_cores[..],
                                              &tx_cores[..],
                                              NUM_RXD,
                                              NUM_TXD)
    }

    /// Create a new port that can talk to BESS.
    fn new_bess_port(name: &str, core: i32) -> Result<Arc<PmdPort>> {
        // This call returns the port number
        let port = unsafe {
            // This bit should not be required, but is an unfortunate problem with DPDK today.
            init_bess_eth_ring(name.as_ptr(), core)
        };
        // FIXME: Can we really not close?
        if port >= 0 {
            Ok(Arc::new(PmdPort {
                connected: true,
                port: port,
                rxqs: 1,
                txqs: 1,
                should_close: false,
                stats_rx: vec![Arc::new(PmdStats::new())],
                stats_tx: vec![Arc::new(PmdStats::new())],
            }))
        } else {
            Err(ZCSIError::FailedToInitializePort)
        }
    }

    fn new_ovs_port(name: &str, core: i32) -> Result<Arc<PmdPort>> {
        match name.parse() {
            Ok(iface) => {
                // This call returns the port number
                let port = unsafe { init_ovs_eth_ring(iface, core) };
                if port >= 0 {
                    Ok(Arc::new(PmdPort {
                        connected: true,
                        port: port,
                        rxqs: 1,
                        txqs: 1,
                        should_close: false,
                        stats_rx: vec![Arc::new(PmdStats::new())],
                        stats_tx: vec![Arc::new(PmdStats::new())],
                    }))
                } else {
                    Err(ZCSIError::FailedToInitializePort)
                }
            }
            _ => Err(ZCSIError::BadVdev),
        }
    }

    pub fn new_vdev(name: &str, core: i32) -> Result<Arc<PmdPort>> {
        let parts: Vec<_> = name.split(':').collect();
        if parts.len() == 2 {
            match parts[0] {
                "bess" => PmdPort::new_bess_port(parts[1], core),
                "ovs" => PmdPort::new_ovs_port(parts[1], core),
                _ => Err(ZCSIError::BadVdev),
            }
        } else {
            Err(ZCSIError::BadVdev)
        }
    }

    pub fn null_port() -> Result<Arc<PmdPort>> {
        Ok(Arc::new(PmdPort {
            connected: false,
            port: 0,
            rxqs: 0,
            txqs: 0,
            should_close: false,
            stats_rx: vec![Arc::new(PmdStats::new())],
            stats_tx: vec![Arc::new(PmdStats::new())],
        }))
    }

    #[inline]
    pub fn mac_address(&self) -> MacAddress {
        let mut address = MacAddress { addr: [0; 6] };
        unsafe {
            rte_eth_macaddr_get(self.port, &mut address as *mut MacAddress);
            address
        }
    }
}
<|endoftext|>