<commit_before>// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use cast::transmute;
use either::*;
use libc::{c_void, uintptr_t, c_char, exit, STDERR_FILENO};
use option::{Some, None, Option};
use rt::util::dumb_println;
use str::StrSlice;
use str::raw::from_c_str;
use u32;
use u32::{min};
use unstable::raw::Closure;
use vec::ImmutableVector;

struct LogDirective {
    name: ~str,
    level: u32
}

// This is the Rust representation of the mod_entry struct in src/rt/rust_crate_map.h
struct ModEntry{
    name: *c_char,
    log_level: *mut u32
}

static MAX_LOG_LEVEL: u32 = 255;
static DEFAULT_LOG_LEVEL: u32 = 1;

fn iter_crate_map(map: *u8, f: &fn(*mut ModEntry)) {
    unsafe {
        let closure : Closure = transmute(f);
        let code = transmute(closure.code);
        let env = transmute(closure.env);
        rust_iter_crate_map(transmute(map), iter_cb, code, env);
    }

    extern fn iter_cb(code: *c_void, env: *c_void, entry: *ModEntry){
         unsafe {
            let closure: Closure = Closure {
                code: transmute(code),
                env: transmute(env),
            };
            let closure: &fn(*ModEntry) = transmute(closure);
            return closure(entry);
        }
    }
    extern {
        #[cfg(not(stage0))]
        #[rust_stack]
        fn rust_iter_crate_map(map: *c_void,
                    f: extern "C" fn(*c_void, *c_void, entry: *ModEntry),
                    code: *c_void,
                    data: *c_void);

        #[cfg(stage0)]
        #[rust_stack]
        fn rust_iter_crate_map(map: *c_void,
                    f: *u8,
                    code: *c_void,
                    data: *c_void);
    }
}
static log_level_names : &'static[&'static str] = &'static["error", "warn", "info", "debug"];

/// Parse an individual log level that is either a number or a symbolic log level
fn parse_log_level(level: &str) -> Option<u32> {
    let num = u32::from_str(level);
    let mut log_level;
    match (num) {
        Some(num) => {
            if num < MAX_LOG_LEVEL {
                log_level = Some(num);
            } else {
                log_level = Some(MAX_LOG_LEVEL);
            }
        }
        _ => {
            let position = log_level_names.iter().position(|&name| name == level);
            match (position) {
                Some(position) => {
                    log_level = Some(min(MAX_LOG_LEVEL, (position + 1) as u32))
                },
                _ => {
                    log_level = None;
                }
            }
        }
    }
    log_level
}

/// Parse a logging specification string (e.g: "crate1,crate2::mod3,crate3::x=1")
/// and return a vector with log directives.
/// Valid log levels are 0-255, with the most likely ones being 1-4 (defined in std::).
/// Also supports string log levels of error, warn, info, and debug

fn parse_logging_spec(spec: ~str) -> ~[LogDirective]{
    let mut dirs = ~[];
    for s in spec.split_iter(',') {
        let parts: ~[&str] = s.split_iter('=').collect();
        let mut log_level;
        match parts.len() {
            1 => log_level = MAX_LOG_LEVEL,
            2 => {
                let possible_log_level = parse_log_level(parts[1]);
                match possible_log_level {
                    Some(num) => {
                        log_level = num;
                    },
                    _ => {
                        dumb_println(fmt!("warning: invalid logging spec \
                                           '%s', ignoring it", parts[1]));
                        loop;
                    }
                }
            },
            _ => {
                dumb_println(fmt!("warning: invalid logging spec '%s',\
                                  ignoring it", s));
                loop;
            }
        }
        let dir = LogDirective {name: parts[0].to_owned(), level: log_level};
        dirs.push(dir);
    }
    return dirs;
}

/// Set the log level of an entry in the crate map depending on the vector
/// of log directives
fn update_entry(dirs: &[LogDirective], entry: *mut ModEntry) -> u32 {
    let mut new_lvl: u32 = DEFAULT_LOG_LEVEL;
    let mut longest_match = 0;
    unsafe {
        for dir in dirs.iter() {
            let name = from_c_str((*entry).name);
            if name.starts_with(dir.name) && dir.name.len() > longest_match {
                longest_match = dir.name.len();
                new_lvl = dir.level;
            }
        }
        *(*entry).log_level = new_lvl;
    }
    if longest_match > 0 { return 1; } else { return 0; }
}

#[fixed_stack_segment] #[inline(never)]
/// Set log level for every entry in crate_map according to the sepecification
/// in settings
fn update_log_settings(crate_map: *u8, settings: ~str) {
    let mut dirs = ~[];
    if settings.len() > 0 {
        if settings == ~"::help" || settings == ~"?" {
            dumb_println("\nCrate log map:\n");
            do iter_crate_map(crate_map) |entry: *mut ModEntry| {
                unsafe {
                    dumb_println(" "+from_c_str((*entry).name));
                }
            }
            unsafe {
                exit(1);
            }
        }
        dirs = parse_logging_spec(settings);
    }

    let mut n_matches: u32 = 0;
    do iter_crate_map(crate_map) |entry: *mut ModEntry| {
        let m = update_entry(dirs, entry);
        n_matches += m;
    }

    if n_matches < (dirs.len() as u32) {
        dumb_println(fmt!("warning: got %u RUST_LOG specs but only matched %u of them.\n\
                          You may have mistyped a RUST_LOG spec.\n\
                          Use RUST_LOG=::help to see the list of crates and modules.\n",
                          dirs.len() as uint, n_matches as uint));
    }
}

