<commit_before>#![cfg(windows)]

extern crate libc;
extern crate winapi;
extern crate "kernel32-sys" as kernel32;

use self::kernel32::*;
use self::winapi::*;
use std::ptr;
use std::iter::repeat;
use std::cmp::min;
use screen::Key;
use screen::Key::*;

extern "system" {
    fn CreateFileA(lpFileName: LPCSTR, dwDesiredAccess: DWORD, dwShareMode: DWORD, lpSecurityAttributes: LPSECURITY_ATTRIBUTES, dwCreationDisposition: DWORD, dwFlagsAndAttributes: DWORD, hTemplateFile: HANDLE) -> HANDLE;
    fn ReadFile(hFile: HANDLE, lpBuffer: LPVOID, nNumberOfBytesToRead: DWORD, lpNumberOfBytesRead: LPDWORD, lpOverlapped: LPOVERLAPPED) -> BOOL;
    fn WriteFile(hFile: HANDLE, lpBuffer: LPVOID, nNumberOfBytesToWrite: DWORD, lpNumberOfBytesWritten: LPDWORD, lpOverlapped: LPOVERLAPPED) -> BOOL;
}

macro_rules! win32 {
    ($funcall:expr) => (
        if unsafe { $funcall } == 0 {
            panic!("Win32 call failed");
        }
    );
}

pub struct Screen {
    pub height: u16,
    pub width: u16,
    pub visible_choices: u16,
    pub start_line: u16,
    original_console_mode: DWORD,
    original_colors: WORD,
    conin: HANDLE,
    conout: HANDLE,
}

impl Drop for Screen {
    fn drop(&mut self) {
        win32!(SetConsoleMode(self.conout, self.original_console_mode));
    }
}

impl Screen {
    pub fn open_screen() -> Screen {
        let mut orig_mode;
        let conin: HANDLE;
        let conout: HANDLE;
        unsafe {
            // Unlike in Linux, we *could* use the default stdin/stdout file handles to talk
            // directly to the console. However, we need the default stdin handle for the list of
            // choices, and we need to write just the final selection to stdout. Therefore we have
            // to explicitly create new handles to talk to the console. The gory details are
            // available at:
            //
            // https://msdn.microsoft.com/en-us/library/windows/desktop/ms682075%28v=vs.85%29.aspx
            // http://stackoverflow.com/questions/377152/what-does-createfileconin-do
            const OPEN_EXISTING: DWORD = 3;
            let rw_access = GENERIC_READ | GENERIC_WRITE;
            conin = CreateFileA("CONIN$\0".as_ptr() as *const i8, rw_access, FILE_SHARE_READ, ptr::null_mut(), OPEN_EXISTING, 0, ptr::null_mut());
            conout = CreateFileA("CONOUT$\0".as_ptr() as *const i8, rw_access, FILE_SHARE_READ, ptr::null_mut(), OPEN_EXISTING, 0, ptr::null_mut());
            orig_mode = ::std::mem::uninitialized();
        }

        if conin == INVALID_HANDLE_VALUE || conout == INVALID_HANDLE_VALUE {
            panic!("Unable to open console");
        }

        let (cols, rows) = Screen::winsize(conout).unwrap();

        win32!(GetConsoleMode(conout, &mut orig_mode));
        let new_mode = orig_mode & !(ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT);
        win32!(SetConsoleMode(conin, new_mode));

        let visible_choices = min(20, rows - 1);
        let start_line = rows - visible_choices - 1;
        let original_colors = Screen::get_original_colors(conout);
        Screen {
            height: rows,
            width: cols,
            visible_choices: visible_choices,
            start_line: start_line,
            original_console_mode: orig_mode,
            original_colors: original_colors,
            conin: conin,
            conout: conout,
        }
    }

    pub fn move_cursor(&mut self, line: u16, column: u16) {
        win32!(SetConsoleCursorPosition(self.conout, COORD { X: column as i16, Y: line as i16}));
    }

    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.write(&blank_line);
        }
        self.move_cursor(start_line, 0);
    }

    pub fn show_cursor(&mut self) {
        let cursor_info = CONSOLE_CURSOR_INFO { dwSize: 100, bVisible: TRUE };
        win32!(SetConsoleCursorInfo(self.conout, &cursor_info));
    }

    pub fn hide_cursor(&mut self) {
        let cursor_info = CONSOLE_CURSOR_INFO { dwSize: 100, bVisible: FALSE };
        win32!(SetConsoleCursorInfo(self.conout, &cursor_info));
    }

    pub fn write(&mut self, s: &str) {
        let mut copy = s.to_string();
        let len: DWORD = copy.len() as DWORD;
        copy.push('\0');
        let mut bytes_written: DWORD = 0;
        win32!(WriteFile(self.conout, copy.as_ptr() as PVOID, len, &mut bytes_written as LPDWORD, ptr::null_mut()));
    }

    pub fn write_inverted(&mut self, s: &str) {
        let orig = self.original_colors;
        const BLACK_ON_WHITE: WORD = (BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE) as WORD;
        self.set_colors(BLACK_ON_WHITE);
        self.write(s);
        self.set_colors(orig);
    }

    fn set_colors(&mut self, colors: WORD) {
        win32!(SetConsoleTextAttribute(self.conout, colors));
    }

    // Currently the Windows implementation does not buffer, so this function just performs a
    // blocking read of a single key.
    pub fn get_buffered_keys(&mut self) -> Vec<Key> {
        let mut buf: Vec<u16> = repeat(0u16).take(0x1000).collect();
        let mut chars_read: DWORD = 0;
        win32!(ReadFile(self.conin, buf.as_mut_ptr() as LPVOID, 1, &mut chars_read as LPDWORD, ptr::null_mut()));

        let mut ret = Vec::new();
        for i in range(0, chars_read) {
            ret.push(Screen::translate_byte(buf[i as usize]));
        }
        ret
    }

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

    fn get_original_colors(handle: HANDLE) -> WORD {
        let mut buffer_info = unsafe { ::std::mem::uninitialized() };
        win32!(GetConsoleScreenBufferInfo(handle, &mut buffer_info));
        buffer_info.wAttributes
    }

    fn winsize(conin: HANDLE) -> Option<(u16, u16)> {
        let mut buffer_info = unsafe { ::std::mem::uninitialized() };
        let result = unsafe { GetConsoleScreenBufferInfo(conin, &mut buffer_info) };
        if result != 0 {
            // This code specifically computes the size of the window,
            // *not* the size of the buffer (which is easily available
            // from dwSize). I got the algorithm from:
            //
            // http://stackoverflow.com/a/12642749
            let left = buffer_info.srWindow.Left;
            let top = buffer_info.srWindow.Top;
            let right = buffer_info.srWindow.Right;
            let bottom = buffer_info.srWindow.Bottom;
            let cols = right - left + 1;
            let rows = bottom - top + 1;
            Some((cols as u16, rows as u16))
        } else {
            None
        }
    }
}

#[test]
fn winsize_test() {
    let conout = unsafe { kernel32::GetStdHandle(-11) };
    let (cols, rows) = Screen::winsize(conout).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>Remove deprecated core features<commit_after>#![cfg(windows)]

extern crate libc;
extern crate winapi;
extern crate "kernel32-sys" as kernel32;

use self::kernel32::*;
use self::winapi::*;
use std::ptr;
use std::iter::repeat;
use std::cmp::min;
use screen::Key;
use screen::Key::*;

extern "system" {
    fn CreateFileA(lpFileName: LPCSTR, dwDesiredAccess: DWORD, dwShareMode: DWORD, lpSecurityAttributes: LPSECURITY_ATTRIBUTES, dwCreationDisposition: DWORD, dwFlagsAndAttributes: DWORD, hTemplateFile: HANDLE) -> HANDLE;
    fn ReadFile(hFile: HANDLE, lpBuffer: LPVOID, nNumberOfBytesToRead: DWORD, lpNumberOfBytesRead: LPDWORD, lpOverlapped: LPOVERLAPPED) -> BOOL;
    fn WriteFile(hFile: HANDLE, lpBuffer: LPVOID, nNumberOfBytesToWrite: DWORD, lpNumberOfBytesWritten: LPDWORD, lpOverlapped: LPOVERLAPPED) -> BOOL;
}

macro_rules! win32 {
    ($funcall:expr) => (
        if unsafe { $funcall } == 0 {
            panic!("Win32 call failed");
        }
    );
}

pub struct Screen {
    pub height: u16,
    pub width: u16,
    pub visible_choices: u16,
    pub start_line: u16,
    original_console_mode: DWORD,
    original_colors: WORD,
    conin: HANDLE,
    conout: HANDLE,
}

impl Drop for Screen {
    fn drop(&mut self) {
        win32!(SetConsoleMode(self.conout, self.original_console_mode));
    }
}

impl Screen {
    pub fn open_screen() -> Screen {
        let mut orig_mode;
        let conin: HANDLE;
        let conout: HANDLE;
        unsafe {
            // Unlike in Linux, we *could* use the default stdin/stdout file handles to talk
            // directly to the console. However, we need the default stdin handle for the list of
            // choices, and we need to write just the final selection to stdout. Therefore we have
            // to explicitly create new handles to talk to the console. The gory details are
            // available at:
            //
            // https://msdn.microsoft.com/en-us/library/windows/desktop/ms682075%28v=vs.85%29.aspx
            // http://stackoverflow.com/questions/377152/what-does-createfileconin-do
            const OPEN_EXISTING: DWORD = 3;
            let rw_access = GENERIC_READ | GENERIC_WRITE;
            conin = CreateFileA("CONIN$\0".as_ptr() as *const i8, rw_access, FILE_SHARE_READ, ptr::null_mut(), OPEN_EXISTING, 0, ptr::null_mut());
            conout = CreateFileA("CONOUT$\0".as_ptr() as *const i8, rw_access, FILE_SHARE_READ, ptr::null_mut(), OPEN_EXISTING, 0, ptr::null_mut());
            orig_mode = ::std::mem::uninitialized();
        }

        if conin == INVALID_HANDLE_VALUE || conout == INVALID_HANDLE_VALUE {
            panic!("Unable to open console");
        }

        let (cols, rows) = Screen::winsize(conout).unwrap();

        win32!(GetConsoleMode(conout, &mut orig_mode));
        let new_mode = orig_mode & !(ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT);
        win32!(SetConsoleMode(conin, new_mode));

        let visible_choices = min(20, rows - 1);
        let start_line = rows - visible_choices - 1;
        let original_colors = Screen::get_original_colors(conout);
        Screen {
            height: rows,
            width: cols,
            visible_choices: visible_choices,
            start_line: start_line,
            original_console_mode: orig_mode,
            original_colors: original_colors,
            conin: conin,
            conout: conout,
        }
    }

    pub fn move_cursor(&mut self, line: u16, column: u16) {
        win32!(SetConsoleCursorPosition(self.conout, COORD { X: column as i16, Y: line as i16}));
    }

    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 0..self.height {
            self.write(&blank_line);
        }
        self.move_cursor(start_line, 0);
    }

    pub fn show_cursor(&mut self) {
        let cursor_info = CONSOLE_CURSOR_INFO { dwSize: 100, bVisible: TRUE };
        win32!(SetConsoleCursorInfo(self.conout, &cursor_info));
    }

    pub fn hide_cursor(&mut self) {
        let cursor_info = CONSOLE_CURSOR_INFO { dwSize: 100, bVisible: FALSE };
        win32!(SetConsoleCursorInfo(self.conout, &cursor_info));
    }

    pub fn write(&mut self, s: &str) {
        let mut copy = s.to_string();
        let len: DWORD = copy.len() as DWORD;
        copy.push('\0');
        let mut bytes_written: DWORD = 0;
        win32!(WriteFile(self.conout, copy.as_ptr() as PVOID, len, &mut bytes_written as LPDWORD, ptr::null_mut()));
    }

    pub fn write_inverted(&mut self, s: &str) {
        let orig = self.original_colors;
        const BLACK_ON_WHITE: WORD = (BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE) as WORD;
        self.set_colors(BLACK_ON_WHITE);
        self.write(s);
        self.set_colors(orig);
    }

    fn set_colors(&mut self, colors: WORD) {
        win32!(SetConsoleTextAttribute(self.conout, colors));
    }

    // Currently the Windows implementation does not buffer, so this function just performs a
    // blocking read of a single key.
    pub fn get_buffered_keys(&mut self) -> Vec<Key> {
        let mut buf: Vec<u16> = repeat(0u16).take(0x1000).collect();
        let mut chars_read: DWORD = 0;
        win32!(ReadFile(self.conin, buf.as_mut_ptr() as LPVOID, 1, &mut chars_read as LPDWORD, ptr::null_mut()));

        let mut ret = Vec::new();
        for i in 0..chars_read {
            ret.push(Screen::translate_byte(buf[i as usize]));
        }
        ret
    }

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

    fn get_original_colors(handle: HANDLE) -> WORD {
        let mut buffer_info = unsafe { ::std::mem::uninitialized() };
        win32!(GetConsoleScreenBufferInfo(handle, &mut buffer_info));
        buffer_info.wAttributes
    }

    fn winsize(conin: HANDLE) -> Option<(u16, u16)> {
        let mut buffer_info = unsafe { ::std::mem::uninitialized() };
        let result = unsafe { GetConsoleScreenBufferInfo(conin, &mut buffer_info) };
        if result != 0 {
            // This code specifically computes the size of the window,
            // *not* the size of the buffer (which is easily available
            // from dwSize). I got the algorithm from:
            //
            // http://stackoverflow.com/a/12642749
            let left = buffer_info.srWindow.Left;
            let top = buffer_info.srWindow.Top;
            let right = buffer_info.srWindow.Right;
            let bottom = buffer_info.srWindow.Bottom;
            let cols = right - left + 1;
            let rows = bottom - top + 1;
            Some((cols as u16, rows as u16))
        } else {
            None
        }
    }
}