pub trait Logger {
    fn log(&mut self, msg: Either<~str, &'static str>);
}

pub struct StdErrLogger;

impl Logger for StdErrLogger {
    fn log(&mut self, msg: Either<~str, &'static str>) {
        use io::{Writer, WriterUtil};

        if !should_log_console() {
            return;
        }

        let s: &str = match msg {
            Left(ref s) => {
                let s: &str = *s;
                s
            }
            Right(ref s) => {
                let s: &str = *s;
                s
            }
        };

        // Truncate the string
        let buf_bytes = 2048;
        if s.len() > buf_bytes {
            let s = s.slice(0, buf_bytes) + "[...]";
            print(s);
        } else {
            print(s)
        };

        fn print(s: &str) {
            let dbg = STDERR_FILENO as ::io::fd_t;
            dbg.write_str(s);
            dbg.write_str("\n");
            dbg.flush();
        }
    }
}
/// Configure logging by traversing the crate map and setting the
/// per-module global logging flags based on the logging spec
#[fixed_stack_segment] #[inline(never)]
pub fn init(crate_map: *u8) {
    use os;

    let log_spec = os::getenv("RUST_LOG");
    match log_spec {
        Some(spec) => {
            update_log_settings(crate_map, spec);
        }
        None => {
            update_log_settings(crate_map, ~"");
        }
    }
}

#[fixed_stack_segment] #[inline(never)]
pub fn console_on() { unsafe { rust_log_console_on() } }

#[fixed_stack_segment] #[inline(never)]
pub fn console_off() { unsafe { rust_log_console_off() } }

#[fixed_stack_segment] #[inline(never)]
fn should_log_console() -> bool { unsafe { rust_should_log_console() != 0 } }

extern {
    fn rust_log_console_on();
    fn rust_log_console_off();
    fn rust_should_log_console() -> uintptr_t;
}

// Tests for parse_logging_spec()
#[test]
fn parse_logging_spec_valid() {
    let dirs: ~[LogDirective] = parse_logging_spec(~"crate1::mod1=1,crate1::mod2,crate2=4");
    assert_eq!(dirs.len(), 3);
    assert!(dirs[0].name == ~"crate1::mod1");
    assert_eq!(dirs[0].level, 1);

    assert!(dirs[1].name == ~"crate1::mod2");
    assert_eq!(dirs[1].level, MAX_LOG_LEVEL);

    assert!(dirs[2].name == ~"crate2");
    assert_eq!(dirs[2].level, 4);
}

#[test]
fn parse_logging_spec_invalid_crate() {
    // test parse_logging_spec with multiple = in specification
    let dirs: ~[LogDirective] = parse_logging_spec(~"crate1::mod1=1=2,crate2=4");
    assert_eq!(dirs.len(), 1);
    assert!(dirs[0].name == ~"crate2");
    assert_eq!(dirs[0].level, 4);
}

#[test]
fn parse_logging_spec_invalid_log_level() {
    // test parse_logging_spec with 'noNumber' as log level
    let dirs: ~[LogDirective] = parse_logging_spec(~"crate1::mod1=noNumber,crate2=4");
    assert_eq!(dirs.len(), 1);
    assert!(dirs[0].name == ~"crate2");
    assert_eq!(dirs[0].level, 4);
}

#[test]
fn parse_logging_spec_string_log_level() {
    // test parse_logging_spec with 'warn' as log level
    let dirs: ~[LogDirective] = parse_logging_spec(~"crate1::mod1=wrong,crate2=warn");
    assert_eq!(dirs.len(), 1);
    assert!(dirs[0].name == ~"crate2");
    assert_eq!(dirs[0].level, 2);
}

// Tests for update_entry
#[test]
fn update_entry_match_full_path() {
    use c_str::ToCStr;
    let dirs = ~[LogDirective {name: ~"crate1::mod1", level: 2 },
    LogDirective {name: ~"crate2", level: 3}];
    unsafe {
        do "crate1::mod1".to_c_str().with_ref |ptr| {
            let entry= &ModEntry {name: ptr, log_level: &mut 0};
            let m = update_entry(dirs, transmute(entry));
            assert!(*entry.log_level == 2);
            assert!(m == 1);
        }
    }
}

#[test]
fn update_entry_no_match() {
    use c_str::ToCStr;
    let dirs = ~[LogDirective {name: ~"crate1::mod1", level: 2 },
        LogDirective {name: ~"crate2", level: 3}];
    unsafe {
        do "crate3::mod1".to_c_str().with_ref |ptr| {
            let entry= &ModEntry {name: ptr, log_level: &mut 0};
            let m = update_entry(dirs, transmute(entry));
            assert!(*entry.log_level == DEFAULT_LOG_LEVEL);
            assert!(m == 0);
        }
    }
}

#[test]
fn update_entry_match_beginning() {
    use c_str::ToCStr;
    let dirs = ~[LogDirective {name: ~"crate1::mod1", level: 2 },
        LogDirective {name: ~"crate2", level: 3}];
    unsafe {
        do "crate2::mod1".to_c_str().with_ref |ptr| {
            let entry= &ModEntry {name: ptr, log_level: &mut 0};
            let m = update_entry(dirs, transmute(entry));
            assert!(*entry.log_level == 3);
            assert!(m == 1);
        }
    }
}

#[test]
fn update_entry_match_beginning_longest_match() {
    use c_str::ToCStr;
    let dirs = ~[LogDirective {name: ~"crate1::mod1", level: 2 },
        LogDirective {name: ~"crate2", level: 3}, LogDirective {name: ~"crate2::mod", level: 4}];
    unsafe {
        do "crate2::mod1".to_c_str().with_ref |ptr| {
            let entry = &ModEntry {name: ptr, log_level: &mut 0};
            let m = update_entry(dirs, transmute(entry));
            assert!(*entry.log_level == 4);
            assert!(m == 1);
        }
    }
}
<commit_msg>Handle global log levels (fixes #6033)<commit_after>// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use cast::transmute;
use either::*;
use libc::{c_void, uintptr_t, c_char, exit, STDERR_FILENO};
use option::{Some, None, Option};
use rt::util::dumb_println;
use str::StrSlice;
use str::raw::from_c_str;
use u32;
use unstable::raw::Closure;
use vec::ImmutableVector;

struct LogDirective {
    name: Option<~str>,
    level: u32
}

// This is the Rust representation of the mod_entry struct in src/rt/rust_crate_map.h
struct ModEntry{
    name: *c_char,
    log_level: *mut u32
}

static MAX_LOG_LEVEL: u32 = 255;
static DEFAULT_LOG_LEVEL: u32 = 1;

fn iter_crate_map(map: *u8, f: &fn(*mut ModEntry)) {
    unsafe {
        let closure : Closure = transmute(f);
        let code = transmute(closure.code);
        let env = transmute(closure.env);
        rust_iter_crate_map(transmute(map), iter_cb, code, env);
    }

    extern fn iter_cb(code: *c_void, env: *c_void, entry: *ModEntry){
         unsafe {
            let closure: Closure = Closure {
                code: transmute(code),
                env: transmute(env),
            };
            let closure: &fn(*ModEntry) = transmute(closure);
            return closure(entry);
        }
    }
    extern {
        #[cfg(not(stage0))]
        #[rust_stack]
        fn rust_iter_crate_map(map: *c_void,
                    f: extern "C" fn(*c_void, *c_void, entry: *ModEntry),
                    code: *c_void,
                    data: *c_void);

        #[cfg(stage0)]
        #[rust_stack]
        fn rust_iter_crate_map(map: *c_void,
                    f: *u8,
                    code: *c_void,
                    data: *c_void);
    }
}
static log_level_names : &'static[&'static str] = &'static["error", "warn", "info", "debug"];

/// Parse an individual log level that is either a number or a symbolic log level
fn parse_log_level(level: &str) -> Option<u32> {
    let num = u32::from_str(level);
    let mut log_level;
    match num {
        Some(num) => {
            if num < MAX_LOG_LEVEL {
                log_level = Some(num);
            } else {
                log_level = Some(MAX_LOG_LEVEL);
            }
        }
        _ => {
            let position = log_level_names.iter().position(|&name| name == level);
            match position {
                Some(position) => {
                    log_level = Some(u32::min(MAX_LOG_LEVEL, (position + 1) as u32))
                },
                _ => {
                    log_level = None;
                }
            }
        }
    }
    log_level
}

/// Parse a logging specification string (e.g: "crate1,crate2::mod3,crate3::x=1")
/// and return a vector with log directives.
/// Valid log levels are 0-255, with the most likely ones being 1-4 (defined in std::).
/// Also supports string log levels of error, warn, info, and debug

fn parse_logging_spec(spec: ~str) -> ~[LogDirective]{
    let mut dirs = ~[];
    for s in spec.split_iter(',') {
        let parts: ~[&str] = s.split_iter('=').collect();
        let mut log_level;
        let mut name = Some(parts[0].to_owned());
        match parts.len() {
            1 => {
                //if the single argument is a log-level string or number,
                //treat that as a global fallback
                let possible_log_level = parse_log_level(parts[0]);
                match possible_log_level {
                    Some(num) => {
                        name = None;
                        log_level = num;
                    },
                    _ => {
                        log_level = MAX_LOG_LEVEL
                    }
                }
            }
            2 => {
                let possible_log_level = parse_log_level(parts[1]);
                match possible_log_level {
                    Some(num) => {
                        log_level = num;
                    },
                    _ => {
                        dumb_println(fmt!("warning: invalid logging spec \
                                           '%s', ignoring it", parts[1]));
                        loop;
                    }
                }
            },
            _ => {
                dumb_println(fmt!("warning: invalid logging spec '%s',\
                                  ignoring it", s));
                loop;
            }
        }
        let dir = LogDirective {name: name, level: log_level};
        dirs.push(dir);
    }
    return dirs;
}

/// Set the log level of an entry in the crate map depending on the vector
/// of log directives
fn update_entry(dirs: &[LogDirective], entry: *mut ModEntry) -> u32 {
    let mut new_lvl: u32 = DEFAULT_LOG_LEVEL;
    let mut longest_match = -1i;
    unsafe {
        for dir in dirs.iter() {
            match dir.name {
                None => {
                    if longest_match == -1 {
                        longest_match = 0;
                        new_lvl = dir.level;
                    }
                }
                Some(ref dir_name) => {
                    let name = from_c_str((*entry).name);
                    let len = dir_name.len() as int;
                    if name.starts_with(*dir_name) &&
                        len >= longest_match {
                        longest_match = len;
                        new_lvl = dir.level;
                    }
                }
            };
        }
        *(*entry).log_level = new_lvl;
    }
    if longest_match >= 0 { return 1; } else { return 0; }
}

#[fixed_stack_segment] #[inline(never)]
/// Set log level for every entry in crate_map according to the sepecification
/// in settings
fn update_log_settings(crate_map: *u8, settings: ~str) {
    let mut dirs = ~[];
    if settings.len() > 0 {
        if settings == ~"::help" || settings == ~"?" {
            dumb_println("\nCrate log map:\n");
            do iter_crate_map(crate_map) |entry: *mut ModEntry| {
                unsafe {
                    dumb_println(" "+from_c_str((*entry).name));
                }
            }
            unsafe {
                exit(1);
            }
        }
        dirs = parse_logging_spec(settings);
    }

    let mut n_matches: u32 = 0;
    do iter_crate_map(crate_map) |entry: *mut ModEntry| {
        let m = update_entry(dirs, entry);
        n_matches += m;
    }

    if n_matches < (dirs.len() as u32) {
        dumb_println(fmt!("warning: got %u RUST_LOG specs but only matched %u of them.\n\
                          You may have mistyped a RUST_LOG spec.\n\
                          Use RUST_LOG=::help to see the list of crates and modules.\n",
                          dirs.len() as uint, n_matches as uint));
    }
}

pub trait Logger {
    fn log(&mut self, msg: Either<~str, &'static str>);
}

pub struct StdErrLogger;

impl Logger for StdErrLogger {
    fn log(&mut self, msg: Either<~str, &'static str>) {
        use io::{Writer, WriterUtil};

        if !should_log_console() {
            return;
        }

        let s: &str = match msg {
            Left(ref s) => {
                let s: &str = *s;
                s
            }
            Right(ref s) => {
                let s: &str = *s;
                s
            }
        };

        // Truncate the string
        let buf_bytes = 2048;
        if s.len() > buf_bytes {
            let s = s.slice(0, buf_bytes) + "[...]";
            print(s);
        } else {
            print(s)
        };

        fn print(s: &str) {
            let dbg = STDERR_FILENO as ::io::fd_t;
            dbg.write_str(s);
            dbg.write_str("\n");
            dbg.flush();
        }
    }
}
/// Configure logging by traversing the crate map and setting the
/// per-module global logging flags based on the logging spec
#[fixed_stack_segment] #[inline(never)]
pub fn init(crate_map: *u8) {
    use os;

    let log_spec = os::getenv("RUST_LOG");
    match log_spec {
        Some(spec) => {
            update_log_settings(crate_map, spec);
        }
        None => {
            update_log_settings(crate_map, ~"");
        }
    }
}

#[fixed_stack_segment] #[inline(never)]
pub fn console_on() { unsafe { rust_log_console_on() } }

#[fixed_stack_segment] #[inline(never)]
pub fn console_off() { unsafe { rust_log_console_off() } }

#[fixed_stack_segment] #[inline(never)]
fn should_log_console() -> bool { unsafe { rust_should_log_console() != 0 } }

extern {
    fn rust_log_console_on();
    fn rust_log_console_off();
    fn rust_should_log_console() -> uintptr_t;
}

// Tests for parse_logging_spec()
#[test]
fn parse_logging_spec_valid() {
    let dirs = parse_logging_spec(~"crate1::mod1=1,crate1::mod2,crate2=4");
    assert_eq!(dirs.len(), 3);
    assert!(dirs[0].name == Some(~"crate1::mod1"));
    assert_eq!(dirs[0].level, 1);

    assert!(dirs[1].name == Some(~"crate1::mod2"));
    assert_eq!(dirs[1].level, MAX_LOG_LEVEL);

    assert!(dirs[2].name == Some(~"crate2"));
    assert_eq!(dirs[2].level, 4);
}

#[test]
fn parse_logging_spec_invalid_crate() {
    // test parse_logging_spec with multiple = in specification
    let dirs = parse_logging_spec(~"crate1::mod1=1=2,crate2=4");
    assert_eq!(dirs.len(), 1);
    assert!(dirs[0].name == Some(~"crate2"));
    assert_eq!(dirs[0].level, 4);
}

#[test]
fn parse_logging_spec_invalid_log_level() {
    // test parse_logging_spec with 'noNumber' as log level
    let dirs = parse_logging_spec(~"crate1::mod1=noNumber,crate2=4");
    assert_eq!(dirs.len(), 1);
    assert!(dirs[0].name == Some(~"crate2"));
    assert_eq!(dirs[0].level, 4);
}

#[test]
fn parse_logging_spec_string_log_level() {
    // test parse_logging_spec with 'warn' as log level
    let dirs = parse_logging_spec(~"crate1::mod1=wrong,crate2=warn");
    assert_eq!(dirs.len(), 1);
    assert!(dirs[0].name == Some(~"crate2"));
    assert_eq!(dirs[0].level, 2);
}

#[test]
fn parse_logging_spec_global() {
    // test parse_logging_spec with no crate
    let dirs = parse_logging_spec(~"warn,crate2=4");
    assert_eq!(dirs.len(), 2);
    assert!(dirs[0].name == None);
    assert_eq!(dirs[0].level, 2);
    assert!(dirs[1].name == Some(~"crate2"));
    assert_eq!(dirs[1].level, 4);
}

// Tests for update_entry
#[test]
fn update_entry_match_full_path() {
    use c_str::ToCStr;
    let dirs = ~[LogDirective {name: Some(~"crate1::mod1"), level: 2 },
                 LogDirective {name: Some(~"crate2"), level: 3}];
    let level = &mut 0;
    unsafe {
        do "crate1::mod1".to_c_str().with_ref |ptr| {
            let entry= &ModEntry {name: ptr, log_level: level};
            let m = update_entry(dirs, transmute(entry));
            assert!(*entry.log_level == 2);
            assert!(m == 1);
        }
    }
}

#[test]
fn update_entry_no_match() {
    use c_str::ToCStr;
    let dirs = ~[LogDirective {name: Some(~"crate1::mod1"), level: 2 },
                 LogDirective {name: Some(~"crate2"), level: 3}];
    let level = &mut 0;
    unsafe {
        do "crate3::mod1".to_c_str().with_ref |ptr| {
            let entry= &ModEntry {name: ptr, log_level: level};
            let m = update_entry(dirs, transmute(entry));
            assert!(*entry.log_level == DEFAULT_LOG_LEVEL);
            assert!(m == 0);
        }
    }
}

#[test]
fn update_entry_match_beginning() {
    use c_str::ToCStr;
    let dirs = ~[LogDirective {name: Some(~"crate1::mod1"), level: 2 },
                 LogDirective {name: Some(~"crate2"), level: 3}];
    let level = &mut 0;
    unsafe {
        do "crate2::mod1".to_c_str().with_ref |ptr| {
            let entry= &ModEntry {name: ptr, log_level: level};
            let m = update_entry(dirs, transmute(entry));
            assert!(*entry.log_level == 3);
            assert!(m == 1);
        }
    }
}

#[test]
fn update_entry_match_beginning_longest_match() {
    use c_str::ToCStr;
    let dirs = ~[LogDirective {name: Some(~"crate1::mod1"), level: 2 },
                 LogDirective {name: Some(~"crate2"), level: 3},
                 LogDirective {name: Some(~"crate2::mod"), level: 4}];
    let level = &mut 0;
    unsafe {
        do "crate2::mod1".to_c_str().with_ref |ptr| {
            let entry = &ModEntry {name: ptr, log_level: level};
            let m = update_entry(dirs, transmute(entry));
            assert!(*entry.log_level == 4);
            assert!(m == 1);
        }
    }
}

#[test]
fn update_entry_match_default() {
    use c_str::ToCStr;
    let dirs = ~[LogDirective {name: Some(~"crate1::mod1"), level: 2 },
                 LogDirective {name: None, level: 3}
                ];
    let level = &mut 0;
    unsafe {
        do "crate1::mod1".to_c_str().with_ref |ptr| {
            let entry= &ModEntry {name: ptr, log_level: level};
            let m = update_entry(dirs, transmute(entry));
            assert!(*entry.log_level == 2);
            assert!(m == 1);
        }
        do "crate2::mod2".to_c_str().with_ref |ptr| {
            let entry= &ModEntry {name: ptr, log_level: level};
            let m = update_entry(dirs, transmute(entry));
            assert!(*entry.log_level == 3);
            assert!(m == 1);
        }
    }
}
<|endoftext|>


<commit_before>/*! A codemap is a thing that maps uints to file/line/column positions
 * in a crate. This to make it possible to represent the positions
 * with single-word things, rather than passing records all over the
 * compiler.
 */

use dvec::DVec;
use std::serialization::{Serializable,
                         Deserializable,
                         Serializer,
                         Deserializer};

export filename;
export filemap;
export filemap_;
export span;
export file_substr;
export fss_none;
export fss_internal;
export fss_external;
export CodeMap;
export expn_info;
export expn_info_;
export expanded_from;
export new_filemap;
export new_filemap_w_substr;
export mk_substr_filename;
export lookup_char_pos;
export lookup_char_pos_adj;
export adjust_span;
export span_to_str;
export span_to_filename;
export span_to_lines;
export file_lines;
export get_line;
export next_line;
export span_to_snippet;
export loc;
export get_filemap;
export new_codemap;

type filename = ~str;

struct file_pos {
    ch: uint, byte: uint
}

impl file_pos : cmp::Eq {
    pure fn eq(other: &file_pos) -> bool {
        self.ch == (*other).ch && self.byte == (*other).byte
    }
    pure fn ne(other: &file_pos) -> bool { !self.eq(other) }
}

enum file_substr {
    fss_none,
    fss_internal(span),
    fss_external({filename: ~str, line: uint, col: uint})
}

struct filemap {
    name: filename, substr: file_substr, src: @~str,
    start_pos: file_pos, mut lines: ~[file_pos]
}

impl filemap {
    static fn new_w_substr(+filename: filename, +substr: file_substr,
                           src: @~str,
                           start_pos_ch: uint, start_pos_byte: uint)
        -> filemap {
        return filemap {
            name: filename, substr: substr, src: src,
            start_pos: file_pos {ch: start_pos_ch, byte: start_pos_byte},
            mut lines: ~[file_pos {ch: start_pos_ch, byte: start_pos_byte}]
        };
    }

    static fn new(+filename: filename, src: @~str,
                  start_pos_ch: uint, start_pos_byte: uint)
        -> filemap {
        return filemap::new_w_substr(filename, fss_none, src,
                                     start_pos_ch, start_pos_byte);
    }
}

struct CodeMap_ {
    files: DVec<@filemap>
}

type CodeMap = @CodeMap_;

struct loc {
    file: @filemap, line: uint, col: uint
}

fn new_codemap() -> CodeMap { @CodeMap_ {files: DVec()} }

fn mk_substr_filename(cm: CodeMap, sp: span) -> ~str
{
    let pos = lookup_char_pos(cm, sp.lo);
    return fmt!("<%s:%u:%u>", pos.file.name, pos.line, pos.col);
}

fn next_line(file: @filemap, chpos: uint, byte_pos: uint) {
    file.lines.push(file_pos {ch: chpos, byte: byte_pos + file.start_pos.byte});
}

type lookup_fn = pure fn(file_pos) -> uint;

fn lookup_line(map: CodeMap, pos: uint, lookup: lookup_fn)
    -> {fm: @filemap, line: uint}
{
    let len = map.files.len();
    let mut a = 0u;
    let mut b = len;
    while b - a > 1u {
        let m = (a + b) / 2u;
        if lookup(map.files[m].start_pos) > pos { b = m; } else { a = m; }
    }
    if (a >= len) {
        fail fmt!("position %u does not resolve to a source location", pos)
    }
    let f = map.files[a];
    a = 0u;
    b = vec::len(f.lines);
    while b - a > 1u {
        let m = (a + b) / 2u;
        if lookup(f.lines[m]) > pos { b = m; } else { a = m; }
    }
    return {fm: f, line: a};
}

fn lookup_pos(map: CodeMap, pos: uint, lookup: lookup_fn) -> loc {
    let {fm: f, line: a} = lookup_line(map, pos, lookup);
    return loc {file: f, line: a + 1u, col: pos - lookup(f.lines[a])};
}

fn lookup_char_pos(map: CodeMap, pos: uint) -> loc {
    pure fn lookup(pos: file_pos) -> uint { return pos.ch; }
    return lookup_pos(map, pos, lookup);
}

fn lookup_byte_pos(map: CodeMap, pos: uint) -> loc {
    pure fn lookup(pos: file_pos) -> uint { return pos.byte; }
    return lookup_pos(map, pos, lookup);
}

fn lookup_char_pos_adj(map: CodeMap, pos: uint)
    -> {filename: ~str, line: uint, col: uint, file: Option<@filemap>}
{
    let loc = lookup_char_pos(map, pos);
    match (loc.file.substr) {
      fss_none => {
        {filename: /* FIXME (#2543) */ copy loc.file.name,
         line: loc.line,
         col: loc.col,
         file: Some(loc.file)}
      }
      fss_internal(sp) => {
        lookup_char_pos_adj(map, sp.lo + (pos - loc.file.start_pos.ch))
      }
      fss_external(eloc) => {
        {filename: /* FIXME (#2543) */ copy eloc.filename,
         line: eloc.line + loc.line - 1u,
         col: if loc.line == 1u {eloc.col + loc.col} else {loc.col},
         file: None}
      }
    }
}

fn adjust_span(map: CodeMap, sp: span) -> span {
    pure fn lookup(pos: file_pos) -> uint { return pos.ch; }
    let line = lookup_line(map, sp.lo, lookup);
    match (line.fm.substr) {
      fss_none => sp,
      fss_internal(s) => {
        adjust_span(map, span {lo: s.lo + (sp.lo - line.fm.start_pos.ch),
                               hi: s.lo + (sp.hi - line.fm.start_pos.ch),
                               expn_info: sp.expn_info})}
      fss_external(_) => sp
    }
}

enum expn_info_ {
    expanded_from({call_site: span,
                   callie: {name: ~str, span: Option<span>}})
}
type expn_info = Option<@expn_info_>;

struct span {lo: uint, hi: uint, expn_info: expn_info}

impl span : cmp::Eq {
    pure fn eq(other: &span) -> bool {
        return self.lo == (*other).lo && self.hi == (*other).hi;
    }
    pure fn ne(other: &span) -> bool { !self.eq(other) }
}

impl<S: Serializer> span: Serializable<S> {
    /* Note #1972 -- spans are serialized but not deserialized */
    fn serialize(&self, _s: &S) { }
}

impl<D: Deserializer> span: Deserializable<D> {
    static fn deserialize(_d: &D) -> span {
        ast_util::dummy_sp()
    }
}

fn span_to_str_no_adj(sp: span, cm: CodeMap) -> ~str {
    let lo = lookup_char_pos(cm, sp.lo);
    let hi = lookup_char_pos(cm, sp.hi);
    return fmt!("%s:%u:%u: %u:%u", lo.file.name,
             lo.line, lo.col, hi.line, hi.col)
}

fn span_to_str(sp: span, cm: CodeMap) -> ~str {
    let lo = lookup_char_pos_adj(cm, sp.lo);
    let hi = lookup_char_pos_adj(cm, sp.hi);
    return fmt!("%s:%u:%u: %u:%u", lo.filename,
             lo.line, lo.col, hi.line, hi.col)
}

struct file_lines {
    file: @filemap,
    lines: ~[uint]
}

fn span_to_filename(sp: span, cm: codemap::CodeMap) -> filename {
    let lo = lookup_char_pos(cm, sp.lo);
    return /* FIXME (#2543) */ copy lo.file.name;
}

fn span_to_lines(sp: span, cm: codemap::CodeMap) -> @file_lines {
    let lo = lookup_char_pos(cm, sp.lo);
    let hi = lookup_char_pos(cm, sp.hi);
    let mut lines = ~[];
    for uint::range(lo.line - 1u, hi.line as uint) |i| {
        lines.push(i);
    };
    return @file_lines {file: lo.file, lines: lines};
}

fn get_line(fm: @filemap, line: int) -> ~str unsafe {
    let begin: uint = fm.lines[line].byte - fm.start_pos.byte;
    let end = match str::find_char_from(*fm.src, '\n', begin) {
      Some(e) => e,
      None => str::len(*fm.src)
    };
    str::slice(*fm.src, begin, end)
}

fn lookup_byte_offset(cm: codemap::CodeMap, chpos: uint)
    -> {fm: @filemap, pos: uint} {
    pure fn lookup(pos: file_pos) -> uint { return pos.ch; }
    let {fm, line} = lookup_line(cm, chpos, lookup);
    let line_offset = fm.lines[line].byte - fm.start_pos.byte;
    let col = chpos - fm.lines[line].ch;
    let col_offset = str::count_bytes(*fm.src, line_offset, col);
    {fm: fm, pos: line_offset + col_offset}
}

fn span_to_snippet(sp: span, cm: codemap::CodeMap) -> ~str {
    let begin = lookup_byte_offset(cm, sp.lo);
    let end = lookup_byte_offset(cm, sp.hi);
    assert begin.fm.start_pos == end.fm.start_pos;
    return str::slice(*begin.fm.src, begin.pos, end.pos);
}

fn get_snippet(cm: codemap::CodeMap, fidx: uint, lo: uint, hi: uint) -> ~str
{
    let fm = cm.files[fidx];
    return str::slice(*fm.src, lo, hi)
}

fn get_filemap(cm: CodeMap, filename: ~str) -> @filemap {
    for cm.files.each |fm| { if fm.name == filename { return *fm; } }
    //XXjdm the following triggers a mismatched type bug
    //      (or expected function, found _|_)
    fail; // ("asking for " + filename + " which we don't know about");
}

//
// Local Variables:
// mode: rust
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// End:
//
<commit_msg>Reformatting in codemap<commit_after>/*! A codemap is a thing that maps uints to file/line/column positions
 * in a crate. This to make it possible to represent the positions
 * with single-word things, rather than passing records all over the
 * compiler.
 */

use dvec::DVec;
use std::serialization::{Serializable,
                         Deserializable,
                         Serializer,
                         Deserializer};

export filename;
export filemap;
export filemap_;
export span;
export file_substr;
export fss_none;
export fss_internal;
export fss_external;
export CodeMap;
export expn_info;
export expn_info_;
export expanded_from;
export new_filemap;
export new_filemap_w_substr;
export mk_substr_filename;
export lookup_char_pos;
export lookup_char_pos_adj;
export adjust_span;
export span_to_str;
export span_to_filename;
export span_to_lines;
export file_lines;
export get_line;
export next_line;
export span_to_snippet;
export loc;
export get_filemap;
export new_codemap;

type filename = ~str;

struct file_pos {
    ch: uint, byte: uint
}

impl file_pos : cmp::Eq {
    pure fn eq(other: &file_pos) -> bool {
        self.ch == (*other).ch && self.byte == (*other).byte
    }
    pure fn ne(other: &file_pos) -> bool { !self.eq(other) }
}

enum file_substr {
    fss_none,
    fss_internal(span),
    fss_external({filename: ~str, line: uint, col: uint})
}

struct filemap {
    name: filename,
    substr: file_substr,
    src: @~str,
    start_pos: file_pos,
    mut lines: ~[file_pos]
}

impl filemap {
    static fn new_w_substr(+filename: filename, +substr: file_substr,
                           src: @~str,
                           start_pos_ch: uint, start_pos_byte: uint)
        -> filemap {
        return filemap {
            name: filename, substr: substr, src: src,
            start_pos: file_pos {ch: start_pos_ch, byte: start_pos_byte},
            mut lines: ~[file_pos {ch: start_pos_ch, byte: start_pos_byte}]
        };
    }