#[test]
fn winsize_test() {
    let conout = unsafe { kernel32::GetStdHandle(-11) };
    let (cols, rows) = Screen::winsize(conout).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>//! Provides sourcemap validation functionality.
use std::fs;
use std::io;
use std::env;
use std::io::{Read, Write};
use std::cell::RefCell;
use std::collections::{HashSet, HashMap};
use std::iter::FromIterator;
use std::path::{Path, PathBuf};

use term;
use url::Url;
use api::{Api, FileContents};
use sourcemap;
use might_be_minified;

use prelude::*;

fn join_url(base_url: &str, url: &str) -> Result<String> {
    if base_url.starts_with("~/") {
        match Url::parse(&format!("http://{}", base_url))?.join(url) {
            Ok(url) => {
                let rv = url.to_string();
                if rv.starts_with("http://~/") {
                    Ok(format!("~/{}", &rv[9..]))
                } else {
                    Ok(rv)
                }
            },
            Err(x) => fail!(x)
        }
    } else {
        Ok(Url::parse(base_url)?.join(url)?.to_string())
    }
}

fn split_url(url: &str) -> (Option<&str>, &str, Option<&str>) {
    let mut part_iter = url.rsplitn(2, '/');
    let (filename, ext) = part_iter.next().map(|x| {
        let mut fn_iter = x.splitn(2, '.');
        (fn_iter.next(), fn_iter.next())
    }).unwrap_or((None, None));
    let mut path = part_iter.next();
    (path, filename.unwrap_or(""), ext)
}

fn unsplit_url(path: Option<&str>, basename: &str, ext: Option<&str>) -> String {
    let mut rv = String::new();
    if let Some(path) = path {
        rv.push_str(path);
        rv.push('/');
    }
    rv.push_str(basename);
    if let Some(ext) = ext {
        rv.push('.');
        rv.push_str(ext);
    }
    rv
}

fn find_sourcemap_reference(
    sourcemaps: &HashSet<String>, min_url: &str) -> Result<String>
{
    // if there is only one sourcemap in total we just assume that's the one.
    // We just need to make sure that we fix up the reference if we need to
    // (eg: ~/ -> /).
    if sourcemaps.len() == 1 {
        let rv = sourcemaps.iter().next().unwrap();
        if rv.starts_with("~/") {
            return Ok(rv[2..].to_string());
        }
        return Ok(rv.to_string());
    }

    let (path, basename, ext) = split_url(min_url);

    // foo.min.js -> foo.map
    if sourcemaps.contains(&unsplit_url(path, basename, Some("map"))) {
        return Ok(unsplit_url(None, basename, Some("map")));
    }

    if let Some(ext) = ext.as_ref() {
        // foo.min.js -> foo.min.js.map
        let new_ext = format!("{}.map", ext);
        if sourcemaps.contains(&unsplit_url(path, basename, Some(&new_ext))) {
            return Ok(unsplit_url(None, basename, Some(&new_ext)));
        }

        // foo.min.js -> foo.js.map
        if ext.starts_with("min.") {
            let new_ext = format!("{}.map", &ext[4..]);
            if sourcemaps.contains(&unsplit_url(path, basename, Some(&new_ext))) {
                return Ok(unsplit_url(None, basename, Some(&new_ext)));
            }
        }
    }

    fail!("Could not auto-detect referenced sourcemap for {}.", min_url);
}

#[derive(PartialEq)]
enum SourceType {
    Script,
    MinifiedScript,
    SourceMap,
}

#[derive(PartialEq, Debug)]
enum LogLevel {
    Info,
    Warning,
    Error,
}

impl LogLevel {
    fn is_insignificant(&self) -> bool {
        *self == LogLevel::Info
    }
}

struct Source {
    url: String,
    file_path: PathBuf,
    contents: String,
    ty: SourceType,
    skip_upload: bool,
    headers: Vec<(String, String)>,
}

struct Log {
    last_source: RefCell<Option<String>>,
    verbose: bool,
}

pub struct SourceMapProcessor {
    sources: HashMap<String, Source>,
    log: Log,
}

impl Log {
    pub fn new(verbose: bool) -> Log {
        Log {
            last_source: RefCell::new(None),
            verbose: verbose,
        }
    }

    pub fn log(&self, source: &Source, level: LogLevel, message: String) {
        let mut out_term;
        let mut out_stderr;
        let mut w = if let Some(mut term) = term::stderr() {
            match level {
                LogLevel::Error => { term.fg(term::color::RED).ok(); }
                LogLevel::Warning => { term.fg(term::color::YELLOW).ok(); }
                LogLevel::Info => {}
            }
            out_term = term;
            &mut out_term as &mut Write
        } else {
            out_stderr = io::stderr();
            &mut out_stderr as &mut Write
        };

        {
            let mut last_source = self.last_source.borrow_mut();
            if last_source.as_ref() != Some(&source.url) {
                *last_source = Some(source.url.clone());
                writeln!(w, "  {}", source.url).ok();
            }
        }
        if level.is_insignificant() && !self.verbose {
            return;
        }
        writeln!(w, "    {:?}: {}", level, &message).ok();
        if let Some(mut term) = term::stderr() {
            term.reset().ok();
        }
    }

    pub fn error(&self, source: &Source, message: String) {
        self.log(source, LogLevel::Error, message);
    }

    pub fn warn(&self, source: &Source, message: String) {
        self.log(source, LogLevel::Warning, message);
    }

    pub fn info(&self, source: &Source, message: String) {
        self.log(source, LogLevel::Info, message);
    }
}

impl SourceMapProcessor {
    /// Creates a new sourcemap validator.  If it's set to verbose
    /// it prints the progress to stdout.
    pub fn new(verbose: bool) -> SourceMapProcessor {
        SourceMapProcessor {
            sources: HashMap::new(),
            log: Log::new(verbose),
        }
    }

    /// Adds a new file for processing.
    pub fn add(&mut self, url: &str, path: &Path) -> Result<()> {
        let mut f = fs::File::open(&path)?;
        let mut contents = String::new();
        try!(f.read_to_string(&mut contents));
        let ty = if sourcemap::is_sourcemap_slice(contents.as_bytes()) {
            SourceType::SourceMap
        } else if path.file_name().and_then(|x| x.to_str())
            .map(|x| x.contains(".min.")).unwrap_or(false) ||
            might_be_minified::analyze_str(&contents).is_likely_minified() {
            SourceType::MinifiedScript
        } else {
            SourceType::Script
        };

        self.sources.insert(url.to_owned(), Source {
            url: url.to_owned(),
            file_path: path.to_path_buf(),
            contents: contents,
            ty: ty,
            skip_upload: false,
            headers: vec![],
        });
        Ok(())
    }

    fn validate_script(&self, source: &Source) -> Result<()> {
        let reference = sourcemap::locate_sourcemap_reference_slice(
            source.contents.as_bytes())?;
        if let sourcemap::SourceMapRef::LegacyRef(_) = reference {
            self.log.warn(source, "encountered a legacy reference".into());
        }
        if let Some(url) = reference.get_url() {
            let full_url = join_url(&source.url, url)?;
            self.log.info(source, format!("sourcemap at {}", full_url));
        } else if source.ty == SourceType::MinifiedScript {
            self.log.error(source, "missing sourcemap!".into());
        } else {
            self.log.warn(source, "no sourcemap reference".into());
        }
        Ok(())
    }

    fn validate_sourcemap(&self, source: &Source) -> Result<()> {
        match sourcemap::decode_slice(source.contents.as_bytes())? {
            sourcemap::DecodedMap::Regular(sm) => {
                for idx in 0..sm.get_source_count() {
                    let source_url = sm.get_source(idx).unwrap_or("??");
                    if sm.get_source_contents(idx).is_some() ||
                       self.sources.get(source_url).is_some() {
                        self.log.info(source, format!("found source ({})", source_url));
                    } else {
                        self.log.warn(source, format!("missing sourcecode ({})", source_url));
                    }
                }
            },
            sourcemap::DecodedMap::Index(_) => {
                self.log.warn(source, "encountered indexed sourcemap. We \
                              cannot validate those.".into());
            }
        }
        Ok(())
    }

    /// Validates all sources within.
    pub fn validate_all(&self) -> Result<()> {
        let mut sources : Vec<_> = self.sources.iter().map(|x| x.1).collect();
        sources.sort_by_key(|x| &x.url);
        let mut failed = false;

        for source in sources.iter() {
            match source.ty {
                SourceType::Script | SourceType::MinifiedScript => {
                    if let Err(err) = self.validate_script(&source) {
                        self.log.error(&source, format!("failed to process: {}", err));
                        failed = true;
                    }
                },
                SourceType::SourceMap => {
                    if let Err(err) = self.validate_sourcemap(&source) {
                        self.log.error(&source, format!("failed to process: {}", err));
                        failed = true;
                    }
                }
            }
        }
        if failed {
            fail!("Encountered problems when validating sourcemaps.");
        }
        println!("All Good!");
        Ok(())
    }

    /// Automatically rewrite all sourcemaps.
    ///
    /// This inlines sources, flattens indexes and skips individual uploads.
    pub fn rewrite(&mut self, prefixes: &[&str]) -> Result<()> {
        for (_, source) in self.sources.iter_mut() {
            if source.ty != SourceType::SourceMap {
                continue;
            }
            let options = sourcemap::RewriteOptions {
                load_local_source_contents: true,
                strip_prefixes: prefixes,
                ..Default::default()
            };
            let sm = match sourcemap::decode_slice(source.contents.as_bytes())? {
                sourcemap::DecodedMap::Regular(sm) => {
                    sm.rewrite(&options)?
                }
                sourcemap::DecodedMap::Index(smi) => {
                    smi.flatten_and_rewrite(&options)?
                }
            };
            let mut new_source : Vec<u8> = Vec::new();
            sm.to_writer(&mut new_source)?;
            source.contents = String::from_utf8(new_source)?;
        }
        Ok(())
    }

    /// Adds sourcemap references to all minified files
    pub fn add_sourcemap_references(&mut self) -> Result<()> {
        let sourcemaps = HashSet::from_iter(self.sources.iter()
            .map(|x| x.1)
            .filter(|x| x.ty == SourceType::SourceMap)
            .map(|x| x.url.to_string()));
        for (_, source) in self.sources.iter_mut() {
            if source.ty != SourceType::MinifiedScript {
                continue;
            }
            // we silently ignore when we can't find a sourcemap. Maybwe we should
            // log this.
            match find_sourcemap_reference(&sourcemaps, &source.url) {
                Ok(target_url) => {
                    source.headers.push(("Sourcemap".to_string(), target_url));
                },
                Err(err) => {
                    self.log.warn(source, "could not determine a \
                                  sourcemap reference".into());
                }
            }
        }
        Ok(())
    }

    /// Uploads all files
    pub fn upload(&self, api: &Api, org: &str, project: &str, release: &str)
        -> Result<()>
    {
        let here = env::current_dir()?;
        for (_, source) in self.sources.iter() {
            if source.skip_upload {
                continue;
            }
            let display_path = here.strip_prefix(&here);
            println!("{} -> {}", display_path.as_ref().unwrap_or(
                &source.file_path.as_path()).display(), &source.url);
            if let Some(artifact) = api.upload_release_file(
                org, project, &release, FileContents::FromBytes(
                    source.contents.as_bytes()),
                &source.url, Some(source.headers.as_slice()))? {
                println!("  {}  ({} bytes)", artifact.sha1, artifact.size);
            } else {
                println!("  already present");
            }
        }
        Ok(())
    }
}

#[test]
fn test_split_url() {
    assert_eq!(split_url("/foo.js"), (Some(""), "foo", Some("js")));
    assert_eq!(split_url("foo.js"), (None, "foo", Some("js")));
    assert_eq!(split_url("foo"), (None, "foo", None));
    assert_eq!(split_url("/foo"), (Some(""), "foo", None));
}

#[test]
fn test_unsplit_url() {
    assert_eq!(&unsplit_url(Some(""), "foo", Some("js")), "/foo.js");
    assert_eq!(&unsplit_url(None, "foo", Some("js")), "foo.js");
    assert_eq!(&unsplit_url(None, "foo", None), "foo");
    assert_eq!(&unsplit_url(Some(""), "foo", None), "/foo");
}
<commit_msg>Print what type was detected for sourcemap data on upload<commit_after>//! Provides sourcemap validation functionality.
use std::fs;
use std::io;
use std::fmt;
use std::env;
use std::io::{Read, Write};
use std::cell::RefCell;
use std::collections::{HashSet, HashMap};
use std::iter::FromIterator;
use std::path::{Path, PathBuf};

use term;
use url::Url;
use api::{Api, FileContents};
use sourcemap;
use might_be_minified;

use prelude::*;

fn join_url(base_url: &str, url: &str) -> Result<String> {
    if base_url.starts_with("~/") {
        match Url::parse(&format!("http://{}", base_url))?.join(url) {
            Ok(url) => {
                let rv = url.to_string();
                if rv.starts_with("http://~/") {
                    Ok(format!("~/{}", &rv[9..]))
                } else {
                    Ok(rv)
                }
            },
            Err(x) => fail!(x)
        }
    } else {
        Ok(Url::parse(base_url)?.join(url)?.to_string())
    }
}

fn split_url(url: &str) -> (Option<&str>, &str, Option<&str>) {
    let mut part_iter = url.rsplitn(2, '/');
    let (filename, ext) = part_iter.next().map(|x| {
        let mut fn_iter = x.splitn(2, '.');
        (fn_iter.next(), fn_iter.next())
    }).unwrap_or((None, None));
    let path = part_iter.next();
    (path, filename.unwrap_or(""), ext)
}

fn unsplit_url(path: Option<&str>, basename: &str, ext: Option<&str>) -> String {
    let mut rv = String::new();
    if let Some(path) = path {
        rv.push_str(path);
        rv.push('/');
    }
    rv.push_str(basename);
    if let Some(ext) = ext {
        rv.push('.');
        rv.push_str(ext);
    }
    rv
}

fn find_sourcemap_reference(
    sourcemaps: &HashSet<String>, min_url: &str) -> Result<String>
{
    // if there is only one sourcemap in total we just assume that's the one.
    // We just need to make sure that we fix up the reference if we need to
    // (eg: ~/ -> /).
    if sourcemaps.len() == 1 {
        let rv = sourcemaps.iter().next().unwrap();
        if rv.starts_with("~/") {
            return Ok(rv[2..].to_string());
        }
        return Ok(rv.to_string());
    }

    let (path, basename, ext) = split_url(min_url);

    // foo.min.js -> foo.map
    if sourcemaps.contains(&unsplit_url(path, basename, Some("map"))) {
        return Ok(unsplit_url(None, basename, Some("map")));
    }

    if let Some(ext) = ext.as_ref() {
        // foo.min.js -> foo.min.js.map
        let new_ext = format!("{}.map", ext);
        if sourcemaps.contains(&unsplit_url(path, basename, Some(&new_ext))) {
            return Ok(unsplit_url(None, basename, Some(&new_ext)));
        }

        // foo.min.js -> foo.js.map
        if ext.starts_with("min.") {
            let new_ext = format!("{}.map", &ext[4..]);
            if sourcemaps.contains(&unsplit_url(path, basename, Some(&new_ext))) {
                return Ok(unsplit_url(None, basename, Some(&new_ext)));
            }
        }
    }

    fail!("Could not auto-detect referenced sourcemap for {}.", min_url);
}

#[derive(PartialEq)]
enum SourceType {
    Script,
    MinifiedScript,
    SourceMap,
}

impl fmt::Display for SourceType {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            SourceType::Script => write!(f, "script"),
            SourceType::MinifiedScript => write!(f, "minified script"),
            SourceType::SourceMap => write!(f, "sourcemap"),
        }
    }
}

#[derive(PartialEq, Debug)]
enum LogLevel {
    Info,
    Warning,
    Error,
}

impl LogLevel {
    fn is_insignificant(&self) -> bool {
        *self == LogLevel::Info
    }
}

struct Source {
    url: String,
    file_path: PathBuf,
    contents: String,
    ty: SourceType,
    skip_upload: bool,
    headers: Vec<(String, String)>,
}

struct Log {
    last_source: RefCell<Option<String>>,
    verbose: bool,
}

pub struct SourceMapProcessor {
    sources: HashMap<String, Source>,
    log: Log,
}

impl Log {
    pub fn new(verbose: bool) -> Log {
        Log {
            last_source: RefCell::new(None),
            verbose: verbose,
        }
    }

    pub fn log(&self, source: &Source, level: LogLevel, message: String) {
        let mut out_term;
        let mut out_stderr;
        let mut w = if let Some(mut term) = term::stderr() {
            match level {
                LogLevel::Error => { term.fg(term::color::RED).ok(); }
                LogLevel::Warning => { term.fg(term::color::YELLOW).ok(); }
                LogLevel::Info => {}
            }
            out_term = term;
            &mut out_term as &mut Write
        } else {
            out_stderr = io::stderr();
            &mut out_stderr as &mut Write
        };

        {
            let mut last_source = self.last_source.borrow_mut();
            if last_source.as_ref() != Some(&source.url) {
                *last_source = Some(source.url.clone());
                writeln!(w, "  {}", source.url).ok();
            }
        }
        if level.is_insignificant() && !self.verbose {
            return;
        }
        writeln!(w, "    {:?}: {}", level, &message).ok();
        if let Some(mut term) = term::stderr() {
            term.reset().ok();
        }
    }

    pub fn error(&self, source: &Source, message: String) {
        self.log(source, LogLevel::Error, message);
    }

    pub fn warn(&self, source: &Source, message: String) {
        self.log(source, LogLevel::Warning, message);
    }

    pub fn info(&self, source: &Source, message: String) {
        self.log(source, LogLevel::Info, message);
    }
}

impl SourceMapProcessor {
    /// Creates a new sourcemap validator.  If it's set to verbose
    /// it prints the progress to stdout.
    pub fn new(verbose: bool) -> SourceMapProcessor {
        SourceMapProcessor {
            sources: HashMap::new(),
            log: Log::new(verbose),
        }
    }

    /// Adds a new file for processing.
    pub fn add(&mut self, url: &str, path: &Path) -> Result<()> {
        let mut f = fs::File::open(&path)?;
        let mut contents = String::new();
        try!(f.read_to_string(&mut contents));
        let ty = if sourcemap::is_sourcemap_slice(contents.as_bytes()) {
            SourceType::SourceMap
        } else if path.file_name().and_then(|x| x.to_str())
            .map(|x| x.contains(".min.")).unwrap_or(false) ||
            might_be_minified::analyze_str(&contents).is_likely_minified() {
            SourceType::MinifiedScript
        } else {
            SourceType::Script
        };

        self.sources.insert(url.to_owned(), Source {
            url: url.to_owned(),
            file_path: path.to_path_buf(),
            contents: contents,
            ty: ty,
            skip_upload: false,
            headers: vec![],
        });
        Ok(())
    }

    fn validate_script(&self, source: &Source) -> Result<()> {
        let reference = sourcemap::locate_sourcemap_reference_slice(
            source.contents.as_bytes())?;
        if let sourcemap::SourceMapRef::LegacyRef(_) = reference {
            self.log.warn(source, "encountered a legacy reference".into());
        }
        if let Some(url) = reference.get_url() {
            let full_url = join_url(&source.url, url)?;
            self.log.info(source, format!("sourcemap at {}", full_url));
        } else if source.ty == SourceType::MinifiedScript {
            self.log.error(source, "missing sourcemap!".into());
        } else {
            self.log.warn(source, "no sourcemap reference".into());
        }
        Ok(())
    }

    fn validate_sourcemap(&self, source: &Source) -> Result<()> {
        match sourcemap::decode_slice(source.contents.as_bytes())? {
            sourcemap::DecodedMap::Regular(sm) => {
                for idx in 0..sm.get_source_count() {
                    let source_url = sm.get_source(idx).unwrap_or("??");
                    if sm.get_source_contents(idx).is_some() ||
                       self.sources.get(source_url).is_some() {
                        self.log.info(source, format!("found source ({})", source_url));
                    } else {
                        self.log.warn(source, format!("missing sourcecode ({})", source_url));
                    }
                }
            },
            sourcemap::DecodedMap::Index(_) => {
                self.log.warn(source, "encountered indexed sourcemap. We \
                              cannot validate those.".into());
            }
        }
        Ok(())
    }

    /// Validates all sources within.
    pub fn validate_all(&self) -> Result<()> {
        let mut sources : Vec<_> = self.sources.iter().map(|x| x.1).collect();
        sources.sort_by_key(|x| &x.url);
        let mut failed = false;

        for source in sources.iter() {
            match source.ty {
                SourceType::Script | SourceType::MinifiedScript => {
                    if let Err(err) = self.validate_script(&source) {
                        self.log.error(&source, format!("failed to process: {}", err));
                        failed = true;
                    }
                },
                SourceType::SourceMap => {
                    if let Err(err) = self.validate_sourcemap(&source) {
                        self.log.error(&source, format!("failed to process: {}", err));
                        failed = true;
                    }
                }
            }
        }
        if failed {
            fail!("Encountered problems when validating sourcemaps.");
        }
        println!("All Good!");
        Ok(())
    }