    static fn new(+filename: filename, src: @~str,
                  start_pos_ch: uint, start_pos_byte: uint)
        -> filemap {
        return filemap::new_w_substr(filename, fss_none, src,
                                     start_pos_ch, start_pos_byte);
    }
}

struct CodeMap_ {
    files: DVec<@filemap>
}

type CodeMap = @CodeMap_;

struct loc {
    file: @filemap, line: uint, col: uint
}

fn new_codemap() -> CodeMap { @CodeMap_ {files: DVec()} }

fn mk_substr_filename(cm: CodeMap, sp: span) -> ~str
{
    let pos = lookup_char_pos(cm, sp.lo);
    return fmt!("<%s:%u:%u>", pos.file.name, pos.line, pos.col);
}

fn next_line(file: @filemap, chpos: uint, byte_pos: uint) {
    file.lines.push(file_pos {ch: chpos, byte: byte_pos + file.start_pos.byte});
}

type lookup_fn = pure fn(file_pos) -> uint;

fn lookup_line(map: CodeMap, pos: uint, lookup: lookup_fn)
    -> {fm: @filemap, line: uint}
{
    let len = map.files.len();
    let mut a = 0u;
    let mut b = len;
    while b - a > 1u {
        let m = (a + b) / 2u;
        if lookup(map.files[m].start_pos) > pos { b = m; } else { a = m; }
    }
    if (a >= len) {
        fail fmt!("position %u does not resolve to a source location", pos)
    }
    let f = map.files[a];
    a = 0u;
    b = vec::len(f.lines);
    while b - a > 1u {
        let m = (a + b) / 2u;
        if lookup(f.lines[m]) > pos { b = m; } else { a = m; }
    }
    return {fm: f, line: a};
}

fn lookup_pos(map: CodeMap, pos: uint, lookup: lookup_fn) -> loc {
    let {fm: f, line: a} = lookup_line(map, pos, lookup);
    return loc {file: f, line: a + 1u, col: pos - lookup(f.lines[a])};
}

fn lookup_char_pos(map: CodeMap, pos: uint) -> loc {
    pure fn lookup(pos: file_pos) -> uint { return pos.ch; }
    return lookup_pos(map, pos, lookup);
}

fn lookup_byte_pos(map: CodeMap, pos: uint) -> loc {
    pure fn lookup(pos: file_pos) -> uint { return pos.byte; }
    return lookup_pos(map, pos, lookup);
}

fn lookup_char_pos_adj(map: CodeMap, pos: uint)
    -> {filename: ~str, line: uint, col: uint, file: Option<@filemap>}
{
    let loc = lookup_char_pos(map, pos);
    match (loc.file.substr) {
      fss_none => {
        {filename: /* FIXME (#2543) */ copy loc.file.name,
         line: loc.line,
         col: loc.col,
         file: Some(loc.file)}
      }
      fss_internal(sp) => {
        lookup_char_pos_adj(map, sp.lo + (pos - loc.file.start_pos.ch))
      }
      fss_external(eloc) => {
        {filename: /* FIXME (#2543) */ copy eloc.filename,
         line: eloc.line + loc.line - 1u,
         col: if loc.line == 1u {eloc.col + loc.col} else {loc.col},
         file: None}
      }
    }
}

fn adjust_span(map: CodeMap, sp: span) -> span {
    pure fn lookup(pos: file_pos) -> uint { return pos.ch; }
    let line = lookup_line(map, sp.lo, lookup);
    match (line.fm.substr) {
      fss_none => sp,
      fss_internal(s) => {
        adjust_span(map, span {lo: s.lo + (sp.lo - line.fm.start_pos.ch),
                               hi: s.lo + (sp.hi - line.fm.start_pos.ch),
                               expn_info: sp.expn_info})}
      fss_external(_) => sp
    }
}

enum expn_info_ {
    expanded_from({call_site: span,
                   callie: {name: ~str, span: Option<span>}})
}
type expn_info = Option<@expn_info_>;

struct span {lo: uint, hi: uint, expn_info: expn_info}

impl span : cmp::Eq {
    pure fn eq(other: &span) -> bool {
        return self.lo == (*other).lo && self.hi == (*other).hi;
    }
    pure fn ne(other: &span) -> bool { !self.eq(other) }
}

impl<S: Serializer> span: Serializable<S> {
    /* Note #1972 -- spans are serialized but not deserialized */
    fn serialize(&self, _s: &S) { }
}

impl<D: Deserializer> span: Deserializable<D> {
    static fn deserialize(_d: &D) -> span {
        ast_util::dummy_sp()
    }
}

fn span_to_str_no_adj(sp: span, cm: CodeMap) -> ~str {
    let lo = lookup_char_pos(cm, sp.lo);
    let hi = lookup_char_pos(cm, sp.hi);
    return fmt!("%s:%u:%u: %u:%u", lo.file.name,
             lo.line, lo.col, hi.line, hi.col)
}

fn span_to_str(sp: span, cm: CodeMap) -> ~str {
    let lo = lookup_char_pos_adj(cm, sp.lo);
    let hi = lookup_char_pos_adj(cm, sp.hi);
    return fmt!("%s:%u:%u: %u:%u", lo.filename,
             lo.line, lo.col, hi.line, hi.col)
}

struct file_lines {
    file: @filemap,
    lines: ~[uint]
}

fn span_to_filename(sp: span, cm: codemap::CodeMap) -> filename {
    let lo = lookup_char_pos(cm, sp.lo);
    return /* FIXME (#2543) */ copy lo.file.name;
}

fn span_to_lines(sp: span, cm: codemap::CodeMap) -> @file_lines {
    let lo = lookup_char_pos(cm, sp.lo);
    let hi = lookup_char_pos(cm, sp.hi);
    let mut lines = ~[];
    for uint::range(lo.line - 1u, hi.line as uint) |i| {
        lines.push(i);
    };
    return @file_lines {file: lo.file, lines: lines};
}

fn get_line(fm: @filemap, line: int) -> ~str unsafe {
    let begin: uint = fm.lines[line].byte - fm.start_pos.byte;
    let end = match str::find_char_from(*fm.src, '\n', begin) {
      Some(e) => e,
      None => str::len(*fm.src)
    };
    str::slice(*fm.src, begin, end)
}

fn lookup_byte_offset(cm: codemap::CodeMap, chpos: uint)
    -> {fm: @filemap, pos: uint} {
    pure fn lookup(pos: file_pos) -> uint { return pos.ch; }
    let {fm, line} = lookup_line(cm, chpos, lookup);
    let line_offset = fm.lines[line].byte - fm.start_pos.byte;
    let col = chpos - fm.lines[line].ch;
    let col_offset = str::count_bytes(*fm.src, line_offset, col);
    {fm: fm, pos: line_offset + col_offset}
}

fn span_to_snippet(sp: span, cm: codemap::CodeMap) -> ~str {
    let begin = lookup_byte_offset(cm, sp.lo);
    let end = lookup_byte_offset(cm, sp.hi);
    assert begin.fm.start_pos == end.fm.start_pos;
    return str::slice(*begin.fm.src, begin.pos, end.pos);
}

fn get_snippet(cm: codemap::CodeMap, fidx: uint, lo: uint, hi: uint) -> ~str
{
    let fm = cm.files[fidx];
    return str::slice(*fm.src, lo, hi)
}

fn get_filemap(cm: CodeMap, filename: ~str) -> @filemap {
    for cm.files.each |fm| { if fm.name == filename { return *fm; } }
    //XXjdm the following triggers a mismatched type bug
    //      (or expected function, found _|_)
    fail; // ("asking for " + filename + " which we don't know about");
}

//
// Local Variables:
// mode: rust
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// End:
//
<|endoftext|>


<commit_before>use std::ascii::AsciiExt;

use std::borrow::Cow::Owned;
use itertools::Itertools;

use liquid;
use liquid::Token::{self, Identifier};
use liquid::lexer::Element::{self, Expression, Tag, Raw};

use syntect::parsing::{SyntaxDefinition, SyntaxSet};
use syntect::highlighting::{ThemeSet, Theme};
use syntect::html::{IncludeBackground, highlighted_snippet_for_string, styles_to_coloured_html,
                    start_coloured_html_snippet};
use syntect::easy::HighlightLines;

use pulldown_cmark as cmark;
use pulldown_cmark::Event::{self, Start, End, Text, Html};

use error;

struct Setup {
    syntax_set: SyntaxSet,
    theme_set: ThemeSet,
}

unsafe impl Send for Setup {}
unsafe impl Sync for Setup {}

lazy_static!{
    static ref SETUP: Setup = Setup {
        syntax_set: SyntaxSet::load_defaults_newlines(),
        theme_set: ThemeSet::load_defaults()
    };
}

pub fn has_syntax_theme(name: &str) -> error::Result<bool> {
    return Ok(SETUP.theme_set.themes.contains_key(name));
}

pub fn list_syntax_themes<'a>() -> Vec<&'a String> {
    return SETUP.theme_set.themes.keys().collect::<Vec<_>>();
}

pub fn list_syntaxes<'a>() -> Vec<String> {
    fn definition_to_string(sd: &SyntaxDefinition) -> String {
        let extensions = sd.file_extensions.iter().join(&", ".to_owned());
        format!("{} [{}]", sd.name, extensions)
    }

    let mut syntaxes = SETUP
        .syntax_set
        .syntaxes()
        .iter()
        .map(definition_to_string)
        .collect::<Vec<_>>();

    // sort alphabetically with insensitive ascii case
    syntaxes.sort_by_key(|a| (a as &str).to_ascii_lowercase());

    return syntaxes;
}

struct CodeBlock {
    lang: Option<String>,
    code: String,
    theme: Theme,
}

impl liquid::Renderable for CodeBlock {
    fn render(&self, _: &mut liquid::Context) -> Result<Option<String>, liquid::Error> {
        let syntax = match self.lang {
                Some(ref lang) => SETUP.syntax_set.find_syntax_by_token(lang),
                _ => None,
            }
            .unwrap_or_else(|| SETUP.syntax_set.find_syntax_plain_text());

        Ok(Some(highlighted_snippet_for_string(&self.code, syntax, &self.theme)))
    }
}

pub fn initialize_codeblock(arguments: &[Token],
                            tokens: &[Element],
                            theme_name: &str)
                            -> Result<Box<liquid::Renderable>, liquid::Error> {

    let content = tokens
        .iter()
        .fold("".to_owned(), |a, b| {
            match *b {
                    Expression(_, ref text) |
                    Tag(_, ref text) |
                    Raw(ref text) => text,
                }
                .to_owned() + &a
        });

    let lang = match arguments.iter().next() {
        Some(&Identifier(ref x)) => Some(x.clone()),
        _ => None,
    };

    Ok(Box::new(CodeBlock {
                    code: content,
                    lang: lang,
                    theme: SETUP.theme_set.themes[theme_name].clone(),
                }))
}

pub struct DecoratedParser<'a> {
    h: Option<HighlightLines<'a>>,
    parser: cmark::Parser<'a>,
    theme: &'a Theme,
}

impl<'a> DecoratedParser<'a> {
    pub fn new(parser: cmark::Parser<'a>, theme: &'a Theme) -> Self {
        DecoratedParser {
            h: None,
            parser: parser,
            theme: &theme,
        }
    }
}

impl<'a> Iterator for DecoratedParser<'a> {
    type Item = Event<'a>;

    fn next(&mut self) -> Option<Event<'a>> {
        match self.parser.next() {
            Some(item) => {
                if let Text(text) = item {
                    if let Some(ref mut h) = self.h {
                        let highlighted = &h.highlight(&text);
                        let html = styles_to_coloured_html(highlighted, IncludeBackground::Yes);
                        Some(Html(Owned(html)))
                    } else {
                        Some(Text(text))
                    }
                } else {
                    if let Start(cmark::Tag::CodeBlock(ref info)) = item {
                        // set local highlighter, if found
                        let cur_syntax =
                            info.clone()
                                .split(' ')
                                .next()
                                .and_then(|lang| SETUP.syntax_set.find_syntax_by_token(lang))
                                .unwrap_or_else(|| SETUP.syntax_set.find_syntax_plain_text());
                        self.h = Some(HighlightLines::new(&cur_syntax, self.theme));
                        let snippet = start_coloured_html_snippet(self.theme);
                        return Some(Html(Owned(snippet)));
                    }
                    if let End(cmark::Tag::CodeBlock(_)) = item {
                        // reset highlighter
                        self.h = None;
                        // close the code block
                        return Some(Html(Owned("
".to_owned())));
                    }

                    Some(item)
                }
            }
            None => None,
        }
    }
}

pub fn decorate_markdown<'a>(parser: cmark::Parser<'a>, theme_name: &str) -> DecoratedParser<'a> {
    DecoratedParser::new(parser, &SETUP.theme_set.themes[theme_name])
}

#[cfg(test)]
mod test {

    use std::default::Default;
    use liquid::{self, Renderable, LiquidOptions, Context};

    use super::*;

    const CODE_BLOCK: &'static str = "mod test {
        fn hello(arg: int) -> bool {
            \
                                      true
        }
    }
    ";

    const CODEBLOCK_RENDERED: &'static str = "
\nmod test {\n        fn hello(arg: int) -> bool {\n            true\n        \
         }\n    \
         }\n    \
         \n
\n";

    const MARKDOWN_RENDERED: &'static str = "