    /// Automatically rewrite all sourcemaps.
    ///
    /// This inlines sources, flattens indexes and skips individual uploads.
    pub fn rewrite(&mut self, prefixes: &[&str]) -> Result<()> {
        for (_, source) in self.sources.iter_mut() {
            if source.ty != SourceType::SourceMap {
                continue;
            }
            let options = sourcemap::RewriteOptions {
                load_local_source_contents: true,
                strip_prefixes: prefixes,
                ..Default::default()
            };
            let sm = match sourcemap::decode_slice(source.contents.as_bytes())? {
                sourcemap::DecodedMap::Regular(sm) => {
                    sm.rewrite(&options)?
                }
                sourcemap::DecodedMap::Index(smi) => {
                    smi.flatten_and_rewrite(&options)?
                }
            };
            let mut new_source : Vec<u8> = Vec::new();
            sm.to_writer(&mut new_source)?;
            source.contents = String::from_utf8(new_source)?;
        }
        Ok(())
    }

    /// Adds sourcemap references to all minified files
    pub fn add_sourcemap_references(&mut self) -> Result<()> {
        let sourcemaps = HashSet::from_iter(self.sources.iter()
            .map(|x| x.1)
            .filter(|x| x.ty == SourceType::SourceMap)
            .map(|x| x.url.to_string()));
        for (_, source) in self.sources.iter_mut() {
            if source.ty != SourceType::MinifiedScript {
                continue;
            }
            // we silently ignore when we can't find a sourcemap. Maybwe we should
            // log this.
            match find_sourcemap_reference(&sourcemaps, &source.url) {
                Ok(target_url) => {
                    source.headers.push(("Sourcemap".to_string(), target_url));
                },
                Err(err) => {
                    self.log.warn(source, format!("could not determine a \
                                  sourcemap reference ({})", err));
                }
            }
        }
        Ok(())
    }

    /// Uploads all files
    pub fn upload(&self, api: &Api, org: &str, project: &str, release: &str)
        -> Result<()>
    {
        let here = env::current_dir()?;
        for (_, source) in self.sources.iter() {
            if source.skip_upload {
                continue;
            }
            let display_path = here.strip_prefix(&here);
            println!("{} -> {} [{}]", display_path.as_ref().unwrap_or(
                &source.file_path.as_path()).display(), &source.url,
                source.ty);
            if let Some(artifact) = api.upload_release_file(
                org, project, &release, FileContents::FromBytes(
                    source.contents.as_bytes()),
                &source.url, Some(source.headers.as_slice()))? {
                println!("  {}  ({} bytes)", artifact.sha1, artifact.size);
            } else {
                println!("  already present");
            }
        }
        Ok(())
    }
}

#[test]
fn test_split_url() {
    assert_eq!(split_url("/foo.js"), (Some(""), "foo", Some("js")));
    assert_eq!(split_url("foo.js"), (None, "foo", Some("js")));
    assert_eq!(split_url("foo"), (None, "foo", None));
    assert_eq!(split_url("/foo"), (Some(""), "foo", None));
}

#[test]
fn test_unsplit_url() {
    assert_eq!(&unsplit_url(Some(""), "foo", Some("js")), "/foo.js");
    assert_eq!(&unsplit_url(None, "foo", Some("js")), "foo.js");
    assert_eq!(&unsplit_url(None, "foo", None), "foo");
    assert_eq!(&unsplit_url(Some(""), "foo", None), "/foo");
}
<|endoftext|>


<commit_before>use byteorder::{BigEndian, ByteOrder};
use std::io::Cursor;
use super::source_destination::SourceDestination;

pub struct IPv4Header {
    pub version: u8,
    pub header_length: u8,
    pub total_length: u16,
    pub protocol: Protocol,
    pub source: u32,
    pub destination: u32,
}

#[derive(Debug, PartialEq)]
pub enum Protocol {
    TCP,
    UDP,
    OTHER,
}

impl IPv4Header {
    pub fn parse(raw: &[u8]) -> IPv4Header {
        IPv4Header {
            version: raw[0] >> 4,
            header_length: (raw[0] & 0xf) << 2,
            total_length: BigEndian::read_u16(&raw[2..4]),
            protocol: match raw[9] {
                6 => Protocol::TCP,
                17 => Protocol::UDP,
                _ => Protocol::OTHER
            },
            source: BigEndian::read_u32(&raw[12..16]),
            destination: BigEndian::read_u32(&raw[16..20]),
        }
    }

    pub fn set_total_length(&mut self, raw: &mut [u8], total_length: u16) {
        self.total_length = total_length;
        BigEndian::write_u16(&mut raw[2..4], total_length);
    }

    pub fn set_source(&mut self, raw: &mut [u8], source: u32) {
        self.source = source;
        BigEndian::write_u32(&mut raw[12..16], source);
    }

    pub fn set_destination(&mut self, raw: &mut [u8], destination: u32) {
        self.destination = destination;
        BigEndian::write_u32(&mut raw[16..20], destination);
    }

    pub fn compute_checksum(&mut self, raw: &mut [u8]) {
        // reset checksum field
        self.set_checksum(raw, 0);

        let j = self.header_length as usize / 2;
        let mut sum = (0..j).map(|i| {
            let range = 2*i..2*(i+1);
            BigEndian::read_u16(&raw[range]) as u32
        }).sum::<u32>();
        while (sum & !0xffff) != 0 {
            sum = (sum & 0xffff) + (sum >> 16);
        }

        self.set_checksum(raw, !sum as u16);
    }

    fn get_checksum(&mut self, raw: &[u8]) -> u16 {
        BigEndian::read_u16(&raw[10..12])
    }

    fn set_checksum(&mut self, raw: &mut [u8], checksum: u16) {
        BigEndian::write_u16(&mut raw[10..12], checksum);
    }

    pub fn read_version(raw: &[u8]) -> Option<u8> {
        if raw.is_empty() {
            None
        } else {
            // version is stored in the 4 first bits
            Some(raw[0] >> 4)
        }
    }

    pub fn read_length(raw: &[u8]) -> Option<u16> {
        if raw.len() < 4 {
            None
        } else {
            // packet length is 16 bits starting at offset 2
            let length = BigEndian::read_u16(&raw[2..4]);
            Some(length)
        }
    }
}

impl SourceDestination<u32> for IPv4Header {
    fn get_source(&self, _: &[u8]) -> u32 {
        self.source
    }

    fn get_destination(&self, _: &[u8]) -> u32 {
        self.destination
    }

    fn set_source(&mut self, raw: &mut [u8], source: u32) {
        (self as &mut IPv4Header).set_source(raw, source);
    }

    fn set_destination(&mut self, raw: &mut [u8], destination: u32) {
        (self as &mut IPv4Header).set_destination(raw, destination);
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::io::Cursor;
    use byteorder::{BigEndian, WriteBytesExt};

    fn create_header() -> Vec<u8> {
        let mut raw: Vec<u8> = vec![];
        raw.reserve(20);
        raw.write_u8(4u8 << 4 | 5).unwrap(); // version_and_ihl
        raw.write_u8(0).unwrap(); //ToS
        raw.write_u16::<BigEndian>(28).unwrap(); // total length
        raw.write_u32::<BigEndian>(0).unwrap(); // id_flags_fragment_offset
        raw.write_u8(0).unwrap(); // TTL
        raw.write_u8(17).unwrap(); // protocol (UDP)
        raw.write_u16::<BigEndian>(0).unwrap(); // checksum
        raw.write_u32::<BigEndian>(0x12345678).unwrap(); // source address
        raw.write_u32::<BigEndian>(0x42424242).unwrap(); // destination address
        raw
    }

    #[test]
    fn parse_header() {
        let raw = &create_header()[..];
        let data = IPv4Header::parse(raw);
        assert_eq!(4, data.version);
        assert_eq!(20, data.header_length);
        assert_eq!(28, data.total_length);
        assert_eq!(Protocol::UDP, data.protocol);
        assert_eq!(0x12345678, data.source);
        assert_eq!(0x42424242, data.destination);
    }

    #[test]
    fn edit_header() {
        let raw = &mut create_header()[..];
        let mut header = IPv4Header::parse(raw);

        header.set_source(raw, 0x87654321);
        header.set_destination(raw, 0x24242424);
        header.set_total_length(raw, 42);
        assert_eq!(0x87654321, header.source);
        assert_eq!(0x24242424, header.destination);
        assert_eq!(42, header.total_length);

        // assert that the buffer has been modified
        let raw_source = BigEndian::read_u32(&raw[12..16]);
        let raw_destination = BigEndian::read_u32(&raw[16..20]);
        let raw_total_length = BigEndian::read_u16(&raw[2..4]);
        assert_eq!(0x87654321, raw_source);
        assert_eq!(0x24242424, raw_destination);
        assert_eq!(42, raw_total_length);

        header.switch_source_and_destination(raw);

        assert_eq!(0x24242424, header.source);
        assert_eq!(0x87654321, header.destination);

        let raw_source = BigEndian::read_u32(&raw[12..16]);
        let raw_destination = BigEndian::read_u32(&raw[16..20]);
        assert_eq!(0x24242424, raw_source);
        assert_eq!(0x87654321, raw_destination);
    }

    #[test]
    fn compute_checksum() {
        let mut raw = &mut create_header()[..];
        let mut header = IPv4Header::parse(raw);

        // set a fake checksum value to assert that it is correctly computed
        header.set_checksum(raw, 0x79);

        header.compute_checksum(raw);

        let mut sum: u32 = 0x4500 + 0x001C + 0x0000 + 0x0000 + 0x0011
                         + 0x0000 + 0x1234 + 0x5678 + 0x4242 + 0x4242;
        while (sum & !0xffff) != 0 {
            sum = (sum & 0xffff) + (sum >> 16);
        }
        let sum = !sum as u16;
        assert_eq!(sum, header.get_checksum(raw));
    }

    #[test]
    fn read_ip_version_unavailable() {
        let empty_slice = &[][0..0];
        let version = IPv4Header::read_version(empty_slice);
        assert!(version.is_none());
    }

    #[test]
    fn read_ip_version_available() {
        let version_and_ihl: u8 = (4 << 4) | 5;
        let raw = [ version_and_ihl, 0, 0, 0, 0, 0, 0, 0 ];
        let version = IPv4Header::read_version(&raw);
        assert_eq!(4, version.unwrap());
    }
}
<commit_msg>Add tests for IPv4 header length<commit_after>use byteorder::{BigEndian, ByteOrder};
use std::io::Cursor;
use super::source_destination::SourceDestination;

pub struct IPv4Header {
    pub version: u8,
    pub header_length: u8,
    pub total_length: u16,
    pub protocol: Protocol,
    pub source: u32,
    pub destination: u32,
}

#[derive(Debug, PartialEq)]
pub enum Protocol {
    TCP,
    UDP,
    OTHER,
}

impl IPv4Header {
    pub fn parse(raw: &[u8]) -> IPv4Header {
        IPv4Header {
            version: raw[0] >> 4,
            header_length: (raw[0] & 0xf) << 2,
            total_length: BigEndian::read_u16(&raw[2..4]),
            protocol: match raw[9] {
                6 => Protocol::TCP,
                17 => Protocol::UDP,
                _ => Protocol::OTHER
            },
            source: BigEndian::read_u32(&raw[12..16]),
            destination: BigEndian::read_u32(&raw[16..20]),
        }
    }

    pub fn set_total_length(&mut self, raw: &mut [u8], total_length: u16) {
        self.total_length = total_length;
        BigEndian::write_u16(&mut raw[2..4], total_length);
    }

    pub fn set_source(&mut self, raw: &mut [u8], source: u32) {
        self.source = source;
        BigEndian::write_u32(&mut raw[12..16], source);
    }

    pub fn set_destination(&mut self, raw: &mut [u8], destination: u32) {
        self.destination = destination;
        BigEndian::write_u32(&mut raw[16..20], destination);
    }

    pub fn compute_checksum(&mut self, raw: &mut [u8]) {
        // reset checksum field
        self.set_checksum(raw, 0);

        let j = self.header_length as usize / 2;
        let mut sum = (0..j).map(|i| {
            let range = 2*i..2*(i+1);
            BigEndian::read_u16(&raw[range]) as u32
        }).sum::<u32>();
        while (sum & !0xffff) != 0 {
            sum = (sum & 0xffff) + (sum >> 16);
        }

        self.set_checksum(raw, !sum as u16);
    }

    fn get_checksum(&mut self, raw: &[u8]) -> u16 {
        BigEndian::read_u16(&raw[10..12])
    }

    fn set_checksum(&mut self, raw: &mut [u8], checksum: u16) {
        BigEndian::write_u16(&mut raw[10..12], checksum);
    }

    pub fn read_version(raw: &[u8]) -> Option<u8> {
        if raw.is_empty() {
            None
        } else {
            // version is stored in the 4 first bits
            Some(raw[0] >> 4)
        }
    }

    pub fn read_length(raw: &[u8]) -> Option<u16> {
        if raw.len() < 4 {
            None
        } else {
            // packet length is 16 bits starting at offset 2
            let length = BigEndian::read_u16(&raw[2..4]);
            Some(length)
        }
    }
}

impl SourceDestination<u32> for IPv4Header {
    fn get_source(&self, _: &[u8]) -> u32 {
        self.source
    }

    fn get_destination(&self, _: &[u8]) -> u32 {
        self.destination
    }

    fn set_source(&mut self, raw: &mut [u8], source: u32) {
        (self as &mut IPv4Header).set_source(raw, source);
    }

    fn set_destination(&mut self, raw: &mut [u8], destination: u32) {
        (self as &mut IPv4Header).set_destination(raw, destination);
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use byteorder::{BigEndian, WriteBytesExt};

    fn create_header() -> Vec<u8> {
        let mut raw: Vec<u8> = vec![];
        raw.reserve(20);
        raw.write_u8(4u8 << 4 | 5).unwrap(); // version_and_ihl
        raw.write_u8(0).unwrap(); //ToS
        raw.write_u16::<BigEndian>(28).unwrap(); // total length
        raw.write_u32::<BigEndian>(0).unwrap(); // id_flags_fragment_offset
        raw.write_u8(0).unwrap(); // TTL
        raw.write_u8(17).unwrap(); // protocol (UDP)
        raw.write_u16::<BigEndian>(0).unwrap(); // checksum
        raw.write_u32::<BigEndian>(0x12345678).unwrap(); // source address
        raw.write_u32::<BigEndian>(0x42424242).unwrap(); // destination address
        raw
    }

    #[test]
    fn parse_header() {
        let raw = &create_header()[..];
        let data = IPv4Header::parse(raw);
        assert_eq!(4, data.version);
        assert_eq!(20, data.header_length);
        assert_eq!(28, data.total_length);
        assert_eq!(Protocol::UDP, data.protocol);
        assert_eq!(0x12345678, data.source);
        assert_eq!(0x42424242, data.destination);
    }

    #[test]
    fn edit_header() {
        let raw = &mut create_header()[..];
        let mut header = IPv4Header::parse(raw);

        header.set_source(raw, 0x87654321);
        header.set_destination(raw, 0x24242424);
        header.set_total_length(raw, 42);
        assert_eq!(0x87654321, header.source);
        assert_eq!(0x24242424, header.destination);
        assert_eq!(42, header.total_length);

        // assert that the buffer has been modified
        let raw_source = BigEndian::read_u32(&raw[12..16]);
        let raw_destination = BigEndian::read_u32(&raw[16..20]);
        let raw_total_length = BigEndian::read_u16(&raw[2..4]);
        assert_eq!(0x87654321, raw_source);
        assert_eq!(0x24242424, raw_destination);
        assert_eq!(42, raw_total_length);

        header.switch_source_and_destination(raw);

        assert_eq!(0x24242424, header.source);
        assert_eq!(0x87654321, header.destination);

        let raw_source = BigEndian::read_u32(&raw[12..16]);
        let raw_destination = BigEndian::read_u32(&raw[16..20]);
        assert_eq!(0x24242424, raw_source);
        assert_eq!(0x87654321, raw_destination);
    }

    #[test]
    fn compute_checksum() {
        let mut raw = &mut create_header()[..];
        let mut header = IPv4Header::parse(raw);

        // set a fake checksum value to assert that it is correctly computed
        header.set_checksum(raw, 0x79);

        header.compute_checksum(raw);

        let mut sum: u32 = 0x4500 + 0x001C + 0x0000 + 0x0000 + 0x0011
                         + 0x0000 + 0x1234 + 0x5678 + 0x4242 + 0x4242;
        while (sum & !0xffff) != 0 {
            sum = (sum & 0xffff) + (sum >> 16);
        }
        let sum = !sum as u16;
        assert_eq!(sum, header.get_checksum(raw));
    }

    #[test]
    fn read_ip_version_unavailable() {
        let empty_slice = &[][0..0];
        let version = IPv4Header::read_version(empty_slice);
        assert!(version.is_none());
    }

    #[test]
    fn read_ip_version_available() {
        let version_and_ihl: u8 = (4 << 4) | 5;
        let raw = [ version_and_ihl, 0, 0, 0, 0, 0, 0, 0 ];
        let version = IPv4Header::read_version(&raw);
        assert_eq!(4, version.unwrap());
    }

    #[test]
    fn read_ip_length_unavailable() {
        let empty_slice = &[][0..0];
        let length = IPv4Header::read_length(empty_slice);
        assert!(length.is_none());
    }

    #[test]
    fn read_ip_length_available() {
        let raw = [ 0u8, 0, 0x01, 0x23 ];
        let length = IPv4Header::read_length(&raw);
        assert_eq!(0x123, length.unwrap());
    }
}
<|endoftext|>


<commit_before>use elements::deserialize_file;
use elements::{Module, Internal, ExportEntry, InitExpr, Opcode, ValueType, GlobalType, GlobalEntry};
use interpreter::{ExecutionParams, ProgramInstance};
use interpreter::value::RuntimeValue;
use interpreter::module::{ModuleInstanceInterface, ItemIndex};
use builder::module;

#[test]
fn interpreter_inc_i32() {
    // Name of function contained in WASM file (note the leading underline)
    const FUNCTION_NAME: &'static str = "_inc_i32";
    // The WASM file containing the module and function
    const WASM_FILE: &str = &"res/cases/v1/inc_i32.wasm";

    let program = ProgramInstance::new();
	let env_module = module()
		.memory()
			.with_min(256)
			.build()
			.with_export(ExportEntry::new("memory".into(), Internal::Memory(0)))
		.table()
			.with_min(64)
			.build()
			.with_export(ExportEntry::new("table".into(), Internal::Table(0)))
		.with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, false), InitExpr::new(vec![Opcode::I32Const(0)])))
			.with_export(ExportEntry::new("tableBase".into(), Internal::Global(0)))
		.with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, false), InitExpr::new(vec![Opcode::I32Const(0)])))
			.with_export(ExportEntry::new("memoryBase".into(), Internal::Global(1)))
		.build();
	let _env_instance = program.add_module("env", env_module, None).unwrap();

    let module: Module =
        deserialize_file(WASM_FILE).expect("Failed to deserialize module from buffer");
    let i32_val = 42;
    // the functions expects a single i32 parameter
    let args = vec![RuntimeValue::I32(i32_val)];
    let exp_retval = Some(RuntimeValue::I32(i32_val + 1));
    let execution_params = ExecutionParams::from(args);

    let module_result = program
        .add_module("main", module, None);

    let module = module_result
        .expect("Failed to initialize module");

    let retval = module
        .execute_export(FUNCTION_NAME, execution_params)
        .expect("");
    assert_eq!(exp_retval, retval);
}

#[test]
fn interpreter_accumulate_u8() {
    // Name of function contained in WASM file (note the leading underline)
    const FUNCTION_NAME: &'static str = "_accumulate_u8";
    // The WASM file containing the module and function
    const WASM_FILE: &str = &"res/cases/v1/accumulate_u8.wasm";
    // The octet sequence being accumulated
    const BUF: &[u8] = &[9,8,7,6,5,4,3,2,1];

    // Declare the memory limits of the runtime-environment
    let program = ProgramInstance::new();
	let env_module = module()
		.memory()
			.with_min(256)
			.build()
			.with_export(ExportEntry::new("memory".into(), Internal::Memory(0)))
		.table()
			.with_min(64)
			.build()
			.with_export(ExportEntry::new("table".into(), Internal::Table(0)))
		.with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, false), InitExpr::new(vec![Opcode::I32Const(0)])))
			.with_export(ExportEntry::new("tableBase".into(), Internal::Global(0)))
		.with_global(GlobalEntry::new(GlobalType::new(ValueType::I32, false), InitExpr::new(vec![Opcode::I32Const(0)])))
			.with_export(ExportEntry::new("memoryBase".into(), Internal::Global(1)))
		.build();
	let _env_instance = program.add_module("env", env_module, None).unwrap();

    // Load the module-structure from wasm-file and add to program
    let module: Module =
        deserialize_file(WASM_FILE).expect("Failed to deserialize module from buffer");
    let module = program
        .add_module("main", module, None)
        .expect("Failed to initialize module");

    // => env module is created
    let env_instance = program.module("env").unwrap();
    // => linear memory is created
    let env_memory = env_instance.memory(ItemIndex::Internal(0)).unwrap();

    // Place the octet-sequence at index 0 in linear memory
    let offset: u32 = 0;
    let _ = env_memory.set(offset, BUF);

    // Set up the function argument list and invoke the function
    let args = vec![RuntimeValue::I32(BUF.len() as i32), RuntimeValue::I32(offset as i32)];
    let execution_params = ExecutionParams::from(args);
    let retval = module
        .execute_export(FUNCTION_NAME, execution_params)
        .expect("Failed to execute function");

    // For verification, repeat accumulation using native code
    let accu = BUF.into_iter().fold(0 as i32, |a, b| a + *b as i32);
    let exp_retval: Option<RuntimeValue> = Some(RuntimeValue::I32(accu));

    // Verify calculation from WebAssembly runtime is identical to expected result
    assert_eq!(exp_retval, retval);
}
<commit_msg>Clean wasm.rs tests<commit_after>use elements::deserialize_file;
use elements::Module;
use interpreter::ExecutionParams;
use interpreter::value::RuntimeValue;
use interpreter::module::{ModuleInstanceInterface, ItemIndex};
use super::utils::program_with_default_env;

#[test]
fn interpreter_inc_i32() {
    // Name of function contained in WASM file (note the leading underline)
    const FUNCTION_NAME: &'static str = "_inc_i32";
    // The WASM file containing the module and function
    const WASM_FILE: &str = &"res/cases/v1/inc_i32.wasm";

    let program = program_with_default_env();

    let module: Module =
        deserialize_file(WASM_FILE).expect("Failed to deserialize module from buffer");
    let i32_val = 42;
    // the functions expects a single i32 parameter
    let args = vec![RuntimeValue::I32(i32_val)];
    let exp_retval = Some(RuntimeValue::I32(i32_val + 1));
    let execution_params = ExecutionParams::from(args);

    let module_result = program
        .add_module("main", module, None);

    let module = module_result
        .expect("Failed to initialize module");

    let retval = module
        .execute_export(FUNCTION_NAME, execution_params)
        .expect("");
    assert_eq!(exp_retval, retval);
}