\nmod test {\n        fn hello(arg: int) -> bool {\n            true\n        }\n    }\n    \n
";

    #[test]
    fn codeblock_renders_rust() {
        // Syntect isn't thread safe, for now run everything in the same test.
        {
            let mut options: LiquidOptions = Default::default();
            options
                .blocks
                .insert("codeblock".to_string(),
                        Box::new(|_, args, tokens, _| {
                                     initialize_codeblock(args, tokens, "base16-ocean.dark")
                                 }));
            let template = liquid::parse(&format!("{{% codeblock rust %}}{}{{% endcodeblock %}}",
                                                  CODE_BLOCK),
                                         options)
                    .unwrap();
            let mut data = Context::new();
            let output = template.render(&mut data);
            assert_eq!(output.unwrap(), Some(CODEBLOCK_RENDERED.to_string()));
        }

        {
            let html = format!("```rust
{}
```",
                               CODE_BLOCK);

            let mut buf = String::new();
            let parser = cmark::Parser::new(&html);
            cmark::html::push_html(&mut buf, decorate_markdown(parser, "base16-ocean.dark"));
            assert_eq!(buf, MARKDOWN_RENDERED);
        }
    }
}
<commit_msg>chore: Remove useless newline<commit_after>use std::ascii::AsciiExt;

use std::borrow::Cow::Owned;
use itertools::Itertools;

use liquid;
use liquid::Token::{self, Identifier};
use liquid::lexer::Element::{self, Expression, Tag, Raw};

use syntect::parsing::{SyntaxDefinition, SyntaxSet};
use syntect::highlighting::{ThemeSet, Theme};
use syntect::html::{IncludeBackground, highlighted_snippet_for_string, styles_to_coloured_html,
                    start_coloured_html_snippet};
use syntect::easy::HighlightLines;

use pulldown_cmark as cmark;
use pulldown_cmark::Event::{self, Start, End, Text, Html};

use error;

struct Setup {
    syntax_set: SyntaxSet,
    theme_set: ThemeSet,
}

unsafe impl Send for Setup {}
unsafe impl Sync for Setup {}

lazy_static!{
    static ref SETUP: Setup = Setup {
        syntax_set: SyntaxSet::load_defaults_newlines(),
        theme_set: ThemeSet::load_defaults()
    };
}

pub fn has_syntax_theme(name: &str) -> error::Result<bool> {
    return Ok(SETUP.theme_set.themes.contains_key(name));
}

pub fn list_syntax_themes<'a>() -> Vec<&'a String> {
    return SETUP.theme_set.themes.keys().collect::<Vec<_>>();
}

pub fn list_syntaxes<'a>() -> Vec<String> {
    fn definition_to_string(sd: &SyntaxDefinition) -> String {
        let extensions = sd.file_extensions.iter().join(&", ".to_owned());
        format!("{} [{}]", sd.name, extensions)
    }

    let mut syntaxes = SETUP
        .syntax_set
        .syntaxes()
        .iter()
        .map(definition_to_string)
        .collect::<Vec<_>>();

    // sort alphabetically with insensitive ascii case
    syntaxes.sort_by_key(|a| (a as &str).to_ascii_lowercase());

    return syntaxes;
}

struct CodeBlock {
    lang: Option<String>,
    code: String,
    theme: Theme,
}

impl liquid::Renderable for CodeBlock {
    fn render(&self, _: &mut liquid::Context) -> Result<Option<String>, liquid::Error> {
        let syntax = match self.lang {
                Some(ref lang) => SETUP.syntax_set.find_syntax_by_token(lang),
                _ => None,
            }
            .unwrap_or_else(|| SETUP.syntax_set.find_syntax_plain_text());

        Ok(Some(highlighted_snippet_for_string(&self.code, syntax, &self.theme)))
    }
}

pub fn initialize_codeblock(arguments: &[Token],
                            tokens: &[Element],
                            theme_name: &str)
                            -> Result<Box<liquid::Renderable>, liquid::Error> {
    let content = tokens
        .iter()
        .fold("".to_owned(), |a, b| {
            match *b {
                    Expression(_, ref text) |
                    Tag(_, ref text) |
                    Raw(ref text) => text,
                }
                .to_owned() + &a
        });

    let lang = match arguments.iter().next() {
        Some(&Identifier(ref x)) => Some(x.clone()),
        _ => None,
    };

    Ok(Box::new(CodeBlock {
                    code: content,
                    lang: lang,
                    theme: SETUP.theme_set.themes[theme_name].clone(),
                }))
}

pub struct DecoratedParser<'a> {
    h: Option<HighlightLines<'a>>,
    parser: cmark::Parser<'a>,
    theme: &'a Theme,
}

impl<'a> DecoratedParser<'a> {
    pub fn new(parser: cmark::Parser<'a>, theme: &'a Theme) -> Self {
        DecoratedParser {
            h: None,
            parser: parser,
            theme: &theme,
        }
    }
}

impl<'a> Iterator for DecoratedParser<'a> {
    type Item = Event<'a>;

    fn next(&mut self) -> Option<Event<'a>> {
        match self.parser.next() {
            Some(item) => {
                if let Text(text) = item {
                    if let Some(ref mut h) = self.h {
                        let highlighted = &h.highlight(&text);
                        let html = styles_to_coloured_html(highlighted, IncludeBackground::Yes);
                        Some(Html(Owned(html)))
                    } else {
                        Some(Text(text))
                    }
                } else {
                    if let Start(cmark::Tag::CodeBlock(ref info)) = item {
                        // set local highlighter, if found
                        let cur_syntax =
                            info.clone()
                                .split(' ')
                                .next()
                                .and_then(|lang| SETUP.syntax_set.find_syntax_by_token(lang))
                                .unwrap_or_else(|| SETUP.syntax_set.find_syntax_plain_text());
                        self.h = Some(HighlightLines::new(&cur_syntax, self.theme));
                        let snippet = start_coloured_html_snippet(self.theme);
                        return Some(Html(Owned(snippet)));
                    }
                    if let End(cmark::Tag::CodeBlock(_)) = item {
                        // reset highlighter
                        self.h = None;
                        // close the code block
                        return Some(Html(Owned("
".to_owned())));
                    }

                    Some(item)
                }
            }
            None => None,
        }
    }
}

pub fn decorate_markdown<'a>(parser: cmark::Parser<'a>, theme_name: &str) -> DecoratedParser<'a> {
    DecoratedParser::new(parser, &SETUP.theme_set.themes[theme_name])
}

#[cfg(test)]
mod test {

    use std::default::Default;
    use liquid::{self, Renderable, LiquidOptions, Context};

    use super::*;

    const CODE_BLOCK: &'static str = "mod test {
        fn hello(arg: int) -> bool {
            \
                                      true
        }
    }
    ";

    const CODEBLOCK_RENDERED: &'static str = "
\nmod test {\n        fn hello(arg: int) -> bool {\n            true\n        \
         }\n    \
         }\n    \
         \n
\n";

    const MARKDOWN_RENDERED: &'static str = "
\nmod test {\n        fn hello(arg: int) -> bool {\n            true\n        }\n    }\n    \n
";

    #[test]
    fn codeblock_renders_rust() {
        // Syntect isn't thread safe, for now run everything in the same test.
        {
            let mut options: LiquidOptions = Default::default();
            options
                .blocks
                .insert("codeblock".to_string(),
                        Box::new(|_, args, tokens, _| {
                                     initialize_codeblock(args, tokens, "base16-ocean.dark")
                                 }));
            let template = liquid::parse(&format!("{{% codeblock rust %}}{}{{% endcodeblock %}}",
                                                  CODE_BLOCK),
                                         options)
                    .unwrap();
            let mut data = Context::new();
            let output = template.render(&mut data);
            assert_eq!(output.unwrap(), Some(CODEBLOCK_RENDERED.to_string()));
        }

        {
            let html = format!("```rust
{}
```",
                               CODE_BLOCK);

            let mut buf = String::new();
            let parser = cmark::Parser::new(&html);
            cmark::html::push_html(&mut buf, decorate_markdown(parser, "base16-ocean.dark"));
            assert_eq!(buf, MARKDOWN_RENDERED);
        }
    }
}
<|endoftext|>


<commit_before>// Support code for rustc's built in test runner generator. Currently,
// none of this is meant for users. It is intended to support the
// simplest interface possible for representing and running tests
// while providing a base that other test frameworks may build off of.

export test_name;
export test_fn;
export test_desc;
export test_main;

// The name of a test. By convention this follows the rules for rust
// paths, i.e it should be a series of identifiers seperated by double
// colons. This way if some test runner wants to arrange the tests
// heirarchically it may.
type test_name = str;

// A function that runs a test. If the function returns successfully,
// the test succeeds; if the function fails then the test fails. We
// may need to come up with a more clever definition of test in order
// to support isolation of tests into tasks.
type test_fn = fn();

// The definition of a single test. A test runner will run a list of
// these.
type test_desc = rec(test_name name,
                     test_fn fn);

// The default console test runner. It accepts the command line
// arguments and a vector of test_descs (generated at compile time).
fn test_main(&vec[str] args, &test_desc[] tests) -> int {
    if (run_tests(tests)) {
        ret 0;
    } else {
        ret -1;
    }
}

fn run_tests(&test_desc[] tests) -> bool {

    auto out = io::stdout();

    auto total = ivec::len(tests);
    out.write_line(#fmt("running %u tests", total));

    auto passed = 0u;
    auto failed = 0u;

    for (test_desc test in tests) {
        out.write_str(#fmt("running %s ... ", test.name));
        if (run_test(test)) {
            passed += 1u;
            write_ok(out);
            out.write_line("");
        } else {
            failed += 1u;
            write_failed(out);
            out.write_line("");
        }
    }

    assert passed + failed == total;

    out.write_str(#fmt("\nresults: %u passed; %u failed\n\n",
                        passed, failed));

    ret true;

    fn run_test(&test_desc test) -> bool {
        test.fn();
        ret true;
    }

    fn write_ok(&io::writer out) {
        if (term::color_supported()) {
            term::fg(out.get_buf_writer(), term::color_green);
        }
        out.write_str("ok");
        if (term::color_supported()) {
            term::reset(out.get_buf_writer());
        }
     }

    fn write_failed(&io::writer out) {
        if (term::color_supported()) {
            term::fg(out.get_buf_writer(), term::color_red);
        }
        out.write_str("FAILED");
        if (term::color_supported()) {
            term::reset(out.get_buf_writer());
        }
    }
}

// Local Variables:
// mode: rust;
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// compile-command: "make -k -C .. 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
// End:
<commit_msg>Support running a subset of tests from the command line. Issue #428<commit_after>// Support code for rustc's built in test runner generator. Currently,
// none of this is meant for users. It is intended to support the
// simplest interface possible for representing and running tests
// while providing a base that other test frameworks may build off of.

export test_name;
export test_fn;
export test_desc;
export test_main;

// The name of a test. By convention this follows the rules for rust
// paths, i.e it should be a series of identifiers seperated by double
// colons. This way if some test runner wants to arrange the tests
// heirarchically it may.
type test_name = str;

// A function that runs a test. If the function returns successfully,
// the test succeeds; if the function fails then the test fails. We
// may need to come up with a more clever definition of test in order
// to support isolation of tests into tasks.
type test_fn = fn();

// The definition of a single test. A test runner will run a list of
// these.
type test_desc = rec(test_name name,
                     test_fn fn);

// The default console test runner. It accepts the command line
// arguments and a vector of test_descs (generated at compile time).
fn test_main(&vec[str] args, &test_desc[] tests) -> int {
    if (run_tests(parse_opts(args), tests)) {
        ret 0;
    } else {
        ret -1;
    }
}

type test_opts = rec(option::t[str] filter);

fn parse_opts(&vec[str] args) -> test_opts {
    rec(filter = if (vec::len(args) > 1u) {
            option::some(args.(1))
        } else {
            option::none
        })
}

// A simple console test runner
fn run_tests(&test_opts opts, &test_desc[] tests) -> bool {

    auto filtered_tests = filter_tests(opts, tests);

    auto out = io::stdout();

    auto total = ivec::len(filtered_tests);
    out.write_line(#fmt("running %u tests", total));

    auto passed = 0u;
    auto failed = 0u;

    for (test_desc test in filtered_tests) {
        out.write_str(#fmt("running %s ... ", test.name));
        if (run_test(test)) {
            passed += 1u;
            write_ok(out);
            out.write_line("");
        } else {
            failed += 1u;
            write_failed(out);
            out.write_line("");
        }
    }

    assert passed + failed == total;

    out.write_str(#fmt("\nresults: %u passed; %u failed\n\n",
                        passed, failed));

    ret true;

    fn run_test(&test_desc test) -> bool {
        test.fn();
        ret true;
    }

    fn write_ok(&io::writer out) {
        if (term::color_supported()) {
            term::fg(out.get_buf_writer(), term::color_green);
        }
        out.write_str("ok");
        if (term::color_supported()) {
            term::reset(out.get_buf_writer());
        }
     }

    fn write_failed(&io::writer out) {
        if (term::color_supported()) {
            term::fg(out.get_buf_writer(), term::color_red);
        }
        out.write_str("FAILED");
        if (term::color_supported()) {
            term::reset(out.get_buf_writer());
        }
    }
}

fn filter_tests(&test_opts opts, &test_desc[] tests) -> test_desc[] {
    if (option::is_none(opts.filter)) {
        ret tests;
    }

    auto filter_str = alt opts.filter { option::some(?f) { f }
                                        option::none { "" } };

    auto filter = bind fn(&test_desc test,
                          str filter_str) -> option::t[test_desc] {
        if (str::find(test.name, filter_str) >= 0) {
            ret option::some(test);
        } else {
            ret option::none;
        }
    } (_, filter_str);

    ret ivec::filter_map(filter, tests);
}

// Local Variables:
// mode: rust;
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// compile-command: "make -k -C .. 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
// End:
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use selectors::gecko_like_types as dummies;
use servo_arc::Arc;
use std::mem::{size_of, align_of};
use style;
use style::applicable_declarations::ApplicableDeclarationBlock;
use style::data::{ElementData, ElementStyles};
use style::gecko::selector_parser as real;
use style::properties::ComputedValues;
use style::rule_tree::{RuleNode, StrongRuleNode};
use style::values::computed;
use style::values::specified;

#[test]
fn size_of_selectors_dummy_types() {
    assert_eq!(size_of::<dummies::PseudoClass>(), size_of::<real::NonTSPseudoClass>());
    assert_eq!(align_of::<dummies::PseudoClass>(), align_of::<real::NonTSPseudoClass>());

    assert_eq!(size_of::<dummies::PseudoElement>(), size_of::<real::PseudoElement>());
    assert_eq!(align_of::<dummies::PseudoElement>(), align_of::<real::PseudoElement>());

    assert_eq!(size_of::<dummies::Atom>(), size_of::<style::Atom>());
    assert_eq!(align_of::<dummies::Atom>(), align_of::<style::Atom>());
}

// The size of this is critical to performance on the bloom-basic microbenchmark.
// When iterating over a large Rule array, we want to be able to fast-reject
// selectors (with the inline hashes) with as few cache misses as possible.
size_of_test!(test_size_of_rule, style::stylist::Rule, 32);

// Large pages generate tens of thousands of ComputedValues.
size_of_test!(test_size_of_cv, ComputedValues, 272);

size_of_test!(test_size_of_option_arc_cv, Option<Arc<ComputedValues>>, 8);
size_of_test!(test_size_of_option_rule_node, Option<StrongRuleNode>, 8);

size_of_test!(test_size_of_element_styles, ElementStyles, 16);
size_of_test!(test_size_of_element_data, ElementData, 24);

size_of_test!(test_size_of_property_declaration, style::properties::PropertyDeclaration, 32);

size_of_test!(test_size_of_application_declaration_block, ApplicableDeclarationBlock, 24);

// FIXME(bholley): This can shrink with a little bit of work.
// See https://github.com/servo/servo/issues/17280
size_of_test!(test_size_of_rule_node, RuleNode, 80);

// This is huge, but we allocate it on the stack and then never move it,
// we only pass `&mut SourcePropertyDeclaration` references around.
size_of_test!(test_size_of_parsed_declaration, style::properties::SourcePropertyDeclaration, 704);

size_of_test!(test_size_of_computed_image, computed::image::Image, 40);
size_of_test!(test_size_of_specified_image, specified::image::Image, 40);

// FIXME(bz): These can shrink if we move the None_ value inside the
// enum instead of paying an extra word for the Either discriminant.
size_of_test!(test_size_of_computed_image_layer, computed::image::ImageLayer, 48);
size_of_test!(test_size_of_specified_image_layer, specified::image::ImageLayer, 48);
<commit_msg>stylo: Update ComputedValues size in test<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use selectors::gecko_like_types as dummies;
use servo_arc::Arc;
use std::mem::{size_of, align_of};
use style;
use style::applicable_declarations::ApplicableDeclarationBlock;
use style::data::{ElementData, ElementStyles};
use style::gecko::selector_parser as real;
use style::properties::ComputedValues;
use style::rule_tree::{RuleNode, StrongRuleNode};
use style::values::computed;
use style::values::specified;

#[test]
fn size_of_selectors_dummy_types() {
    assert_eq!(size_of::<dummies::PseudoClass>(), size_of::<real::NonTSPseudoClass>());
    assert_eq!(align_of::<dummies::PseudoClass>(), align_of::<real::NonTSPseudoClass>());

    assert_eq!(size_of::<dummies::PseudoElement>(), size_of::<real::PseudoElement>());
    assert_eq!(align_of::<dummies::PseudoElement>(), align_of::<real::PseudoElement>());

    assert_eq!(size_of::<dummies::Atom>(), size_of::<style::Atom>());
    assert_eq!(align_of::<dummies::Atom>(), align_of::<style::Atom>());
}

// The size of this is critical to performance on the bloom-basic microbenchmark.
// When iterating over a large Rule array, we want to be able to fast-reject
// selectors (with the inline hashes) with as few cache misses as possible.
size_of_test!(test_size_of_rule, style::stylist::Rule, 32);

// Large pages generate tens of thousands of ComputedValues.
size_of_test!(test_size_of_cv, ComputedValues, 256);

size_of_test!(test_size_of_option_arc_cv, Option<Arc<ComputedValues>>, 8);
size_of_test!(test_size_of_option_rule_node, Option<StrongRuleNode>, 8);

size_of_test!(test_size_of_element_styles, ElementStyles, 16);
size_of_test!(test_size_of_element_data, ElementData, 24);

size_of_test!(test_size_of_property_declaration, style::properties::PropertyDeclaration, 32);

size_of_test!(test_size_of_application_declaration_block, ApplicableDeclarationBlock, 24);

// FIXME(bholley): This can shrink with a little bit of work.
// See https://github.com/servo/servo/issues/17280
size_of_test!(test_size_of_rule_node, RuleNode, 80);

// This is huge, but we allocate it on the stack and then never move it,
// we only pass `&mut SourcePropertyDeclaration` references around.
size_of_test!(test_size_of_parsed_declaration, style::properties::SourcePropertyDeclaration, 704);

size_of_test!(test_size_of_computed_image, computed::image::Image, 40);
size_of_test!(test_size_of_specified_image, specified::image::Image, 40);

// FIXME(bz): These can shrink if we move the None_ value inside the
// enum instead of paying an extra word for the Either discriminant.
size_of_test!(test_size_of_computed_image_layer, computed::image::ImageLayer, 48);
size_of_test!(test_size_of_specified_image_layer, specified::image::ImageLayer, 48);
<|endoftext|>


<commit_before>
use array::Array;

#[derive(Copy, Clone)]
pub union MaybeUninitCopy<T>
    where T: Copy
{
    empty: (),
    value: T,
}

impl<T> MaybeUninitCopy<T>
    where T: Copy
{
    /// Create a new MaybeUninit with uninitialized interior
    pub unsafe fn uninitialized() -> Self {
        Self { empty: () }
    }

    /// Create a new MaybeUninit from the value `v`.
    pub fn from(value: T) -> Self {
        Self { value }
    }

    // Raw pointer casts written so that we don't reference or access the
    // uninitialized interior value

    /// Return a raw pointer to the start of the interior array
    pub fn ptr(&self) -> *const T::Item
        where T: Array
    {
        self as *const _ as *const T::Item
    }

    /// Return a mut raw pointer to the start of the interior array
    pub fn ptr_mut(&mut self) -> *mut T::Item
        where T: Array
    {
        self as *mut _ as *mut T::Item
    }
}

<commit_msg>FIX: Use repr(C) MaybeUninitCopy after discussion with RalfJung<commit_after>
use array::Array;

#[derive(Copy, Clone)]
#[repr(C)] // for cast from self ptr to value
pub union MaybeUninitCopy<T>
    where T: Copy
{
    empty: (),
    value: T,
}

impl<T> MaybeUninitCopy<T>
    where T: Copy
{
    /// Create a new MaybeUninit with uninitialized interior
    pub unsafe fn uninitialized() -> Self {
        Self { empty: () }
    }

    /// Create a new MaybeUninit from the value `v`.
    pub fn from(value: T) -> Self {
        Self { value }
    }

    // Raw pointer casts written so that we don't reference or access the
    // uninitialized interior value

    /// Return a raw pointer to the start of the interior array
    pub fn ptr(&self) -> *const T::Item
        where T: Array
    {
        self as *const _ as *const T::Item
    }

    /// Return a mut raw pointer to the start of the interior array
    pub fn ptr_mut(&mut self) -> *mut T::Item
        where T: Array
    {
        self as *mut _ as *mut T::Item
    }
}

<|endoftext|>


<commit_before>use std::fs::File;
use std::io::BufReader;
use std::path::Path;

use cgmath::Point2;
use cgmath::conv::*;

use glium::backend::Facade;
use glium::texture::{RawImage2d, SrgbTexture2d};

use image::ImageFormat;

use crate::obj_load;
use crate::color::Color;

/// Material for CPU rendering
pub struct Material {
    pub diffuse: Color,
    pub diffuse_image: Option<image::RgbaImage>,    // Texture on the CPU
    pub emissive: Option<Color>,
}

/// Material for GPU rendering
pub struct GPUMaterial {
    pub diffuse: [f32; 3],
    pub has_diffuse: bool,
    pub diffuse_texture: SrgbTexture2d,      // Texture on the GPU
    pub has_emissive: bool,
}

impl Material {
    /// Create a new material based on a material loaded from the scene file
    pub fn new(obj_mat: &obj_load::Material) -> Material {
        // Create diffuse texture and load it to the GPU
        let diffuse_image = match obj_mat.tex_diffuse {
            Some(ref tex_path) => Some(Material::load_image(tex_path)),
            None => None
        };
        let emissive = obj_mat.c_emissive.and_then(
            |e| if e == [0.0, 0.0, 0.0] { None }
            else { Some(Color::from(e)) });

        Material {
            diffuse: Color::from(obj_mat.c_diffuse.expect("No diffuse color!")),
            diffuse_image,
            emissive,
        }
    }

    /// Load an image from path
    fn load_image(path: &Path) -> image::RgbaImage {
        let image_format = match path.extension().unwrap().to_str().unwrap() {
            "png" => ImageFormat::PNG,
            "jpg" | "jpeg" => ImageFormat::JPEG,
            "gif" => ImageFormat::GIF,
            "webp" => ImageFormat::WEBP,
            "pnm" => ImageFormat::PNM,
            "tiff" => ImageFormat::TIFF,
            "tga" => ImageFormat::TGA,
            "bmp" => ImageFormat::BMP,
            "ico" => ImageFormat::ICO,
            "hdr" => ImageFormat::HDR,
            ext => panic!("Unknown image extension {}", ext),
        };
        // TODO: dont panic just warn and ignore texture
        let tex_reader = BufReader::new(File::open(path).unwrap_or_else(
            |_| panic!("Failed to open image {:?}!", path)));
        image::load(tex_reader, image_format).expect("Failed to load image!").to_rgba()
    }

    /// Upload textures to the GPU
    pub fn upload_textures<F: Facade>(&self, facade: &F) -> GPUMaterial {
        let diffuse_texture = match self.diffuse_image {
            Some(ref image) => {
                let image_dim = image.dimensions();
                let tex_image = RawImage2d::from_raw_rgba_reversed(&image.clone().into_raw(), image_dim);
                SrgbTexture2d::new(facade, tex_image).expect("Failed to upload texture!")
            }
            // Use empty texture as a placeholder
            None => SrgbTexture2d::empty(facade, 0, 0).expect("Failed to upload empty texture!")
        };
        GPUMaterial {
            diffuse: array3(self.diffuse),
            has_diffuse: self.diffuse_image.is_some(),
            diffuse_texture,
            has_emissive: self.emissive.is_some(),
        }
    }

    pub fn diffuse(&self, tex_coords: Point2<f32>) -> Color {
        if let Some(tex) = &self.diffuse_image {
            let (width, height) = tex.dimensions();
            let x = tex_coords.x.mod_euc(1.0) * (width - 1) as f32;
            let y = (1.0 - tex_coords.y.mod_euc(1.0)) * (height - 1) as f32;
            let x_fract = x.fract();
            let y_fract = y.fract();
            let (left, right) = if x >= (width - 1) as f32{
                (width - 1, width - 1)
            } else {
                (x.floor() as u32, x.ceil() as u32)
            };
            let (top, bottom) = if y >= (height - 1) as f32{
                (height - 1, height - 1)
            } else {
                (y.floor() as u32, y.ceil() as u32)
            };
            let tl = Color::from_srgb(*tex.get_pixel(left, top));
            let bl = Color::from_srgb(*tex.get_pixel(left, bottom));
            let tr = Color::from_srgb(*tex.get_pixel(right, top));
            let br = Color::from_srgb(*tex.get_pixel(right, bottom));
            let top_c = x_fract * tr + (1.0 - x_fract) * tl;
            let bottom_c = x_fract * br + (1.0 - x_fract) * bl;
            y_fract * bottom_c + (1.0 - y_fract) * top_c
        } else {
            self.diffuse
        }
    }
}
<commit_msg>Don't panic on failed texture loads<commit_after>use std::fs::File;
use std::io::BufReader;
use std::path::Path;

use cgmath::Point2;
use cgmath::conv::*;

use glium::backend::Facade;
use glium::texture::{RawImage2d, SrgbTexture2d};

use image::ImageFormat;

use crate::obj_load;
use crate::color::Color;

/// Material for CPU rendering
pub struct Material {
    pub diffuse: Color,
    pub diffuse_image: Option<image::RgbaImage>,    // Texture on the CPU
    pub emissive: Option<Color>,
}

/// Material for GPU rendering
pub struct GPUMaterial {
    pub diffuse: [f32; 3],
    pub has_diffuse: bool,
    pub diffuse_texture: SrgbTexture2d,      // Texture on the GPU
    pub has_emissive: bool,
}

impl Material {
    /// Create a new material based on a material loaded from the scene file
    pub fn new(obj_mat: &obj_load::Material) -> Material {
        // Create diffuse texture and load it to the GPU
        let diffuse_image = match obj_mat.tex_diffuse {
            Some(ref tex_path) => load_image(tex_path),
            None => None
        };
        let emissive = obj_mat.c_emissive.and_then(
            |e| if e == [0.0, 0.0, 0.0] { None }
            else { Some(Color::from(e)) });

        Material {
            diffuse: Color::from(obj_mat.c_diffuse.expect("No diffuse color!")),
            diffuse_image,
            emissive,
        }
    }

    /// Upload textures to the GPU
    pub fn upload_textures<F: Facade>(&self, facade: &F) -> GPUMaterial {
        let diffuse_texture = match self.diffuse_image {
            Some(ref image) => {
                let image_dim = image.dimensions();
                let tex_image = RawImage2d::from_raw_rgba_reversed(&image.clone().into_raw(), image_dim);
                SrgbTexture2d::new(facade, tex_image).expect("Failed to upload texture!")
            }
            // Use empty texture as a placeholder
            None => SrgbTexture2d::empty(facade, 0, 0).expect("Failed to upload empty texture!")
        };
        GPUMaterial {
            diffuse: array3(self.diffuse),
            has_diffuse: self.diffuse_image.is_some(),
            diffuse_texture,
            has_emissive: self.emissive.is_some(),
        }
    }

    pub fn diffuse(&self, tex_coords: Point2<f32>) -> Color {
        if let Some(tex) = &self.diffuse_image {
            let (width, height) = tex.dimensions();
            let x = tex_coords.x.mod_euc(1.0) * (width - 1) as f32;
            let y = (1.0 - tex_coords.y.mod_euc(1.0)) * (height - 1) as f32;
            let x_fract = x.fract();
            let y_fract = y.fract();
            let (left, right) = if x >= (width - 1) as f32{
                (width - 1, width - 1)
            } else {
                (x.floor() as u32, x.ceil() as u32)
            };
            let (top, bottom) = if y >= (height - 1) as f32{
                (height - 1, height - 1)
            } else {
                (y.floor() as u32, y.ceil() as u32)
            };
            let tl = Color::from_srgb(*tex.get_pixel(left, top));
            let bl = Color::from_srgb(*tex.get_pixel(left, bottom));
            let tr = Color::from_srgb(*tex.get_pixel(right, top));
            let br = Color::from_srgb(*tex.get_pixel(right, bottom));
            let top_c = x_fract * tr + (1.0 - x_fract) * tl;
            let bottom_c = x_fract * br + (1.0 - x_fract) * bl;
            y_fract * bottom_c + (1.0 - y_fract) * top_c
        } else {
            self.diffuse
        }
    }
}

/// Load an image from path
fn load_image(path: &Path) -> Option<image::RgbaImage> {
    let image_format = match path.extension().unwrap().to_str().unwrap() {
        "png" => ImageFormat::PNG,
        "jpg" | "jpeg" => ImageFormat::JPEG,
        "gif" => ImageFormat::GIF,
        "webp" => ImageFormat::WEBP,
        "pnm" => ImageFormat::PNM,
        "tiff" => ImageFormat::TIFF,
        "tga" => ImageFormat::TGA,
        "bmp" => ImageFormat::BMP,
        "ico" => ImageFormat::ICO,
        "hdr" => ImageFormat::HDR,
        ext => {
            println!("Unknown image extension {}", ext);
            return None;
        },
    };
    let tex_reader = match File::open(path) {
        Ok(file) => BufReader::new(file),
        Err(err) => {
            println!("Failed to open image {:?}: {}", path, err);
            return None;
        },
    };
    match image::load(tex_reader, image_format) {
        Ok(image) => Some(image.to_rgba()),
        Err(err) => {
            println!("Failed to open image {:?}: {}", path, err);
            None
        },
    }
}
<|endoftext|>


<commit_before><commit_msg>Reverting the accidental commit of a rust file (smohanty)<commit_after><|endoftext|>