#[test]
fn interpreter_accumulate_u8() {
    // Name of function contained in WASM file (note the leading underline)
    const FUNCTION_NAME: &'static str = "_accumulate_u8";
    // The WASM file containing the module and function
    const WASM_FILE: &str = &"res/cases/v1/accumulate_u8.wasm";
    // The octet sequence being accumulated
    const BUF: &[u8] = &[9,8,7,6,5,4,3,2,1];

    let program = program_with_default_env();

    // Load the module-structure from wasm-file and add to program
    let module: Module =
        deserialize_file(WASM_FILE).expect("Failed to deserialize module from buffer");
    let module = program
        .add_module("main", module, None)
        .expect("Failed to initialize module");

    // => env module is created
    let env_instance = program.module("env").unwrap();
    // => linear memory is created
    let env_memory = env_instance.memory(ItemIndex::Internal(0)).unwrap();

    // Place the octet-sequence at index 0 in linear memory
    let offset: u32 = 0;
    let _ = env_memory.set(offset, BUF);

    // Set up the function argument list and invoke the function
    let args = vec![RuntimeValue::I32(BUF.len() as i32), RuntimeValue::I32(offset as i32)];
    let execution_params = ExecutionParams::from(args);
    let retval = module
        .execute_export(FUNCTION_NAME, execution_params)
        .expect("Failed to execute function");

    // For verification, repeat accumulation using native code
    let accu = BUF.into_iter().fold(0 as i32, |a, b| a + *b as i32);
    let exp_retval: Option<RuntimeValue> = Some(RuntimeValue::I32(accu));

    // Verify calculation from WebAssembly runtime is identical to expected result
    assert_eq!(exp_retval, retval);
}
<|endoftext|>


<commit_before>use std::collections::HashMap;

/// Data associated with a single metadata block.
#[derive(Debug)]
pub struct Metadata {
  /// Marks whether the current metadata block is the last.
  pub is_last: bool,
  /// The length, in bytes, of the block being parsed. This does not include
  /// the metadata block header.
  pub length: u32,
  /// Block data containing one of the eight different types of metadata.
  pub data: Data,
}

/// General enum that hold all the different metadata block data.
#[derive(Debug, PartialEq, Eq)]
pub enum Data {
  /// Information regarding the entire audio stream.
  StreamInfo(StreamInfo),
  /// Block that represents a number of padded bytes.
  Padding(u32),
  /// Data used by third-party applications.
  Application(Application),
  /// Table of multiple points to seek, or skip, to within the FLAC file.
  SeekTable(Vec<SeekPoint>),
  /// Stores human-readable name/value pairs.
  VorbisComment(VorbisComment),
  /// Stores cue information
  CueSheet(CueSheet),
  /// Stores pictures associated with the FLAC file.
  Picture(Picture),
  /// A type of block data that isn't know or doesn't match the type above.
  Unknown(Vec<u8>),
}

/// Information regarding the entire audio stream.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct StreamInfo {
  /// Minimum block size, in samples, used in the stream.
  pub min_block_size: u16,
  /// Maximum block size, in samples, used in the stream.
  pub max_block_size: u16,
  /// Minimum frame size, in bytes, used in the stream. May be zero to imply
  /// the value isn't know.
  pub min_frame_size: u32,
  /// Maximum frame size, in bytes, used in the stream. May be zero to imply
  /// the value isn't know.
  pub max_frame_size: u32,
  /// Sample rate in hertz (Hz).
  pub sample_rate: u32,
  /// Number of channels. FLAC supports one to eight channel.
  pub channels: u8,
  /// Bits per sample. FLAC supports four to thirty-two bits per sample.
  pub bits_per_sample: u8,
  /// Total samples in the stream. A value of zero means the number is
  /// unknown.
  pub total_samples: u64,
  /// MD5 signature of the unencoded audio data.
  pub md5_sum: [u8; 16],
}

impl StreamInfo {
  pub fn new() -> StreamInfo {
    StreamInfo {
      min_block_size: 0,
      max_block_size: 0,
      min_frame_size: 0,
      max_frame_size: 0,
      sample_rate: 0,
      channels: 0,
      bits_per_sample: 0,
      total_samples: 0,
      md5_sum: [0; 16],
    }
  }

  #[inline]
  pub fn is_varied_block_size(&self) -> bool {
    self.min_block_size != self.max_block_size
  }

  #[inline]
  pub fn is_fixed_block_size(&self) -> bool {
    self.min_block_size == self.max_block_size
  }
}

/// Data used by third-party applications.
#[derive(Debug, PartialEq, Eq)]
pub struct Application {
  /// Registered application ID.
  pub id: String,
  /// Data used by the third-party application.
  pub data: Vec<u8>,
}

/// Seek, or skip, to a point within the FLAC file.
#[derive(Debug, PartialEq, Eq)]
pub struct SeekPoint {
  /// Sample number of the first sample in the target frame.
  pub sample_number: u64,
  /// Byte offset of the target frame's header.
  pub stream_offset: u64,
  /// Number of samples in the target frame.
  pub frame_samples: u16,
}

/// Stores human-readable name/value pairs.
#[derive(Debug, PartialEq, Eq)]
pub struct VorbisComment {
  /// Vendor name.
  pub vendor_string: String,
  /// Comments associated with a name, or category, followed by it's
  /// contents.
  pub comments: HashMap<String, String>,
}

/// Stores cue information.
///
/// Generally for storing information from Compact Disk Digital Audio, but
/// can be used as a cueing mechanism for playback.
#[derive(Debug, PartialEq, Eq)]
pub struct CueSheet {
  /// Media catalog number.
  pub media_catalog_number: String,
  /// Number of lead-in samples.
  pub lead_in: u64,
  /// Whether or not this `CueSheet` corresponds to a Compact Disc.
  pub is_cd: bool,
  /// One or more tracks.
  pub tracks: Vec<CueSheetTrack>,
}

/// Track information inside a cue sheet.
#[derive(Debug, PartialEq, Eq)]
pub struct CueSheetTrack {
  /// Track offset, in samples, relative to the beginning of the FLAC audio
  /// stream.
  pub offset: u64,
  /// Track number.
  pub number: u8,
  /// Twelve digit alphanumeric code.
  pub isrc: String,
  /// Whether the cue sheet track is audio.
  pub is_audio: bool,
  /// Whether the cue sheet track is pre-emphasis.
  pub is_pre_emphasis: bool,
  /// For all tracks except the lead-out track.
  pub indices: Vec<CueSheetTrackIndex>,
}

/// An index point within a track, inside of a cue sheet.
#[derive(Debug, PartialEq, Eq)]
pub struct CueSheetTrackIndex {
  /// Offset, in samples, relative to the track offset of the index point.
  pub offset: u64,
  /// Index point number.
  pub number: u8,
}

/// Stores pictures associated with the FLAC file.
///
/// More than likely these pictures will be cover art, but you can have more
/// than one within a file, which are distinguished by `PictureType`and it's
/// mime type string.
#[derive(Debug, PartialEq, Eq)]
pub struct Picture {
  /// Picture type, based on the ID3v2 APIC frame.
  pub picture_type: PictureType,
  /// Multipurpose Internet Mail Extensions (MIME) type.
  pub mime_type: String,
  /// A string describing the picture.
  pub description: String,
  /// Width of the picture in pixels.
  pub width: u32,
  /// Height of the picture in pixels.
  pub height: u32,
  /// Color depth of the picture in bits-per-pixel.
  pub depth: u32,
  /// Number of colors used.
  pub colors: u32,
  /// Binary picture data.
  pub data: Vec<u8>,
}

/// The picture type according to the ID3v2 attached picture frame.
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum PictureType {
  /// Other picture type not categorized in this enum.
  Other,
  /// 32x32 pixels 'file icon'.
  FileIconStandard,
  /// Other, or non-standard, file icon.
  FileIcon,
  /// Cover (front).
  FrontCover,
  /// Cover (back).
  BackCover,
  /// Leaflet page.
  LeafletPage,
  /// Media, like label side of a CD.
  Media,
  /// Lead artist, lead performer, or soloist.
  LeadArtist,
  /// Artist or performer.
  Artist,
  /// Conductor.
  Conductor,
  /// Band or orchestra.
  Band,
  /// Composer.
  Composer,
  /// Lyricist or text writer.
  Lyricist,
  /// Recording location.
  RecordingLocation,
  /// During recording.
  DuringRecording,
  /// During performance.
  DuringPerformace,
  /// Movie, or video, screen capture.
  VideoScreenCapture,
  /// A bright colored fish.
  Fish,
  /// Illustration.
  Illustration,
  /// Band, or artist, logotype.
  BandLogo,
  /// Publisher, or studio, logotype.
  PublisherLogo,
}

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

  #[test]
  fn test_is_varied_block_size() {
    let mut info = StreamInfo::new();

    info.min_block_size = 512;
    info.max_block_size = 1024;

    assert!(info.is_varied_block_size());

    info.min_block_size = 2048;
    info.max_block_size = 2048;

    assert!(!info.is_varied_block_size());
  }

  #[test]
  fn test_is_fixed_block_size() {
    let mut info = StreamInfo::new();

    info.min_block_size = 512;
    info.max_block_size = 512;

    assert!(info.is_fixed_block_size());

    info.min_block_size = 1024;
    info.max_block_size = 2048;

    assert!(!info.is_fixed_block_size());
  }
}
<commit_msg>Add `Display` implementation for `PictureType`<commit_after>use std::collections::HashMap;
use std::fmt;

/// Data associated with a single metadata block.
#[derive(Debug)]
pub struct Metadata {
  /// Marks whether the current metadata block is the last.
  pub is_last: bool,
  /// The length, in bytes, of the block being parsed. This does not include
  /// the metadata block header.
  pub length: u32,
  /// Block data containing one of the eight different types of metadata.
  pub data: Data,
}

/// General enum that hold all the different metadata block data.
#[derive(Debug, PartialEq, Eq)]
pub enum Data {
  /// Information regarding the entire audio stream.
  StreamInfo(StreamInfo),
  /// Block that represents a number of padded bytes.
  Padding(u32),
  /// Data used by third-party applications.
  Application(Application),
  /// Table of multiple points to seek, or skip, to within the FLAC file.
  SeekTable(Vec<SeekPoint>),
  /// Stores human-readable name/value pairs.
  VorbisComment(VorbisComment),
  /// Stores cue information
  CueSheet(CueSheet),
  /// Stores pictures associated with the FLAC file.
  Picture(Picture),
  /// A type of block data that isn't know or doesn't match the type above.
  Unknown(Vec<u8>),
}

/// Information regarding the entire audio stream.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct StreamInfo {
  /// Minimum block size, in samples, used in the stream.
  pub min_block_size: u16,
  /// Maximum block size, in samples, used in the stream.
  pub max_block_size: u16,
  /// Minimum frame size, in bytes, used in the stream. May be zero to imply
  /// the value isn't know.
  pub min_frame_size: u32,
  /// Maximum frame size, in bytes, used in the stream. May be zero to imply
  /// the value isn't know.
  pub max_frame_size: u32,
  /// Sample rate in hertz (Hz).
  pub sample_rate: u32,
  /// Number of channels. FLAC supports one to eight channel.
  pub channels: u8,
  /// Bits per sample. FLAC supports four to thirty-two bits per sample.
  pub bits_per_sample: u8,
  /// Total samples in the stream. A value of zero means the number is
  /// unknown.
  pub total_samples: u64,
  /// MD5 signature of the unencoded audio data.
  pub md5_sum: [u8; 16],
}

impl StreamInfo {
  pub fn new() -> StreamInfo {
    StreamInfo {
      min_block_size: 0,
      max_block_size: 0,
      min_frame_size: 0,
      max_frame_size: 0,
      sample_rate: 0,
      channels: 0,
      bits_per_sample: 0,
      total_samples: 0,
      md5_sum: [0; 16],
    }
  }

  #[inline]
  pub fn is_varied_block_size(&self) -> bool {
    self.min_block_size != self.max_block_size
  }

  #[inline]
  pub fn is_fixed_block_size(&self) -> bool {
    self.min_block_size == self.max_block_size
  }
}

/// Data used by third-party applications.
#[derive(Debug, PartialEq, Eq)]
pub struct Application {
  /// Registered application ID.
  pub id: String,
  /// Data used by the third-party application.
  pub data: Vec<u8>,
}

/// Seek, or skip, to a point within the FLAC file.
#[derive(Debug, PartialEq, Eq)]
pub struct SeekPoint {
  /// Sample number of the first sample in the target frame.
  pub sample_number: u64,
  /// Byte offset of the target frame's header.
  pub stream_offset: u64,
  /// Number of samples in the target frame.
  pub frame_samples: u16,
}

/// Stores human-readable name/value pairs.
#[derive(Debug, PartialEq, Eq)]
pub struct VorbisComment {
  /// Vendor name.
  pub vendor_string: String,
  /// Comments associated with a name, or category, followed by it's
  /// contents.
  pub comments: HashMap<String, String>,
}

/// Stores cue information.
///
/// Generally for storing information from Compact Disk Digital Audio, but
/// can be used as a cueing mechanism for playback.
#[derive(Debug, PartialEq, Eq)]
pub struct CueSheet {
  /// Media catalog number.
  pub media_catalog_number: String,
  /// Number of lead-in samples.
  pub lead_in: u64,
  /// Whether or not this `CueSheet` corresponds to a Compact Disc.
  pub is_cd: bool,
  /// One or more tracks.
  pub tracks: Vec<CueSheetTrack>,
}

/// Track information inside a cue sheet.
#[derive(Debug, PartialEq, Eq)]
pub struct CueSheetTrack {
  /// Track offset, in samples, relative to the beginning of the FLAC audio
  /// stream.
  pub offset: u64,
  /// Track number.
  pub number: u8,
  /// Twelve digit alphanumeric code.
  pub isrc: String,
  /// Whether the cue sheet track is audio.
  pub is_audio: bool,
  /// Whether the cue sheet track is pre-emphasis.
  pub is_pre_emphasis: bool,
  /// For all tracks except the lead-out track.
  pub indices: Vec<CueSheetTrackIndex>,
}

/// An index point within a track, inside of a cue sheet.
#[derive(Debug, PartialEq, Eq)]
pub struct CueSheetTrackIndex {
  /// Offset, in samples, relative to the track offset of the index point.
  pub offset: u64,
  /// Index point number.
  pub number: u8,
}

/// Stores pictures associated with the FLAC file.
///
/// More than likely these pictures will be cover art, but you can have more
/// than one within a file, which are distinguished by `PictureType`and it's
/// mime type string.
#[derive(Debug, PartialEq, Eq)]
pub struct Picture {
  /// Picture type, based on the ID3v2 APIC frame.
  pub picture_type: PictureType,
  /// Multipurpose Internet Mail Extensions (MIME) type.
  pub mime_type: String,
  /// A string describing the picture.
  pub description: String,
  /// Width of the picture in pixels.
  pub width: u32,
  /// Height of the picture in pixels.
  pub height: u32,
  /// Color depth of the picture in bits-per-pixel.
  pub depth: u32,
  /// Number of colors used.
  pub colors: u32,
  /// Binary picture data.
  pub data: Vec<u8>,
}

/// The picture type according to the ID3v2 attached picture frame.
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum PictureType {
  /// Other picture type not categorized in this enum.
  Other,
  /// 32x32 pixels 'file icon'.
  FileIconStandard,
  /// Other, or non-standard, file icon.
  FileIcon,
  /// Cover (front).
  FrontCover,
  /// Cover (back).
  BackCover,
  /// Leaflet page.
  LeafletPage,
  /// Media, like label side of a CD.
  Media,
  /// Lead artist, lead performer, or soloist.
  LeadArtist,
  /// Artist or performer.
  Artist,
  /// Conductor.
  Conductor,
  /// Band or orchestra.
  Band,
  /// Composer.
  Composer,
  /// Lyricist or text writer.
  Lyricist,
  /// Recording location.
  RecordingLocation,
  /// During recording.
  DuringRecording,
  /// During performance.
  DuringPerformace,
  /// Movie, or video, screen capture.
  VideoScreenCapture,
  /// A bright colored fish.
  Fish,
  /// Illustration.
  Illustration,
  /// Band, or artist, logotype.
  BandLogo,
  /// Publisher, or studio, logotype.
  PublisherLogo,
}

impl fmt::Display for PictureType {
  fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
    write!(formatter, "{}", match *self {
      PictureType::Other              => "Other",
      PictureType::FileIconStandard   => "File Icon (standard)",
      PictureType::FileIcon           => "File Icon",
      PictureType::FrontCover         => "Cover (front)",
      PictureType::BackCover          => "Cover (back)",
      PictureType::LeafletPage        => "Leaflet Page",
      PictureType::Media              => "Media",
      PictureType::LeadArtist         => "Lead Artist",
      PictureType::Artist             => "Arist",
      PictureType::Conductor          => "Conductor",
      PictureType::Band               => "Band",
      PictureType::Composer           => "Composer",
      PictureType::Lyricist           => "Lyricist",
      PictureType::RecordingLocation  => "Recoding Location",
      PictureType::DuringRecording    => "During Recording",
      PictureType::DuringPerformace   => "During Performace",
      PictureType::VideoScreenCapture => "Video Screen Capture",
      PictureType::Fish               => "Fish",
      PictureType::Illustration       => "Illustration",
      PictureType::BandLogo           => "Band Logo",
      PictureType::PublisherLogo      => "Publisher Logo",
    })
  }
}

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

  #[test]
  fn test_is_varied_block_size() {
    let mut info = StreamInfo::new();

    info.min_block_size = 512;
    info.max_block_size = 1024;

    assert!(info.is_varied_block_size());

    info.min_block_size = 2048;
    info.max_block_size = 2048;

    assert!(!info.is_varied_block_size());
  }

  #[test]
  fn test_is_fixed_block_size() {
    let mut info = StreamInfo::new();

    info.min_block_size = 512;
    info.max_block_size = 512;

    assert!(info.is_fixed_block_size());

    info.min_block_size = 1024;
    info.max_block_size = 2048;

    assert!(!info.is_fixed_block_size());
  }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;

use chrono::NaiveDateTime;
use diesel::prelude::*;

use crate::util::errors::{cargo_err, AppResult};

use crate::models::{Crate, Dependency, User, VersionOwnerAction};
use crate::schema::*;
use crate::views::{EncodableAuditAction, EncodableVersion, EncodableVersionLinks};

// Queryable has a custom implementation below
#[derive(Clone, Identifiable, Associations, Debug, Queryable, Deserialize, Serialize)]
#[belongs_to(Crate)]
pub struct Version {
    pub id: i32,
    pub crate_id: i32,
    pub num: semver::Version,
    pub updated_at: NaiveDateTime,
    pub created_at: NaiveDateTime,
    pub downloads: i32,
    pub features: serde_json::Value,
    pub yanked: bool,
    pub license: Option<String>,
    pub crate_size: Option<i32>,
    pub published_by: Option<i32>,
}

#[derive(Insertable, Debug)]
#[table_name = "versions"]
pub struct NewVersion {
    crate_id: i32,
    num: String,
    features: serde_json::Value,
    license: Option<String>,
    crate_size: Option<i32>,
    published_by: i32,
}

/// The highest version (semver order) and the most recently updated version.
/// Typically used for a single crate.
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct TopVersions {
    /// The "highest" version in terms of semver
    pub highest: semver::Version,
    /// The "newest" version in terms of publishing date
    pub newest: semver::Version,
}

impl TopVersions {
    /// Return both the newest (most recently updated) and the
    /// highest version (in semver order) for a list of `Version` instances.
    pub fn from_versions(versions: Vec<Version>) -> Self {
        Self::from_date_version_pairs(versions.into_iter().map(|v| (v.created_at, v.num)))
    }

    /// Return both the newest (most recently updated) and the
    /// highest version (in semver order) for a collection of date/version pairs.
    pub fn from_date_version_pairs<T>(pairs: T) -> Self
    where
        T: Clone + IntoIterator<Item = (NaiveDateTime, semver::Version)>,
    {
        let newest = pairs
            .clone()
            .into_iter()
            .max()
            .unwrap_or((
                NaiveDateTime::from_timestamp(0, 0),
                default_semver_version(),
            ))
            .1;

        let highest = pairs
            .into_iter()
            .map(|(_, v)| v)
            .max()
            .unwrap_or_else(default_semver_version);

        Self { newest, highest }
    }
}

/// A default semver value, "0.0.0", for use in TopVersions
fn default_semver_version() -> semver::Version {
    semver::Version {
        major: 0,
        minor: 0,
        patch: 0,
        pre: vec![],
        build: vec![],
    }
}

impl Version {
    pub fn encodable(
        self,
        crate_name: &str,
        published_by: Option<User>,
        audit_actions: Vec<(VersionOwnerAction, User)>,
    ) -> EncodableVersion {
        let Version {
            id,
            num,
            updated_at,
            created_at,
            downloads,
            features,
            yanked,
            license,
            crate_size,
            ..
        } = self;
        let num = num.to_string();
        EncodableVersion {
            dl_path: format!("/api/v1/crates/{}/{}/download", crate_name, num),
            readme_path: format!("/api/v1/crates/{}/{}/readme", crate_name, num),
            num: num.clone(),
            id,
            krate: crate_name.to_string(),
            updated_at,
            created_at,
            downloads,
            features,
            yanked,
            license,
            links: EncodableVersionLinks {
                dependencies: format!("/api/v1/crates/{}/{}/dependencies", crate_name, num),
                version_downloads: format!("/api/v1/crates/{}/{}/downloads", crate_name, num),
                authors: format!("/api/v1/crates/{}/{}/authors", crate_name, num),
            },
            crate_size,
            published_by: published_by.map(User::into),
            audit_actions: audit_actions
                .into_iter()
                .map(|(audit_action, user)| EncodableAuditAction {
                    action: audit_action.action.into(),
                    user: user.into(),
                    time: audit_action.time,
                })
                .collect(),
        }
    }

    /// Returns (dependency, crate dependency name)
    pub fn dependencies(&self, conn: &PgConnection) -> QueryResult<Vec<(Dependency, String)>> {
        Dependency::belonging_to(self)
            .inner_join(crates::table)
            .select((dependencies::all_columns, crates::name))
            .order((dependencies::optional, crates::name))
            .load(conn)
    }

    pub fn record_readme_rendering(version_id_: i32, conn: &PgConnection) -> QueryResult<usize> {
        use crate::schema::readme_renderings::dsl::*;
        use diesel::dsl::now;

        diesel::insert_into(readme_renderings)
            .values(version_id.eq(version_id_))
            .on_conflict(version_id)
            .do_update()
            .set(rendered_at.eq(now))
            .execute(conn)
    }

    /// Gets the User who ran `cargo publish` for this version, if recorded.
    /// Not for use when you have a group of versions you need the publishers for.
    pub fn published_by(&self, conn: &PgConnection) -> Option<User> {
        match self.published_by {
            Some(pb) => users::table.find(pb).first(conn).ok(),
            None => None,
        }
    }
}

impl NewVersion {
    pub fn new(
        crate_id: i32,
        num: &semver::Version,
        features: &HashMap<String, Vec<String>>,
        license: Option<String>,
        license_file: Option<&str>,
        crate_size: i32,
        published_by: i32,
    ) -> AppResult<Self> {
        let features = serde_json::to_value(features)?;

        let mut new_version = NewVersion {
            crate_id,
            num: num.to_string(),
            features,
            license,
            crate_size: Some(crate_size),
            published_by,
        };

        new_version.validate_license(license_file)?;

        Ok(new_version)
    }

    pub fn save(
        &self,
        conn: &PgConnection,
        authors: &[String],
        published_by_email: &str,
    ) -> AppResult<Version> {
        use crate::schema::version_authors::{name, version_id};
        use crate::schema::versions::dsl::*;
        use diesel::dsl::exists;
        use diesel::{insert_into, select};

        conn.transaction(|| {
            let already_uploaded = versions
                .filter(crate_id.eq(self.crate_id))
                .filter(num.eq(&self.num));
            if select(exists(already_uploaded)).get_result(conn)? {
                return Err(cargo_err(&format_args!(
                    "crate version `{}` is already \
                     uploaded",
                    self.num
                )));
            }

            let version: Version = insert_into(versions).values(self).get_result(conn)?;

            insert_into(versions_published_by::table)
                .values((
                    versions_published_by::version_id.eq(version.id),
                    versions_published_by::email.eq(published_by_email),
                ))
                .execute(conn)?;

            let new_authors = authors
                .iter()
                .map(|s| (version_id.eq(version.id), name.eq(s)))
                .collect::<Vec<_>>();

            insert_into(version_authors::table)
                .values(&new_authors)
                .execute(conn)?;
            Ok(version)
        })
    }

    fn validate_license(&mut self, license_file: Option<&str>) -> AppResult<()> {
        if let Some(ref license) = self.license {
            for part in license.split('/') {
                license_exprs::validate_license_expr(part).map_err(|e| {
                    cargo_err(&format_args!(
                        "{}; see http://opensource.org/licenses \
                         for options, and http://spdx.org/licenses/ \
                         for their identifiers",
                        e
                    ))
                })?;
            }
        } else if license_file.is_some() {
            // If no license is given, but a license file is given, flag this
            // crate as having a nonstandard license. Note that we don't
            // actually do anything else with license_file currently.
            self.license = Some(String::from("non-standard"));
        }
        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use super::TopVersions;
    use chrono::NaiveDateTime;

    #[track_caller]
    fn date(str: &str) -> NaiveDateTime {
        str.parse().unwrap()
    }

    #[track_caller]
    fn version(str: &str) -> semver::Version {
        semver::Version::parse(str).unwrap()
    }

    #[test]
    fn top_versions_empty() {
        let versions = vec![];
        assert_eq!(
            TopVersions::from_date_version_pairs(versions),
            TopVersions {
                highest: version("0.0.0"),
                newest: version("0.0.0"),
            }
        );
    }

    #[test]
    fn top_versions_single() {
        let versions = vec![(date("2020-12-03T12:34:56"), version("1.0.0"))];
        assert_eq!(
            TopVersions::from_date_version_pairs(versions),
            TopVersions {
                highest: version("1.0.0"),
                newest: version("1.0.0"),
            }
        );
    }

    #[test]
    fn top_versions_multiple() {
        let versions = vec![
            (date("2018-12-03T12:34:56"), version("1.0.0")),
            (date("2019-12-03T12:34:56"), version("2.0.0-alpha.1")),
            (date("2020-12-03T12:34:56"), version("1.1.0")),
        ];
        assert_eq!(
            TopVersions::from_date_version_pairs(versions),
            TopVersions {
                highest: version("2.0.0-alpha.1"),
                newest: version("1.1.0"),
            }
        );
    }
}
<commit_msg>models/version: Simplify `from_date_version_pairs()` function<commit_after>use std::collections::HashMap;

use chrono::NaiveDateTime;
use diesel::prelude::*;

use crate::util::errors::{cargo_err, AppResult};

use crate::models::{Crate, Dependency, User, VersionOwnerAction};
use crate::schema::*;
use crate::views::{EncodableAuditAction, EncodableVersion, EncodableVersionLinks};

// Queryable has a custom implementation below
#[derive(Clone, Identifiable, Associations, Debug, Queryable, Deserialize, Serialize)]
#[belongs_to(Crate)]
pub struct Version {
    pub id: i32,
    pub crate_id: i32,
    pub num: semver::Version,
    pub updated_at: NaiveDateTime,
    pub created_at: NaiveDateTime,
    pub downloads: i32,
    pub features: serde_json::Value,
    pub yanked: bool,
    pub license: Option<String>,
    pub crate_size: Option<i32>,
    pub published_by: Option<i32>,
}

#[derive(Insertable, Debug)]
#[table_name = "versions"]
pub struct NewVersion {
    crate_id: i32,
    num: String,
    features: serde_json::Value,
    license: Option<String>,
    crate_size: Option<i32>,
    published_by: i32,
}

/// The highest version (semver order) and the most recently updated version.
/// Typically used for a single crate.
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct TopVersions {
    /// The "highest" version in terms of semver
    pub highest: semver::Version,
    /// The "newest" version in terms of publishing date
    pub newest: semver::Version,
}

impl TopVersions {
    /// Return both the newest (most recently updated) and the
    /// highest version (in semver order) for a list of `Version` instances.
    pub fn from_versions(versions: Vec<Version>) -> Self {
        Self::from_date_version_pairs(versions.into_iter().map(|v| (v.created_at, v.num)))
    }

    /// Return both the newest (most recently updated) and the
    /// highest version (in semver order) for a collection of date/version pairs.
    pub fn from_date_version_pairs<T>(pairs: T) -> Self
    where
        T: Clone + IntoIterator<Item = (NaiveDateTime, semver::Version)>,
    {
        let newest = pairs.clone().into_iter().max().map(|(_, v)| v);
        let highest = pairs.into_iter().map(|(_, v)| v).max();

        Self {
            newest: newest.unwrap_or_else(default_semver_version),
            highest: highest.unwrap_or_else(default_semver_version),
        }
    }
}

/// A default semver value, "0.0.0", for use in TopVersions
fn default_semver_version() -> semver::Version {
    semver::Version {
        major: 0,
        minor: 0,
        patch: 0,
        pre: vec![],
        build: vec![],
    }
}

impl Version {
    pub fn encodable(
        self,
        crate_name: &str,
        published_by: Option<User>,
        audit_actions: Vec<(VersionOwnerAction, User)>,
    ) -> EncodableVersion {
        let Version {
            id,
            num,
            updated_at,
            created_at,
            downloads,
            features,
            yanked,
            license,
            crate_size,
            ..
        } = self;
        let num = num.to_string();
        EncodableVersion {
            dl_path: format!("/api/v1/crates/{}/{}/download", crate_name, num),
            readme_path: format!("/api/v1/crates/{}/{}/readme", crate_name, num),
            num: num.clone(),
            id,
            krate: crate_name.to_string(),
            updated_at,
            created_at,
            downloads,
            features,
            yanked,
            license,
            links: EncodableVersionLinks {
                dependencies: format!("/api/v1/crates/{}/{}/dependencies", crate_name, num),
                version_downloads: format!("/api/v1/crates/{}/{}/downloads", crate_name, num),
                authors: format!("/api/v1/crates/{}/{}/authors", crate_name, num),
            },
            crate_size,
            published_by: published_by.map(User::into),
            audit_actions: audit_actions
                .into_iter()
                .map(|(audit_action, user)| EncodableAuditAction {
                    action: audit_action.action.into(),
                    user: user.into(),
                    time: audit_action.time,
                })
                .collect(),
        }
    }

    /// Returns (dependency, crate dependency name)
    pub fn dependencies(&self, conn: &PgConnection) -> QueryResult<Vec<(Dependency, String)>> {
        Dependency::belonging_to(self)
            .inner_join(crates::table)
            .select((dependencies::all_columns, crates::name))
            .order((dependencies::optional, crates::name))
            .load(conn)
    }

    pub fn record_readme_rendering(version_id_: i32, conn: &PgConnection) -> QueryResult<usize> {
        use crate::schema::readme_renderings::dsl::*;
        use diesel::dsl::now;

        diesel::insert_into(readme_renderings)
            .values(version_id.eq(version_id_))
            .on_conflict(version_id)
            .do_update()
            .set(rendered_at.eq(now))
            .execute(conn)
    }

    /// Gets the User who ran `cargo publish` for this version, if recorded.
    /// Not for use when you have a group of versions you need the publishers for.
    pub fn published_by(&self, conn: &PgConnection) -> Option<User> {
        match self.published_by {
            Some(pb) => users::table.find(pb).first(conn).ok(),
            None => None,
        }
    }
}

impl NewVersion {
    pub fn new(
        crate_id: i32,
        num: &semver::Version,
        features: &HashMap<String, Vec<String>>,
        license: Option<String>,
        license_file: Option<&str>,
        crate_size: i32,
        published_by: i32,
    ) -> AppResult<Self> {
        let features = serde_json::to_value(features)?;

        let mut new_version = NewVersion {
            crate_id,
            num: num.to_string(),
            features,
            license,
            crate_size: Some(crate_size),
            published_by,
        };

        new_version.validate_license(license_file)?;

        Ok(new_version)
    }

    pub fn save(
        &self,
        conn: &PgConnection,
        authors: &[String],
        published_by_email: &str,
    ) -> AppResult<Version> {
        use crate::schema::version_authors::{name, version_id};
        use crate::schema::versions::dsl::*;
        use diesel::dsl::exists;
        use diesel::{insert_into, select};

        conn.transaction(|| {
            let already_uploaded = versions
                .filter(crate_id.eq(self.crate_id))
                .filter(num.eq(&self.num));
            if select(exists(already_uploaded)).get_result(conn)? {
                return Err(cargo_err(&format_args!(
                    "crate version `{}` is already \
                     uploaded",
                    self.num
                )));
            }

            let version: Version = insert_into(versions).values(self).get_result(conn)?;

            insert_into(versions_published_by::table)
                .values((
                    versions_published_by::version_id.eq(version.id),
                    versions_published_by::email.eq(published_by_email),
                ))
                .execute(conn)?;

            let new_authors = authors
                .iter()
                .map(|s| (version_id.eq(version.id), name.eq(s)))
                .collect::<Vec<_>>();

            insert_into(version_authors::table)
                .values(&new_authors)
                .execute(conn)?;
            Ok(version)
        })
    }

    fn validate_license(&mut self, license_file: Option<&str>) -> AppResult<()> {
        if let Some(ref license) = self.license {
            for part in license.split('/') {
                license_exprs::validate_license_expr(part).map_err(|e| {
                    cargo_err(&format_args!(
                        "{}; see http://opensource.org/licenses \
                         for options, and http://spdx.org/licenses/ \
                         for their identifiers",
                        e
                    ))
                })?;
            }
        } else if license_file.is_some() {
            // If no license is given, but a license file is given, flag this
            // crate as having a nonstandard license. Note that we don't
            // actually do anything else with license_file currently.
            self.license = Some(String::from("non-standard"));
        }
        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use super::TopVersions;
    use chrono::NaiveDateTime;

    #[track_caller]
    fn date(str: &str) -> NaiveDateTime {
        str.parse().unwrap()
    }

    #[track_caller]
    fn version(str: &str) -> semver::Version {
        semver::Version::parse(str).unwrap()
    }

    #[test]
    fn top_versions_empty() {
        let versions = vec![];
        assert_eq!(
            TopVersions::from_date_version_pairs(versions),
            TopVersions {
                highest: version("0.0.0"),
                newest: version("0.0.0"),
            }
        );
    }

    #[test]
    fn top_versions_single() {
        let versions = vec![(date("2020-12-03T12:34:56"), version("1.0.0"))];
        assert_eq!(
            TopVersions::from_date_version_pairs(versions),
            TopVersions {
                highest: version("1.0.0"),
                newest: version("1.0.0"),
            }
        );
    }

    #[test]
    fn top_versions_multiple() {
        let versions = vec![
            (date("2018-12-03T12:34:56"), version("1.0.0")),
            (date("2019-12-03T12:34:56"), version("2.0.0-alpha.1")),
            (date("2020-12-03T12:34:56"), version("1.1.0")),
        ];
        assert_eq!(
            TopVersions::from_date_version_pairs(versions),
            TopVersions {
                highest: version("2.0.0-alpha.1"),
                newest: version("1.1.0"),
            }
        );
    }
}
<|endoftext|>