<commit_before>use crate::display_delegate::display;
use proc_macro2::{Group, Span, TokenTree};
use quote::ToTokens;
use std::fmt::{Error, Formatter};
use syn::punctuated::Punctuated;
use syn::token::{Colon2, Semi};
use syn::{
    self, ArgCaptured, Block, Expr, ExprVerbatim, FnArg, FnDecl, GenericParam, Ident, Pat,
    PatIdent, PathSegment, Stmt,
};

const MOCKTOPUS_CRATE_NAME: &str = "__mocktopus_crate__";
const STD_CRATE_NAME: &str = "__mocktopus_std__";
const ARGS_TO_CONTINUE_NAME: &str = "__mocktopus_args_to_continue__";
const UNWIND_DATA_NAME: &str = "__mocktopus_unwind_data__";

macro_rules! error_msg {
    ($msg:expr) => {
        concat!("Mocktopus internal error: ", $msg)
    };
}

pub enum FnHeaderBuilder<'a> {
    StaticFn,
    StructImpl,
    TraitDefault,
    TraitImpl(&'a Punctuated<PathSegment, Colon2>),
}

impl<'a> FnHeaderBuilder<'a> {
    pub fn build(&self, fn_ident: &Ident, fn_decl: &FnDecl, fn_block_span: Span) -> Stmt {
        let fn_args = &fn_decl.inputs;
        let header_str = format!(
            r#"{{
    extern crate mocktopus as {mocktopus};
    extern crate std as {std_crate};
    match {std_crate}::panic::catch_unwind({std_crate}::panic::AssertUnwindSafe (
            || {mocktopus}::mocking::Mockable::call_mock(&{full_fn_name}, {extract_args}))) {{
        Ok({mocktopus}::mocking::MockResult::Continue({args_to_continue})) => {restore_args},
        Ok({mocktopus}::mocking::MockResult::Return(result)) => {{
            {forget_args}
            let returned = unsafe {{ {std_crate}::mem::transmute_copy(&result) }};
            {std_crate}::mem::forget(result);
            return returned;
        }},
        Err({unwind}) => {{
            {forget_args}
            {std_crate}::panic::resume_unwind({unwind});
        }},
    }}
}}"#,
            mocktopus = MOCKTOPUS_CRATE_NAME,
            std_crate = STD_CRATE_NAME,
            full_fn_name = display(|f| write_full_fn_name(f, self, fn_ident, fn_decl)),
            extract_args = display(|f| write_extract_args(f, fn_args)),
            args_to_continue = ARGS_TO_CONTINUE_NAME,
            restore_args = display(|f| write_restore_args(f, fn_args)),
            forget_args = display(|f| write_forget_args(f, fn_args)),
            unwind = UNWIND_DATA_NAME
        );
        let header_block =
            syn::parse_str::<Block>(&header_str).expect(error_msg!("generated header unparsable"));
        create_call_site_spanned_stmt(header_block, fn_block_span)
    }
}

fn create_call_site_spanned_stmt(block: Block, span: Span) -> Stmt {
    let token_stream = block
        .into_token_stream()
        .into_iter()
        .map(|tt| make_token_tree_span_call_site(tt, span))
        .collect();
    Stmt::Semi(
        Expr::Verbatim(ExprVerbatim { tts: token_stream }),
        Semi([span]),
    )
}

fn make_token_tree_span_call_site(mut token_tree: TokenTree, span: Span) -> TokenTree {
    token_tree.set_span(span);
    if let TokenTree::Group(ref mut group) = token_tree {
        let tokens = group
            .stream()
            .into_iter()
            .map(|tt| make_token_tree_span_call_site(tt, span))
            .collect();
        *group = Group::new(group.delimiter(), tokens);
    }
    token_tree
}

fn write_full_fn_name(
    f: &mut Formatter,
    builder: &FnHeaderBuilder,
    fn_ident: &Ident,
    fn_decl: &FnDecl,
) -> Result<(), Error> {
    match *builder {
        FnHeaderBuilder::StaticFn => (),
        FnHeaderBuilder::StructImpl | FnHeaderBuilder::TraitDefault => write!(f, "Self::")?,
        FnHeaderBuilder::TraitImpl(ref path) => {
            write!(f, "<Self as {}>::", display(|f| write_trait_path(f, path)))?
        }
    }
    write!(
        f,
        "{}::<{}>",
        fn_ident,
        display(|f| write_fn_generics(f, fn_decl))
    )
}

fn write_trait_path<T: ToTokens + Clone>(
    f: &mut Formatter,
    path: &Punctuated<PathSegment, T>,
) -> Result<(), Error> {
    write!(f, "{}", path.into_token_stream())
}

fn write_fn_generics(f: &mut Formatter, fn_decl: &FnDecl) -> Result<(), Error> {
    fn_decl
        .generics
        .params
        .iter()
        .filter_map(get_generic_param_name)
        .map(|param| write!(f, "{},", param))
        .collect()
}

fn get_generic_param_name(param: &GenericParam) -> Option<String> {
    match *param {
        GenericParam::Type(ref type_param) => Some(type_param.ident.to_string()),
        _ => None,
    }
}

fn write_extract_args<T>(f: &mut Formatter, fn_args: &Punctuated<FnArg, T>) -> Result<(), Error> {
    if fn_args.is_empty() {
        return write!(f, "()");
    }
    write!(f, "unsafe {{ (")?;
    for fn_arg_name in iter_fn_arg_names(fn_args) {
        write!(
            f,
            "{}::ptr::read(&{} as *const _), ",
            STD_CRATE_NAME, fn_arg_name
        )?;
    }
    write!(f, ") }}")
}

fn write_restore_args<T>(f: &mut Formatter, fn_args: &Punctuated<FnArg, T>) -> Result<(), Error> {
    if fn_args.is_empty() {
        return writeln!(f, "()");
    }
    writeln!(f, "unsafe {{")?;
    for (fn_arg_index, fn_arg_name) in iter_fn_arg_names(fn_args).enumerate() {
        writeln!(
            f,
            "{}::mem::forget({}::mem::replace({}::mocking_utils::as_mut(&{}), {}.{}));",
            STD_CRATE_NAME,
            STD_CRATE_NAME,
            MOCKTOPUS_CRATE_NAME,
            fn_arg_name,
            ARGS_TO_CONTINUE_NAME,
            fn_arg_index
        )?;
    }
    writeln!(f, "}}")
}

fn write_forget_args<T>(f: &mut Formatter, fn_args: &Punctuated<FnArg, T>) -> Result<(), Error> {
    for fn_arg_name in iter_fn_arg_names(fn_args) {
        writeln!(f, "{}::mem::forget({});", STD_CRATE_NAME, fn_arg_name)?;
    }
    Ok(())
}

fn iter_fn_arg_names<'a, T>(
    input_args: &'a Punctuated<FnArg, T>,
) -> impl Iterator<Item = String> + 'a {
    input_args.iter().map(|fn_arg| match *fn_arg {
        FnArg::SelfRef(_) | FnArg::SelfValue(_) => "self".to_string(),
        FnArg::Captured(ArgCaptured {
            pat:
                Pat::Ident(PatIdent {
                    ref ident,
                    subpat: None,
                    ..
                }),
            ..
        }) => ident.to_string(),
        _ => panic!(
            "{}: '{}'",
            error_msg!("invalid fn arg type"),
            fn_arg.clone().into_token_stream()
        ),
    })
}
<commit_msg>Fix arg copying in macro causing test failure<commit_after>use crate::display_delegate::display;
use proc_macro2::{Group, Span, TokenTree};
use quote::ToTokens;
use std::fmt::{Error, Formatter};
use syn::punctuated::Punctuated;
use syn::token::{Colon2, Semi};
use syn::{
    self, ArgCaptured, Block, Expr, ExprVerbatim, FnArg, FnDecl, GenericParam, Ident, Pat,
    PatIdent, PathSegment, Stmt,
};

const MOCKTOPUS_CRATE_NAME: &str = "__mocktopus_crate__";
const STD_CRATE_NAME: &str = "__mocktopus_std__";
const ARGS_TO_CONTINUE_NAME: &str = "__mocktopus_args_to_continue__";
const UNWIND_DATA_NAME: &str = "__mocktopus_unwind_data__";

macro_rules! error_msg {
    ($msg:expr) => {
        concat!("Mocktopus internal error: ", $msg)
    };
}

pub enum FnHeaderBuilder<'a> {
    StaticFn,
    StructImpl,
    TraitDefault,
    TraitImpl(&'a Punctuated<PathSegment, Colon2>),
}

impl<'a> FnHeaderBuilder<'a> {
    pub fn build(&self, fn_ident: &Ident, fn_decl: &FnDecl, fn_block_span: Span) -> Stmt {
        let fn_args = &fn_decl.inputs;
        let header_str = format!(
            r#"{{
    extern crate mocktopus as {mocktopus};
    extern crate std as {std_crate};
    match {std_crate}::panic::catch_unwind({std_crate}::panic::AssertUnwindSafe (
            || {mocktopus}::mocking::Mockable::call_mock(&{full_fn_name}, {extract_args}))) {{
        Ok({mocktopus}::mocking::MockResult::Continue({args_to_continue})) => {restore_args},
        Ok({mocktopus}::mocking::MockResult::Return(result)) => {{
            {forget_args}
            let returned = unsafe {{ {std_crate}::mem::transmute_copy(&result) }};
            {std_crate}::mem::forget(result);
            return returned;
        }},
        Err({unwind}) => {{
            {forget_args}
            {std_crate}::panic::resume_unwind({unwind});
        }},
    }}
}}"#,
            mocktopus = MOCKTOPUS_CRATE_NAME,
            std_crate = STD_CRATE_NAME,
            full_fn_name = display(|f| write_full_fn_name(f, self, fn_ident, fn_decl)),
            extract_args = display(|f| write_extract_args(f, fn_args)),
            args_to_continue = ARGS_TO_CONTINUE_NAME,
            restore_args = display(|f| write_restore_args(f, fn_args)),
            forget_args = display(|f| write_forget_args(f, fn_args)),
            unwind = UNWIND_DATA_NAME
        );
        let header_block =
            syn::parse_str::<Block>(&header_str).expect(error_msg!("generated header unparsable"));
        create_call_site_spanned_stmt(header_block, fn_block_span)
    }
}

fn create_call_site_spanned_stmt(block: Block, span: Span) -> Stmt {
    let token_stream = block
        .into_token_stream()
        .into_iter()
        .map(|tt| make_token_tree_span_call_site(tt, span))
        .collect();
    Stmt::Semi(
        Expr::Verbatim(ExprVerbatim { tts: token_stream }),
        Semi([span]),
    )
}

fn make_token_tree_span_call_site(mut token_tree: TokenTree, span: Span) -> TokenTree {
    token_tree.set_span(span);
    if let TokenTree::Group(ref mut group) = token_tree {
        let tokens = group
            .stream()
            .into_iter()
            .map(|tt| make_token_tree_span_call_site(tt, span))
            .collect();
        *group = Group::new(group.delimiter(), tokens);
    }
    token_tree
}

fn write_full_fn_name(
    f: &mut Formatter,
    builder: &FnHeaderBuilder,
    fn_ident: &Ident,
    fn_decl: &FnDecl,
) -> Result<(), Error> {
    match *builder {
        FnHeaderBuilder::StaticFn => (),
        FnHeaderBuilder::StructImpl | FnHeaderBuilder::TraitDefault => write!(f, "Self::")?,
        FnHeaderBuilder::TraitImpl(ref path) => {
            write!(f, "<Self as {}>::", display(|f| write_trait_path(f, path)))?
        }
    }
    write!(
        f,
        "{}::<{}>",
        fn_ident,
        display(|f| write_fn_generics(f, fn_decl))
    )
}

fn write_trait_path<T: ToTokens + Clone>(
    f: &mut Formatter,
    path: &Punctuated<PathSegment, T>,
) -> Result<(), Error> {
    write!(f, "{}", path.into_token_stream())
}

fn write_fn_generics(f: &mut Formatter, fn_decl: &FnDecl) -> Result<(), Error> {
    fn_decl
        .generics
        .params
        .iter()
        .filter_map(get_generic_param_name)
        .map(|param| write!(f, "{},", param))
        .collect()
}

fn get_generic_param_name(param: &GenericParam) -> Option<String> {
    match *param {
        GenericParam::Type(ref type_param) => Some(type_param.ident.to_string()),
        _ => None,
    }
}

fn write_extract_args<T>(f: &mut Formatter, fn_args: &Punctuated<FnArg, T>) -> Result<(), Error> {
    if fn_args.is_empty() {
        return write!(f, "()");
    }
    write!(f, "unsafe {{ (")?;
    for fn_arg_name in iter_fn_arg_names(fn_args) {
        write!(
            f,
            "{}::mem::transmute_copy(&{}), ",
            STD_CRATE_NAME, fn_arg_name
        )?;
    }
    write!(f, ") }}")
}

fn write_restore_args<T>(f: &mut Formatter, fn_args: &Punctuated<FnArg, T>) -> Result<(), Error> {
    if fn_args.is_empty() {
        return writeln!(f, "()");
    }
    writeln!(f, "unsafe {{")?;
    for (fn_arg_index, fn_arg_name) in iter_fn_arg_names(fn_args).enumerate() {
        writeln!(
            f,
            "{}::mem::forget({}::mem::replace({}::mocking_utils::as_mut(&{}), {}.{}));",
            STD_CRATE_NAME,
            STD_CRATE_NAME,
            MOCKTOPUS_CRATE_NAME,
            fn_arg_name,
            ARGS_TO_CONTINUE_NAME,
            fn_arg_index
        )?;
    }
    writeln!(f, "}}")
}

fn write_forget_args<T>(f: &mut Formatter, fn_args: &Punctuated<FnArg, T>) -> Result<(), Error> {
    for fn_arg_name in iter_fn_arg_names(fn_args) {
        writeln!(f, "{}::mem::forget({});", STD_CRATE_NAME, fn_arg_name)?;
    }
    Ok(())
}

fn iter_fn_arg_names<'a, T>(
    input_args: &'a Punctuated<FnArg, T>,
) -> impl Iterator<Item = String> + 'a {
    input_args.iter().map(|fn_arg| match *fn_arg {
        FnArg::SelfRef(_) | FnArg::SelfValue(_) => "self".to_string(),
        FnArg::Captured(ArgCaptured {
            pat:
                Pat::Ident(PatIdent {
                    ref ident,
                    subpat: None,
                    ..
                }),
            ..
        }) => ident.to_string(),
        _ => panic!(
            "{}: '{}'",
            error_msg!("invalid fn arg type"),
            fn_arg.clone().into_token_stream()
        ),
    })
}
<|endoftext|>


<commit_before>use std::str;
use std::slice;
use std::borrow::Cow;
use std::cell::RefCell;

use types::{Token, idx_from_token, sourcemap_from_token};
use utils::{get_javascript_token, is_valid_javascript_identifier};

/// An iterator that iterates over tokens in reverse.
pub struct RevTokenIter<'view, 'viewbase, 'map>
    where 'viewbase: 'view
{
    sv: &'view SourceView<'viewbase>,
    token: Option<Token<'map>>,
    source_line: Option<(&'view str, usize, usize, usize)>,
}

impl<'view, 'viewbase, 'map> Iterator for RevTokenIter<'view, 'viewbase, 'map>
    where 'viewbase: 'view
{
    type Item = (Token<'map>, Option<&'view str>);

    fn next(&mut self) -> Option<(Token<'map>, Option<&'view str>)> {
        let token = match self.token.take() {
            None => { return None; }
            Some(token) => token
        };

        let idx = idx_from_token(&token);
        if idx > 0 {
            let sm = sourcemap_from_token(&token);
            self.token = sm.get_token(idx - 1);
        }

        // if we are going to the same line as we did last iteration, we don't have to scan
        // up to it again.  For normal sourcemaps this should mean we only ever go to the
        // line once.
        let (source_line, last_char_offset, last_byte_offset) = if_chain! {
            if let Some((source_line, dst_line, last_char_offset,
                         last_byte_offset)) = self.source_line;

            if dst_line == token.get_dst_line() as usize;
            then {
                (source_line, last_char_offset, last_byte_offset)
            } else {
                if let Some(source_line) = self.sv.get_line(token.get_dst_line()) {
                    (source_line, !0, !0)
                } else {
                    // if we can't find the line, return am empty one
                    ("", !0, !0)
                }
            }
        };

        // find the byte offset where our token starts
        let byte_offset = if last_byte_offset == !0 {
            let mut off = 0;
            let mut idx = 0;
            for c in source_line.chars() {
                if idx >= token.get_dst_col() as usize {
                    break;
                }
                off += c.len_utf8();
                idx += c.len_utf16();
            }
            off
        } else {
            let chars_to_move = last_char_offset - token.get_dst_col() as usize;
            let mut new_offset = last_byte_offset;
            let mut idx = 0;
            for c in source_line.get(..last_byte_offset).unwrap_or("").chars().rev() {
                if idx >= chars_to_move {
                    break;
                }
                new_offset -= c.len_utf8();
                idx += c.len_utf16();
            }
            new_offset
        };

        // remember where we were
        self.source_line = Some((
            source_line,
            token.get_dst_line() as usize,
            token.get_dst_col() as usize,
            byte_offset,
        ));

        // in case we run out of bounds here we reset the cache
        if byte_offset >= source_line.len() {
            self.source_line = None;
            Some((token, None))
        } else {
            Some((
                token,
                source_line.get(byte_offset..)
                    .and_then(|s| get_javascript_token(s))
            ))
        }
    }
}

pub struct Lines<'a> {
    sv: &'a SourceView<'a>,
    idx: u32,
}

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

    fn next(&mut self) -> Option<&'a str> {
        if let Some(line) = self.sv.get_line(self.idx) {
            self.idx += 1;
            Some(line)
        } else {
            None
        }
    }
}

/// Provides efficient access to minified sources.
///
/// This type is used to implement farily efficient source mapping
/// operations.
pub struct SourceView<'a> {
    source: Cow<'a, str>,
    processed_until: RefCell<usize>,
    lines: RefCell<Vec<(*const u8, usize)>>,
}

impl<'a> Clone for SourceView<'a> {
    fn clone(&self) -> SourceView<'a> {
        SourceView {
            source: self.source.clone(),
            processed_until: RefCell::new(0),
            lines: RefCell::new(vec![]),
        }
    }
}

unsafe fn make_str<'a>(tup: (*const u8, usize)) -> &'a str {
    let (data, len) = tup;
    str::from_utf8_unchecked(slice::from_raw_parts(data, len))
}

impl<'a> SourceView<'a> {
    /// Creates an optimized view of a given source.
    pub fn new(source: &'a str) -> SourceView<'a> {
        SourceView {
            source: Cow::Borrowed(source),
            processed_until: RefCell::new(0),
            lines: RefCell::new(vec![]),
        }
    }

    /// Creates an optimized view from a given source string
    pub fn from_string(source: String) -> SourceView<'static> {
        SourceView {
            source: Cow::Owned(source),
            processed_until: RefCell::new(0),
            lines: RefCell::new(vec![]),
        }
    }

    /// Returns a requested minified line.
    pub fn get_line(&self, idx: u32) -> Option<&str> {
        let idx = idx as usize;
        {
            let lines = self.lines.borrow();
            if idx < lines.len() {
                return Some(unsafe { make_str(lines[idx]) });
            }
        }

        // fetched everything
        if *self.processed_until.borrow() >= self.source.len() {
            return None;
        }

        let mut processed_until = self.processed_until.borrow_mut();
        let mut lines = self.lines.borrow_mut();

        loop {
            let rest = &self.source.as_bytes()[*processed_until..];
            if rest.is_empty() {
                break;
            }

            let rv = if let Some(mut idx) = rest.iter().position(|&x| x == b'\n' || x == b'\r') {
                let rv = &rest[..idx];
                if rest[idx] == b'\r' && rest.get(idx + 1) == Some(&b'\n') {
                    idx += 1;
                }
                *processed_until += idx + 1;
                rv
            } else {
                *processed_until += rest.len();
                rest
            };
            lines.push((rv.as_ptr(), rv.len()));
            if let Some(&line) = lines.get(idx) {
                return Some(unsafe { make_str(line) });
            }
        }

        None
    }

    /// Returns a line slice.
    ///
    /// Note that columns are indexed as JavaScript WTF-16 columns.
    pub fn get_line_slice(&self, line: u32, col: u32, span: u32) -> Option<&str> {
        self.get_line(line).and_then(|line| {
            let mut off = 0;
            let mut idx = 0;
            let mut char_iter = line.chars().peekable();

            while let Some(&c) = char_iter.peek() {
                if idx >= col as usize {
                    break;
                }
                char_iter.next();
                off += c.len_utf8();
                idx += c.len_utf16();
            }

            let mut off_end = off;
            while let Some(c) = char_iter.next() {
                if idx >= (col + span) as usize {
                    break;
                }
                off_end += c.len_utf8();
                idx += c.len_utf16();
            }

            if idx < ((col + span) as usize) {
                None
            } else {
                line.get(off..off_end)
            }
        })
    }

    /// Returns an iterator over all lines.
    pub fn lines(&'a self) -> Lines<'a> {
        Lines {
            sv: self,
            idx: 0,
        }
    }

    /// Returns the source.
    pub fn source(&self) -> &str {
        &self.source
    }

    fn rev_token_iter<'this, 'map>(&'this self, token: Token<'map>)
        -> RevTokenIter<'this, 'a, 'map>
    {
        RevTokenIter {
            sv: self,
            token: Some(token),
            source_line: None,
        }
    }

    /// Given a token and minified function name this attemps to resolve the
    /// name to an original function name.
    ///
    /// This invokes some guesswork and requires access to the original minified
    /// source.  This will not yield proper results for anonymous functions or
    /// functions that do not have clear function names.  (For instance it's
    /// recommended that dotted function names are not passed to this
    /// function).
    pub fn get_original_function_name<'map>(&self, token: Token<'map>, minified_name: &str)
        -> Option<&'map str>
    {
        if !is_valid_javascript_identifier(minified_name) {
            return None;
        }

        let mut iter = self.rev_token_iter(token).take(128).peekable();

        while let Some((token, original_identifier)) = iter.next() {
            if_chain! {
                if original_identifier == Some(minified_name);
                if let Some(item) = iter.peek();
                if item.1 == Some("function");
                then {
                    return token.get_name();
                }
            }
        }

        None
    }

    /// Returns the number of lines.
    pub fn line_count(&self) -> usize {
        self.get_line(!0);
        self.lines.borrow().len()
    }
}

#[test]
fn test_minified_source_view() {
    let view = SourceView::new("a\nb\nc");
    assert_eq!(view.get_line(0), Some("a"));
    assert_eq!(view.get_line(0), Some("a"));
    assert_eq!(view.get_line(2), Some("c"));
    assert_eq!(view.get_line(1), Some("b"));
    assert_eq!(view.get_line(3), None);

    assert_eq!(view.line_count(), 3);

    let view = SourceView::new("a\r\nb\r\nc");
    assert_eq!(view.get_line(0), Some("a"));
    assert_eq!(view.get_line(0), Some("a"));
    assert_eq!(view.get_line(2), Some("c"));
    assert_eq!(view.get_line(1), Some("b"));
    assert_eq!(view.get_line(3), None);

    assert_eq!(view.line_count(), 3);

    let view = SourceView::new("abc👌def\nblah");
    assert_eq!(view.get_line_slice(0, 0, 3), Some("abc"));
    assert_eq!(view.get_line_slice(0, 3, 1), Some("👌"));
    assert_eq!(view.get_line_slice(0, 3, 2), Some("👌"));
    assert_eq!(view.get_line_slice(0, 3, 3), Some("👌d"));
    assert_eq!(view.get_line_slice(0, 0, 4), Some("abc👌"));
    assert_eq!(view.get_line_slice(0, 0, 5), Some("abc👌"));
    assert_eq!(view.get_line_slice(0, 0, 6), Some("abc👌d"));
    assert_eq!(view.get_line_slice(1, 0, 4), Some("blah"));
    assert_eq!(view.get_line_slice(1, 0, 5), None);
    assert_eq!(view.get_line_slice(1, 0, 12), None);
}
<commit_msg>fix: Fixed an off-by-one error in source views<commit_after>use std::str;
use std::slice;
use std::borrow::Cow;
use std::cell::RefCell;

use types::{Token, idx_from_token, sourcemap_from_token};
use utils::{get_javascript_token, is_valid_javascript_identifier};

/// An iterator that iterates over tokens in reverse.
pub struct RevTokenIter<'view, 'viewbase, 'map>
    where 'viewbase: 'view
{
    sv: &'view SourceView<'viewbase>,
    token: Option<Token<'map>>,
    source_line: Option<(&'view str, usize, usize, usize)>,
}

impl<'view, 'viewbase, 'map> Iterator for RevTokenIter<'view, 'viewbase, 'map>
    where 'viewbase: 'view
{
    type Item = (Token<'map>, Option<&'view str>);

    fn next(&mut self) -> Option<(Token<'map>, Option<&'view str>)> {
        let token = match self.token.take() {
            None => { return None; }
            Some(token) => token
        };

        let idx = idx_from_token(&token);
        if idx > 0 {
            let sm = sourcemap_from_token(&token);
            self.token = sm.get_token(idx - 1);
        }

        // if we are going to the same line as we did last iteration, we don't have to scan
        // up to it again.  For normal sourcemaps this should mean we only ever go to the
        // line once.
        let (source_line, last_char_offset, last_byte_offset) = if_chain! {
            if let Some((source_line, dst_line, last_char_offset,
                         last_byte_offset)) = self.source_line;

            if dst_line == token.get_dst_line() as usize;
            then {
                (source_line, last_char_offset, last_byte_offset)
            } else {
                if let Some(source_line) = self.sv.get_line(token.get_dst_line()) {
                    (source_line, !0, !0)
                } else {
                    // if we can't find the line, return am empty one
                    ("", !0, !0)
                }
            }
        };

        // find the byte offset where our token starts
        let byte_offset = if last_byte_offset == !0 {
            let mut off = 0;
            let mut idx = 0;
            for c in source_line.chars() {
                if idx >= token.get_dst_col() as usize {
                    break;
                }
                off += c.len_utf8();
                idx += c.len_utf16();
            }
            off
        } else {
            let chars_to_move = last_char_offset - token.get_dst_col() as usize;
            let mut new_offset = last_byte_offset;
            let mut idx = 0;
            for c in source_line.get(..last_byte_offset).unwrap_or("").chars().rev() {
                if idx >= chars_to_move {
                    break;
                }
                new_offset -= c.len_utf8();
                idx += c.len_utf16();
            }
            new_offset
        };

        // remember where we were
        self.source_line = Some((
            source_line,
            token.get_dst_line() as usize,
            token.get_dst_col() as usize,
            byte_offset,
        ));

        // in case we run out of bounds here we reset the cache
        if byte_offset >= source_line.len() {
            self.source_line = None;
            Some((token, None))
        } else {
            Some((
                token,
                source_line.get(byte_offset..)
                    .and_then(|s| get_javascript_token(s))
            ))
        }
    }
}

pub struct Lines<'a> {
    sv: &'a SourceView<'a>,
    idx: u32,
}

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

    fn next(&mut self) -> Option<&'a str> {
        if let Some(line) = self.sv.get_line(self.idx) {
            self.idx += 1;
            Some(line)
        } else {
            None
        }
    }
}

/// Provides efficient access to minified sources.
///
/// This type is used to implement farily efficient source mapping
/// operations.
pub struct SourceView<'a> {
    source: Cow<'a, str>,
    processed_until: RefCell<usize>,
    lines: RefCell<Vec<(*const u8, usize)>>,
}

impl<'a> Clone for SourceView<'a> {
    fn clone(&self) -> SourceView<'a> {
        SourceView {
            source: self.source.clone(),
            processed_until: RefCell::new(0),
            lines: RefCell::new(vec![]),
        }
    }
}

unsafe fn make_str<'a>(tup: (*const u8, usize)) -> &'a str {
    let (data, len) = tup;
    str::from_utf8_unchecked(slice::from_raw_parts(data, len))
}

impl<'a> SourceView<'a> {
    /// Creates an optimized view of a given source.
    pub fn new(source: &'a str) -> SourceView<'a> {
        SourceView {
            source: Cow::Borrowed(source),
            processed_until: RefCell::new(0),
            lines: RefCell::new(vec![]),
        }
    }

    /// Creates an optimized view from a given source string
    pub fn from_string(source: String) -> SourceView<'static> {
        SourceView {
            source: Cow::Owned(source),
            processed_until: RefCell::new(0),
            lines: RefCell::new(vec![]),
        }
    }

    /// Returns a requested minified line.
    pub fn get_line(&self, idx: u32) -> Option<&str> {
        let idx = idx as usize;
        {
            let lines = self.lines.borrow();
            if idx < lines.len() {
                return Some(unsafe { make_str(lines[idx]) });
            }
        }

        // fetched everything
        if *self.processed_until.borrow() > self.source.len() {
            return None;
        }

        let mut processed_until = self.processed_until.borrow_mut();
        let mut lines = self.lines.borrow_mut();
        let mut done = false;

        while !done {
            let rest = &self.source.as_bytes()[*processed_until..];

            let rv = if let Some(mut idx) = rest.iter().position(|&x| x == b'\n' || x == b'\r') {
                let rv = &rest[..idx];
                if rest[idx] == b'\r' && rest.get(idx + 1) == Some(&b'\n') {
                    idx += 1;
                }
                *processed_until += idx + 1;
                rv
            } else {
                *processed_until += rest.len() + 1;
                done = true;
                rest
            };
            lines.push((rv.as_ptr(), rv.len()));
            if let Some(&line) = lines.get(idx) {
                return Some(unsafe { make_str(line) });
            }
        }

        None
    }

    /// Returns a line slice.
    ///
    /// Note that columns are indexed as JavaScript WTF-16 columns.
    pub fn get_line_slice(&self, line: u32, col: u32, span: u32) -> Option<&str> {
        self.get_line(line).and_then(|line| {
            let mut off = 0;
            let mut idx = 0;
            let mut char_iter = line.chars().peekable();

            while let Some(&c) = char_iter.peek() {
                if idx >= col as usize {
                    break;
                }
                char_iter.next();
                off += c.len_utf8();
                idx += c.len_utf16();
            }

            let mut off_end = off;
            while let Some(c) = char_iter.next() {
                if idx >= (col + span) as usize {
                    break;
                }
                off_end += c.len_utf8();
                idx += c.len_utf16();
            }

            if idx < ((col + span) as usize) {
                None
            } else {
                line.get(off..off_end)
            }
        })
    }

    /// Returns an iterator over all lines.
    pub fn lines(&'a self) -> Lines<'a> {
        Lines {
            sv: self,
            idx: 0,
        }
    }

    /// Returns the source.
    pub fn source(&self) -> &str {
        &self.source
    }

    fn rev_token_iter<'this, 'map>(&'this self, token: Token<'map>)
        -> RevTokenIter<'this, 'a, 'map>
    {
        RevTokenIter {
            sv: self,
            token: Some(token),
            source_line: None,
        }
    }

    /// Given a token and minified function name this attemps to resolve the
    /// name to an original function name.
    ///
    /// This invokes some guesswork and requires access to the original minified
    /// source.  This will not yield proper results for anonymous functions or
    /// functions that do not have clear function names.  (For instance it's
    /// recommended that dotted function names are not passed to this
    /// function).
    pub fn get_original_function_name<'map>(&self, token: Token<'map>, minified_name: &str)
        -> Option<&'map str>
    {
        if !is_valid_javascript_identifier(minified_name) {
            return None;
        }

        let mut iter = self.rev_token_iter(token).take(128).peekable();

        while let Some((token, original_identifier)) = iter.next() {
            if_chain! {
                if original_identifier == Some(minified_name);
                if let Some(item) = iter.peek();
                if item.1 == Some("function");
                then {
                    return token.get_name();
                }
            }
        }

        None
    }

    /// Returns the number of lines.
    pub fn line_count(&self) -> usize {
        self.get_line(!0);
        self.lines.borrow().len()
    }
}

#[test]
fn test_minified_source_view() {
    let view = SourceView::new("a\nb\nc");
    assert_eq!(view.get_line(0), Some("a"));
    assert_eq!(view.get_line(0), Some("a"));
    assert_eq!(view.get_line(2), Some("c"));
    assert_eq!(view.get_line(1), Some("b"));
    assert_eq!(view.get_line(3), None);

    assert_eq!(view.line_count(), 3);

    let view = SourceView::new("a\r\nb\r\nc");
    assert_eq!(view.get_line(0), Some("a"));
    assert_eq!(view.get_line(0), Some("a"));
    assert_eq!(view.get_line(2), Some("c"));
    assert_eq!(view.get_line(1), Some("b"));
    assert_eq!(view.get_line(3), None);

    assert_eq!(view.line_count(), 3);

    let view = SourceView::new("abc👌def\nblah");
    assert_eq!(view.get_line_slice(0, 0, 3), Some("abc"));
    assert_eq!(view.get_line_slice(0, 3, 1), Some("👌"));
    assert_eq!(view.get_line_slice(0, 3, 2), Some("👌"));
    assert_eq!(view.get_line_slice(0, 3, 3), Some("👌d"));
    assert_eq!(view.get_line_slice(0, 0, 4), Some("abc👌"));
    assert_eq!(view.get_line_slice(0, 0, 5), Some("abc👌"));
    assert_eq!(view.get_line_slice(0, 0, 6), Some("abc👌d"));
    assert_eq!(view.get_line_slice(1, 0, 4), Some("blah"));
    assert_eq!(view.get_line_slice(1, 0, 5), None);
    assert_eq!(view.get_line_slice(1, 0, 12), None);

    let view = SourceView::new("a\nb\nc\n");
    assert_eq!(view.get_line(0), Some("a"));
    assert_eq!(view.get_line(1), Some("b"));
    assert_eq!(view.get_line(2), Some("c"));
    assert_eq!(view.get_line(3), Some(""));
    assert_eq!(view.get_line(4), None);
}
<|endoftext|>


<commit_before>// Copyright (C) 2018 O.S. Systems Sofware LTDA
//
// SPDX-License-Identifier: Apache-2.0

#[macro_use]
mod macros;
pub(crate) mod actor;
mod download;
mod idle;
pub(crate) mod install;
mod park;
mod poll;
mod probe;
mod reboot;
mod transition;

use self::{
    download::Download,
    idle::Idle,
    install::Install,
    park::Park,
    poll::Poll,
    probe::{Probe, ServerAddress},
    reboot::Reboot,
};
use crate::{firmware::Metadata, http_api, runtime_settings::RuntimeSettings, settings::Settings};
use actix::{Actor, System};
use futures::future::Future;
use lazy_static::lazy_static;
use std::sync::{Arc, RwLock};

lazy_static! {
    static ref SHARED_STATE: Arc<RwLock<Option<SharedState>>> = Arc::new(RwLock::new(None));
}

trait StateChangeImpl {
    fn handle(self) -> Result<StateMachine, failure::Error>;
    fn name(&self) -> &'static str;

    fn handle_download_abort(&self) -> actor::download_abort::Response {
        actor::download_abort::Response::InvalidState
    }

    fn handle_trigger_probe(&self) -> actor::probe::Response {
        actor::probe::Response::InvalidState(self.name().to_owned())
    }
}

trait TransitionCallback: StateChangeImpl + Into<State<Idle>> {}

trait ProgressReporter: TransitionCallback {
    fn package_uid(&self) -> String;
    fn report_enter_state_name(&self) -> &'static str;
    fn report_leave_state_name(&self) -> &'static str;
}

#[derive(Debug, PartialEq)]
struct State<S>(S)
where
    State<S>: StateChangeImpl;

#[derive(Debug, PartialEq)]
struct SharedState {
    settings: Settings,
    runtime_settings: RuntimeSettings,
    firmware: Metadata,
}

#[derive(Debug, PartialEq)]
enum StateMachine {
    Park(State<Park>),
    Idle(State<Idle>),
    Poll(State<Poll>),
    Probe(State<Probe>),
    Download(State<Download>),
    Install(State<Install>),
    Reboot(State<Reboot>),
}

impl<S> State<S>
where
    State<S>: TransitionCallback + ProgressReporter,
{
    fn handle_with_callback_and_report_progress(self) -> Result<StateMachine, failure::Error> {
        use transition::{state_change_callback, Transition};

        let transition = state_change_callback(
            &(shared_state_mut!().settings.firmware.metadata_path.clone()),
            self.name(),
        )?;

        match transition {
            Transition::Continue => Ok(self.handle_and_report_progress()?),
            Transition::Cancel => Ok(StateMachine::Idle(self.into())),
        }
    }
}

impl<S> State<S>
where
    State<S>: ProgressReporter,
{
    fn handle_and_report_progress(self) -> Result<StateMachine, failure::Error> {
        let server = &shared_state_mut!().settings.network.server_address.clone();
        let firmware = &shared_state_mut!().firmware.clone();
        let package_uid = self.package_uid().clone();
        let enter_state = self.report_enter_state_name();
        let leave_state = self.report_leave_state_name();

        let report = |state, previous_state, error_message, current_log| {
            crate::client::Api::new(&server).report(
                state,
                &firmware,
                &package_uid,
                previous_state,
                error_message,
                current_log,
            )
        };

        report(enter_state, None, None, None)?;
        self.handle()
            .and_then(|state| {
                report(leave_state, None, None, None)?;
                Ok(state)
            })
            .or_else(|e| {
                report(
                    "error",
                    Some(enter_state),
                    Some(e.to_string()),
                    Some(crate::logger::buffer().lock().unwrap().to_string()),
                )?;
                Err(e)
            })
    }
}

impl StateMachine {
    fn new() -> Self {
        StateMachine::Idle(State(Idle {}))
    }

    fn move_to_next_state(self) -> Result<Self, failure::Error> {
        match self {
            StateMachine::Park(s) => Ok(s.handle()?),
            StateMachine::Idle(s) => Ok(s.handle()?),
            StateMachine::Poll(s) => Ok(s.handle()?),
            StateMachine::Probe(s) => Ok(s.handle()?),
            StateMachine::Download(s) => Ok(s.handle_with_callback_and_report_progress()?),
            StateMachine::Install(s) => Ok(s.handle_with_callback_and_report_progress()?),
            StateMachine::Reboot(s) => Ok(s.handle_with_callback_and_report_progress()?),
        }
    }
}

/// Runs the state machine up to completion handling all procing
/// states without extra manual work.
///
/// It supports following states, and transitions, as shown in the
/// below diagram:
///
/// ```text
///           .--------------.
///           |              v
/// Park <- Idle -> Poll -> Probe -> Download -> Install -> Reboot
///           ^      ^        '          '          '
///           '      '        '          '          '
///           '      `--------'          '          '
///           `---------------'          '          '
///           `--------------------------'          '
///           `-------------------------------------'
/// ```
///
/// # Example
/// ```
/// # extern crate failure;
/// # extern crate updatehub;
/// # use failure;
/// # fn run() -> Result<(), failure::Error> {
/// use updatehub;
///
/// updatehub::logger::init(0);
/// let settings = updatehub::Settings::load()?;
/// updatehub::run(settings)?;
/// # Ok(())
/// # }
/// ```
pub fn run(settings: Settings) -> Result<(), failure::Error> {
    let mut runtime_settings = RuntimeSettings::new().load(&settings.storage.runtime_settings)?;
    if !settings.storage.read_only {
        runtime_settings.enable_persistency();
    }

    let firmware = Metadata::from_path(&settings.firmware.metadata_path)?;
    set_shared_state!(settings, runtime_settings, firmware);

    let agent_machine = actor::Machine::new(StateMachine::new());

    System::run(|| {
        let addr = agent_machine.start();
        let addr_clone = addr.clone();
        actix_web::HttpServer::new(move || {
            actix_web::App::new().configure(|cfg| http_api::API::configure(cfg, addr_clone.clone()))
        })
        .bind("localhost:8080")
        .unwrap()
        .start();

        // Iterate over the state machine.
        loop {
            addr.send(actor::Step)
                .wait()
                .expect("Failed to communicate with actor");
        }
    })?;

    unreachable!("actix System has stopped");
}
<commit_msg>states: Rework local variables for run method<commit_after>// Copyright (C) 2018 O.S. Systems Sofware LTDA
//
// SPDX-License-Identifier: Apache-2.0

#[macro_use]
mod macros;
pub(crate) mod actor;
mod download;
mod idle;
pub(crate) mod install;
mod park;
mod poll;
mod probe;
mod reboot;
mod transition;

use self::{
    download::Download,
    idle::Idle,
    install::Install,
    park::Park,
    poll::Poll,
    probe::{Probe, ServerAddress},
    reboot::Reboot,
};
use crate::{firmware::Metadata, http_api, runtime_settings::RuntimeSettings, settings::Settings};
use actix::{Actor, System};
use futures::future::Future;
use lazy_static::lazy_static;
use std::sync::{Arc, RwLock};

lazy_static! {
    static ref SHARED_STATE: Arc<RwLock<Option<SharedState>>> = Arc::new(RwLock::new(None));
}

trait StateChangeImpl {
    fn handle(self) -> Result<StateMachine, failure::Error>;
    fn name(&self) -> &'static str;

    fn handle_download_abort(&self) -> actor::download_abort::Response {
        actor::download_abort::Response::InvalidState
    }

    fn handle_trigger_probe(&self) -> actor::probe::Response {
        actor::probe::Response::InvalidState(self.name().to_owned())
    }
}

trait TransitionCallback: StateChangeImpl + Into<State<Idle>> {}

trait ProgressReporter: TransitionCallback {
    fn package_uid(&self) -> String;
    fn report_enter_state_name(&self) -> &'static str;
    fn report_leave_state_name(&self) -> &'static str;
}

#[derive(Debug, PartialEq)]
struct State<S>(S)
where
    State<S>: StateChangeImpl;

#[derive(Debug, PartialEq)]
struct SharedState {
    settings: Settings,
    runtime_settings: RuntimeSettings,
    firmware: Metadata,
}

#[derive(Debug, PartialEq)]
enum StateMachine {
    Park(State<Park>),
    Idle(State<Idle>),
    Poll(State<Poll>),
    Probe(State<Probe>),
    Download(State<Download>),
    Install(State<Install>),
    Reboot(State<Reboot>),
}

impl<S> State<S>
where
    State<S>: TransitionCallback + ProgressReporter,
{
    fn handle_with_callback_and_report_progress(self) -> Result<StateMachine, failure::Error> {
        use transition::{state_change_callback, Transition};

        let transition = state_change_callback(
            &(shared_state_mut!().settings.firmware.metadata_path.clone()),
            self.name(),
        )?;

        match transition {
            Transition::Continue => Ok(self.handle_and_report_progress()?),
            Transition::Cancel => Ok(StateMachine::Idle(self.into())),
        }
    }
}

impl<S> State<S>
where
    State<S>: ProgressReporter,
{
    fn handle_and_report_progress(self) -> Result<StateMachine, failure::Error> {
        let server = &shared_state_mut!().settings.network.server_address.clone();
        let firmware = &shared_state_mut!().firmware.clone();
        let package_uid = self.package_uid().clone();
        let enter_state = self.report_enter_state_name();
        let leave_state = self.report_leave_state_name();

        let report = |state, previous_state, error_message, current_log| {
            crate::client::Api::new(&server).report(
                state,
                &firmware,
                &package_uid,
                previous_state,
                error_message,
                current_log,
            )
        };

        report(enter_state, None, None, None)?;
        self.handle()
            .and_then(|state| {
                report(leave_state, None, None, None)?;
                Ok(state)
            })
            .or_else(|e| {
                report(
                    "error",
                    Some(enter_state),
                    Some(e.to_string()),
                    Some(crate::logger::buffer().lock().unwrap().to_string()),
                )?;
                Err(e)
            })
    }
}

impl StateMachine {
    fn new() -> Self {
        StateMachine::Idle(State(Idle {}))
    }

    fn move_to_next_state(self) -> Result<Self, failure::Error> {
        match self {
            StateMachine::Park(s) => Ok(s.handle()?),
            StateMachine::Idle(s) => Ok(s.handle()?),
            StateMachine::Poll(s) => Ok(s.handle()?),
            StateMachine::Probe(s) => Ok(s.handle()?),
            StateMachine::Download(s) => Ok(s.handle_with_callback_and_report_progress()?),
            StateMachine::Install(s) => Ok(s.handle_with_callback_and_report_progress()?),
            StateMachine::Reboot(s) => Ok(s.handle_with_callback_and_report_progress()?),
        }
    }
}

/// Runs the state machine up to completion handling all procing
/// states without extra manual work.
///
/// It supports following states, and transitions, as shown in the
/// below diagram:
///
/// ```text
///           .--------------.
///           |              v
/// Park <- Idle -> Poll -> Probe -> Download -> Install -> Reboot
///           ^      ^        '          '          '
///           '      '        '          '          '
///           '      `--------'          '          '
///           `---------------'          '          '
///           `--------------------------'          '
///           `-------------------------------------'
/// ```
///
/// # Example
/// ```
/// # extern crate failure;
/// # extern crate updatehub;
/// # use failure;
/// # fn run() -> Result<(), failure::Error> {
/// use updatehub;
///
/// updatehub::logger::init(0);
/// let settings = updatehub::Settings::load()?;
/// updatehub::run(settings)?;
/// # Ok(())
/// # }
/// ```
pub fn run(settings: Settings) -> Result<(), failure::Error> {
    let mut runtime_settings = RuntimeSettings::new().load(&settings.storage.runtime_settings)?;
    if !settings.storage.read_only {
        runtime_settings.enable_persistency();
    }

    let firmware = Metadata::from_path(&settings.firmware.metadata_path)?;
    set_shared_state!(settings, runtime_settings, firmware);

    let agent_machine = actor::Machine::new(StateMachine::new());

    System::run(|| {
        let machine = agent_machine.start();
        let api = machine.clone();
        actix_web::HttpServer::new(move || {
            actix_web::App::new().configure(|cfg| http_api::API::configure(cfg, api.clone()))
        })
        .bind("localhost:8080")
        .unwrap()
        .start();

        // Iterate over the state machine.
        loop {
            machine
                .send(actor::Step)
                .wait()
                .expect("Failed to communicate with actor");
        }
    })?;

    unreachable!("actix System has stopped");
}
<|endoftext|>


<commit_before><commit_msg>make broadcasting continuous<commit_after><|endoftext|>


<commit_before>pub mod ffi;
pub mod frame;

use std::mem;
use std::ptr;
use std::ffi::CString;

use error::{RuntimeError, ErrorKind, VoidResult};

use super::utils::fn_ptr::FnPtrLoader;

pub const GLES2: bool = cfg!(any(all(target_arch="arm", not(feature="gl")), feature="gles2"));

#[inline]
pub fn load<T: FnPtrLoader>(loader: &T) {
   unsafe {
      ffi::load_functions(loader)
   };
}

#[inline]
fn gl_error<T>(function: &str, message: &str) -> Result<T, RuntimeError> {
   Err(
      RuntimeError::new(
         ErrorKind::GL,
         format!("{}: {}", function, message)
      )
   )
}

#[inline]
fn gl_result<T>(function: &str, value: T) -> Result<T, RuntimeError> {
   let result = unsafe {
      ffi::glGetError()
   };

   match result {
      ffi::GL_NO_ERROR => Ok(value),
      ffi::GL_INVALID_ENUM => gl_error(function,
         "An unacceptable value is specified for an enumerated argument"
      ),
      ffi::GL_INVALID_VALUE => gl_error(function,
         "A numeric argument is out of range"
      ),
      ffi::GL_INVALID_OPERATION => gl_error(function,
         "The specified operation is not allowed in the current state"
      ),
      ffi::GL_INVALID_FRAMEBUFFER_OPERATION => gl_error(function,
         "The framebuffer object is not complete"
      ),
      ffi::GL_OUT_OF_MEMORY => gl_error(function,
         "There is not enough memory left to execute the command"
      ),
      ffi::GL_STACK_UNDERFLOW => gl_error(function,
         "Performing an operation that would cause an internal stack to underflow"
      ),
      ffi::GL_STACK_OVERFLOW => gl_error(function,
         "Performing an operation that would cause an internal stack to overflow"
      ),
      _ => gl_error(function, "Unknown OpenGL error")
   }
}

#[inline]
pub fn reset_pixelstore_alignment() -> VoidResult {
   unsafe {
      ffi::glPixelStorei(ffi::GL_UNPACK_ALIGNMENT, 1);
   }

   gl_result("glPixelStorei(GL_UNPACK_ALIGNMENT)", ())
}

#[inline]
pub fn enable_framebuffer_srgb() -> VoidResult {
   unsafe {
      ffi::glEnable(ffi::GL_FRAMEBUFFER_SRGB);
   }

   gl_result("glEnable(GL_FRAMEBUFFER_SRGB)", ())
}

#[inline]
pub fn viewport(width: u32, height: u32) -> VoidResult {
   unsafe {
      ffi::glViewport(0, 0, width as ffi::GLsizei, height as ffi::GLsizei)
   }

   gl_result("glViewport", ())
}

#[inline]
pub fn vertex_attrib_pointer(
   index: ffi::GLuint, size: ffi::GLint, buffer: &[ffi::GLfloat]
) -> VoidResult {

   unsafe {
      ffi::glVertexAttribPointer(
         index,
         size,
         ffi::GL_FLOAT,
         ffi::GL_FALSE,
         0,
         buffer.as_ptr() as *const ffi::GLvoid
      )
   }

   gl_result("glVertexAttribPointer", ())
}

#[inline]
pub fn enable_vertex_attrib_array(index: ffi::GLuint) -> VoidResult {
   unsafe {
      ffi::glEnableVertexAttribArray(index)
   }

   gl_result("glEnableVertexAttribArray", ())
}

#[inline]
pub fn uniform_value_1i(location: ffi::GLint, value: ffi::GLint) -> VoidResult {
   unsafe {
      ffi::glUniform1i(location, value)
   }

   gl_result("glUniform1i", ())
}

#[inline]
pub fn draw_arrays(count: ffi::GLsizei) -> VoidResult {
   unsafe {
      ffi::glDrawArrays(ffi::GL_TRIANGLE_STRIP, 0, count)
   }

   gl_result("glDrawArrays", ())
}

pub struct Texture {
   pub name: ffi::GLuint,
}

impl Texture {
   pub fn new(width: u32, height: u32) -> Result<Self, RuntimeError> {
      let mut name: ffi::GLuint = unsafe { mem::uninitialized() };

      unsafe {
         ffi::glGenTextures(1, &mut name);

         ffi::glBindTexture(ffi::GL_TEXTURE_2D, name);

         ffi::glTexParameteri(
            ffi::GL_TEXTURE_2D,
            ffi::GL_TEXTURE_WRAP_S,
            ffi::GL_CLAMP_TO_EDGE as ffi::GLint
         );

         ffi::glTexParameteri(
            ffi::GL_TEXTURE_2D,
            ffi::GL_TEXTURE_WRAP_T,
            ffi::GL_CLAMP_TO_EDGE as ffi::GLint
         );

         ffi::glTexParameteri(
            ffi::GL_TEXTURE_2D,
            ffi::GL_TEXTURE_MIN_FILTER,
            ffi::GL_NEAREST as ffi::GLint
         );

         ffi::glTexParameteri(
            ffi::GL_TEXTURE_2D,
            ffi::GL_TEXTURE_MAG_FILTER,
            ffi::GL_NEAREST as ffi::GLint
         );

         ffi::glTexImage2D(
            ffi::GL_TEXTURE_2D,
            0,
            ffi::GL_RGBA8 as ffi::GLint,
            width as ffi::GLsizei,
            height as ffi::GLsizei,
            0,
            ffi::GL_RGBA,
            ffi::GL_UNSIGNED_BYTE,
            ptr::null()
         );
      }

      gl_result("glGenTextures", Texture {
         name: name,
      })
   }

   #[inline]
   pub fn bind(&self) -> VoidResult {
      unsafe {
         ffi::glBindTexture(ffi::GL_TEXTURE_2D, self.name);
      }

      gl_result("glBindTexture", ())
   }

   #[inline]
   pub fn resize(&self, width: u32, height: u32) -> VoidResult {
      unsafe {
         ffi::glTexImage2D(
            ffi::GL_TEXTURE_2D,
            0,
            ffi::GL_RGBA8 as ffi::GLint,
            width as ffi::GLsizei,
            height as ffi::GLsizei,
            0,
            ffi::GL_RGBA,
            ffi::GL_UNSIGNED_BYTE,
            ptr::null()
         );
      }

      gl_result("glTexImage2D", ())
   }

   #[inline]
   pub fn null_update(&self, width: u32, height: u32) -> VoidResult {
      unsafe {
         ffi::glTexSubImage2D(
            ffi::GL_TEXTURE_2D,
            0,
            0, 0, width as ffi::GLsizei, height as ffi::GLsizei,
            ffi::GL_RGBA,
            ffi::GL_UNSIGNED_BYTE,
            ptr::null()
         );
      }

      gl_result("glTexSubImage2D(NULL)", ())
   }

   #[inline]
   pub fn update(&self, width: u32, height: u32, data: &[u8]) -> VoidResult {
      unsafe {
         ffi::glTexSubImage2D(
            ffi::GL_TEXTURE_2D,
            0,
            0, 0, width as ffi::GLsizei, height as ffi::GLsizei,
            ffi::GL_RGBA,
            ffi::GL_UNSIGNED_BYTE,
            data.as_ptr() as *const ffi::GLvoid
         );
      }

      gl_result("glTexSubImage2D", ())
   }
}

impl Drop for Texture {
   fn drop (&mut self) {
      unsafe {
         ffi::glDeleteTextures(1, &self.name)
      };
   }
}

pub struct Framebuffer {
   pub name: ffi::GLuint,
}

impl Framebuffer {
   pub fn new(texture: &Texture) -> Result<Self, RuntimeError> {
      let name = try!(Framebuffer::get_framebuffers());

      let framebuffer = Framebuffer {
         name: name,
      };

      try!(framebuffer.bind());
      try!(framebuffer.attach_texture(texture));
      try!(framebuffer.unbind());

      Ok(framebuffer)
   }

   #[inline]
   pub fn get_framebuffers() -> Result<ffi::GLuint, RuntimeError> {
      let mut name: ffi::GLuint = unsafe { mem::uninitialized() };

      unsafe {
         ffi::glGenFramebuffers(1, &mut name)
      };

      gl_result("glGenFramebuffers", name)
   }

   #[inline]
   pub fn bind(&self) -> VoidResult {
      unsafe {
         ffi::glBindFramebuffer(ffi::GL_READ_FRAMEBUFFER, self.name)
      };

      gl_result("glBindFramebuffer(name)", ())
   }

   #[inline]
   pub fn unbind(&self) -> VoidResult {
      unsafe {
         ffi::glBindFramebuffer(ffi::GL_READ_FRAMEBUFFER, 0)
      };

      gl_result("glBindFramebuffer(0)", ())
   }

   #[inline]
   pub fn attach_texture(&self, texture: &Texture) -> VoidResult {
      unsafe {
         ffi::glFramebufferTexture2D(
            ffi::GL_READ_FRAMEBUFFER,
            ffi::GL_COLOR_ATTACHMENT0,
            ffi::GL_TEXTURE_2D,
            texture.name,
            0
         )
      };

      gl_result("glFramebufferTexture2D", ())
   }

   #[inline]
   pub fn blit(&self, width: u32, height: u32) -> VoidResult {
      unsafe {
         ffi::glBlitFramebuffer(
            0, 0, width as ffi::GLint, height as ffi::GLint,
            0, 0, width as ffi::GLint, height as ffi::GLint,
            ffi::GL_COLOR_BUFFER_BIT,
            ffi::GL_NEAREST
         )
      };

      gl_result("glBlitFramebuffer", ())
   }
}

impl Drop for Framebuffer {
   fn drop (&mut self) {
      unsafe {
         ffi::glDeleteFramebuffers(1, &self.name)
      };
   }
}

pub struct Buffer {
   pub name: ffi::GLuint,
   pub ptr: *mut ffi::c_void,
   pub size: ffi::GLsizeiptr,
}

impl Buffer {
   pub fn new() -> Result<Self, RuntimeError> {
      let mut name: ffi::GLuint = unsafe { mem::uninitialized() };

      unsafe {
         ffi::glGenBuffers(1, &mut name)
      };

      gl_result("glGenBuffers", Buffer {
         name: name,
         ptr: ptr::null_mut(),
         size: 0,
      })
   }

   #[inline]
   pub fn bind(&self) -> VoidResult {
      unsafe {
         ffi::glBindBuffer(ffi::GL_PIXEL_UNPACK_BUFFER, self.name)
      };

      gl_result("glBindBuffer(name)", ())
   }

   #[inline]
   pub fn unbind(&self) -> VoidResult {
      unsafe {
         ffi::glBindBuffer(ffi::GL_PIXEL_UNPACK_BUFFER, 0)
      };

      gl_result("glBindBuffer(0)", ())
   }

   #[inline]
   pub fn init_data(&mut self, size: usize) -> VoidResult {
      self.size = size as ffi::GLsizeiptr;
      unsafe {
         ffi::glBufferData(
            ffi::GL_PIXEL_UNPACK_BUFFER,
            self.size,
            ptr::null(),
            ffi::GL_STREAM_DRAW
         )
      };

      gl_result("glBufferData", ())
   }

   #[inline]
   pub fn map(&mut self) -> VoidResult {
      self.ptr = unsafe {
         ffi::glMapBuffer(ffi::GL_PIXEL_UNPACK_BUFFER, ffi::GL_WRITE_ONLY)
      };

      gl_result("glMapBuffer", ())
   }

   #[inline]
   pub fn map_range(&mut self) -> VoidResult {
      self.ptr = unsafe {
         ffi::glMapBufferRange(
            ffi::GL_PIXEL_UNPACK_BUFFER, 0, self.size,
            ffi::GL_MAP_WRITE_BIT// | ffi::GL_MAP_UNSYNCHRONIZED_BIT
         )
      };

      gl_result("glMapBufferRange", ())
   }

   #[inline]
   pub fn unmap(&self) -> VoidResult {
      unsafe {
         ffi::glUnmapBuffer(ffi::GL_PIXEL_UNPACK_BUFFER)
      };

      gl_result("glUnmapBuffer", ())
   }
}

impl Drop for Buffer {
   fn drop (&mut self) {
      unsafe {
         ffi::glDeleteBuffers(1, &self.name)
      };
   }
}

pub struct Shader {
   pub name: ffi::GLuint,
}

impl Shader {
   pub fn new(shader_type: ffi::GLenum) -> Result<Self, RuntimeError> {
      let name = unsafe {
         ffi::glCreateShader(shader_type)
      };

      gl_result("glCreateShader", Shader {
         name: name,
      })
   }

   #[inline]
   pub fn shader_source(&self, string: &str) -> VoidResult {
      let cstring = try!(CString::new(string));

      unsafe {
         ffi::glShaderSource(self.name, 1, [cstring.as_ptr()].as_ptr(), ptr::null())
      };

      gl_result("glShaderSource", ())
   }

   #[inline]
   pub fn compile(&self) -> VoidResult {
      unsafe {
         ffi::glCompileShader(self.name)
      };

      if !try!(self.is_compiled()) {
         return gl_error("glCompileShader", "Shader not compiled")
      }

      Ok(())
   }

   #[inline]
   pub fn is_compiled(&self) -> Result<bool, RuntimeError> {
      let mut compiled: ffi::GLint = unsafe { mem::uninitialized() };

      unsafe {
         ffi::glGetShaderiv(self.name, ffi::GL_COMPILE_STATUS, &mut compiled);
      };

      gl_result("glGetShaderiv", compiled == ffi::GL_TRUE as ffi::GLint)
   }
}

impl Drop for Shader {
   fn drop (&mut self) {
      unsafe {
         ffi::glDeleteShader(self.name)
      };
   }
}

pub struct Program {
   pub name: ffi::GLuint,
}

impl Program {
   pub fn new() -> Result<Self, RuntimeError> {
      let name = unsafe {
         ffi::glCreateProgram()
      };

      gl_result("glCreateProgram", Program {
         name: name,
      })
   }

   #[inline]
   pub fn attach_shader(&self, shader: &Shader) -> VoidResult {
      unsafe {
         ffi::glAttachShader(self.name, shader.name)
      };

      gl_result("glAttachShader", ())
   }

   #[inline]
   pub fn link(&self) -> VoidResult {
      unsafe {
         ffi::glLinkProgram(self.name)
      };

      if !try!(self.is_linked()) {
         return gl_error("glLinkProgram", "Program not linked")
      }

      Ok(())
   }

   #[inline]
   pub fn use_program(&self) -> VoidResult {
      unsafe {
         ffi::glUseProgram(self.name)
      };

      gl_result("glUseProgram", ())
   }

   #[inline]
   pub fn is_linked(&self) -> Result<bool, RuntimeError> {
      let mut linked: ffi::GLint = unsafe { mem::uninitialized() };

      unsafe {
         ffi::glGetProgramiv(self.name, ffi::GL_LINK_STATUS, &mut linked);
      };

      gl_result("glGetProgramiv", linked == ffi::GL_TRUE as ffi::GLint)
   }

   #[inline]
   pub fn get_attrib_location(&self, attrib_name: &str) -> Result<ffi::GLint, RuntimeError> {
      let cname = try!(CString::new(attrib_name));

      let result = unsafe {
         ffi::glGetAttribLocation(self.name, cname.as_ptr())
      };

      gl_result("glGetAttribLocation", result)
   }

   #[inline]
   pub fn get_uniform_location(&self, variable_name: &str) -> Result<ffi::GLint, RuntimeError> {
      let cname = try!(CString::new(variable_name));

      let result = unsafe {
         ffi::glGetUniformLocation(self.name, cname.as_ptr())
      };

      gl_result("glGetUniformLocation", result)
   }
}

impl Drop for Program {
   fn drop (&mut self) {
      unsafe {
         ffi::glDeleteProgram(self.name)
      };
   }
}
<commit_msg>Granular GL error detection for texture creation<commit_after>pub mod ffi;
pub mod frame;

use std::mem;
use std::ptr;
use std::ffi::CString;

use error::{RuntimeError, ErrorKind, VoidResult};

use super::utils::fn_ptr::FnPtrLoader;

pub const GLES2: bool = cfg!(any(all(target_arch="arm", not(feature="gl")), feature="gles2"));

#[inline]
pub fn load<T: FnPtrLoader>(loader: &T) {
   unsafe {
      ffi::load_functions(loader)
   };
}

#[inline]
fn gl_error<T>(function: &str, message: &str) -> Result<T, RuntimeError> {
   Err(
      RuntimeError::new(
         ErrorKind::GL,
         format!("{}: {}", function, message)
      )
   )
}

#[inline]
fn gl_result<T>(function: &str, value: T) -> Result<T, RuntimeError> {
   let result = unsafe {
      ffi::glGetError()
   };

   match result {
      ffi::GL_NO_ERROR => Ok(value),
      ffi::GL_INVALID_ENUM => gl_error(function,
         "An unacceptable value is specified for an enumerated argument"
      ),
      ffi::GL_INVALID_VALUE => gl_error(function,
         "A numeric argument is out of range"
      ),
      ffi::GL_INVALID_OPERATION => gl_error(function,
         "The specified operation is not allowed in the current state"
      ),
      ffi::GL_INVALID_FRAMEBUFFER_OPERATION => gl_error(function,
         "The framebuffer object is not complete"
      ),
      ffi::GL_OUT_OF_MEMORY => gl_error(function,
         "There is not enough memory left to execute the command"
      ),
      ffi::GL_STACK_UNDERFLOW => gl_error(function,
         "Performing an operation that would cause an internal stack to underflow"
      ),
      ffi::GL_STACK_OVERFLOW => gl_error(function,
         "Performing an operation that would cause an internal stack to overflow"
      ),
      _ => gl_error(function, "Unknown OpenGL error")
   }
}

#[inline]
pub fn reset_pixelstore_alignment() -> VoidResult {
   unsafe {
      ffi::glPixelStorei(ffi::GL_UNPACK_ALIGNMENT, 1);
   }

   gl_result("glPixelStorei(GL_UNPACK_ALIGNMENT)", ())
}

#[inline]
pub fn enable_framebuffer_srgb() -> VoidResult {
   unsafe {
      ffi::glEnable(ffi::GL_FRAMEBUFFER_SRGB);
   }

   gl_result("glEnable(GL_FRAMEBUFFER_SRGB)", ())
}

#[inline]
pub fn viewport(width: u32, height: u32) -> VoidResult {
   unsafe {
      ffi::glViewport(0, 0, width as ffi::GLsizei, height as ffi::GLsizei)
   }

   gl_result("glViewport", ())
}

#[inline]
pub fn vertex_attrib_pointer(
   index: ffi::GLuint, size: ffi::GLint, buffer: &[ffi::GLfloat]
) -> VoidResult {

   unsafe {
      ffi::glVertexAttribPointer(
         index,
         size,
         ffi::GL_FLOAT,
         ffi::GL_FALSE,
         0,
         buffer.as_ptr() as *const ffi::GLvoid
      )
   }

   gl_result("glVertexAttribPointer", ())
}

#[inline]
pub fn enable_vertex_attrib_array(index: ffi::GLuint) -> VoidResult {
   unsafe {
      ffi::glEnableVertexAttribArray(index)
   }

   gl_result("glEnableVertexAttribArray", ())
}

#[inline]
pub fn uniform_value_1i(location: ffi::GLint, value: ffi::GLint) -> VoidResult {
   unsafe {
      ffi::glUniform1i(location, value)
   }

   gl_result("glUniform1i", ())
}

#[inline]
pub fn draw_arrays(count: ffi::GLsizei) -> VoidResult {
   unsafe {
      ffi::glDrawArrays(ffi::GL_TRIANGLE_STRIP, 0, count)
   }

   gl_result("glDrawArrays", ())
}

pub struct Texture {
   pub name: ffi::GLuint,
}

impl Texture {
   pub fn new(width: u32, height: u32) -> Result<Self, RuntimeError> {
      let name = try!(Self::gen_texture());

      let texture = Texture {
         name: name,
      };

      try!(texture.bind());

      try!(texture.tex_parameter(ffi::GL_TEXTURE_WRAP_S, ffi::GL_CLAMP_TO_EDGE));

      try!(texture.tex_parameter(ffi::GL_TEXTURE_WRAP_T, ffi::GL_CLAMP_TO_EDGE));

      try!(texture.tex_parameter(ffi::GL_TEXTURE_MIN_FILTER, ffi::GL_NEAREST));

      try!(texture.tex_parameter(ffi::GL_TEXTURE_MAG_FILTER, ffi::GL_NEAREST));

      try!(texture.resize(width, height));

      Ok(texture)
   }

   #[inline]
   pub fn gen_texture() -> Result<ffi::GLuint, RuntimeError> {
      let mut name: ffi::GLuint = unsafe { mem::uninitialized() };

      unsafe {
         ffi::glGenTextures(1, &mut name);
      }

      gl_result("glGenTextures", name)
   }

   #[inline]
   pub fn bind(&self) -> VoidResult {
      unsafe {
         ffi::glBindTexture(ffi::GL_TEXTURE_2D, self.name);
      }

      gl_result("glBindTexture", ())
   }

   #[inline]
   pub fn tex_parameter(&self, pname: ffi::GLenum, param: ffi::GLenum) -> VoidResult {
      unsafe {
         ffi::glTexParameteri(
            ffi::GL_TEXTURE_2D,
            pname,
            param as ffi::GLint
         );
      }

      gl_result(&format!("glTexParameteri(0x{:X}, 0x{:X})", pname, param), ())
   }

   #[inline]
   pub fn resize(&self, width: u32, height: u32) -> VoidResult {
      unsafe {
         ffi::glTexImage2D(
            ffi::GL_TEXTURE_2D,
            0,
            ffi::GL_RGBA8 as ffi::GLint,
            width as ffi::GLsizei,
            height as ffi::GLsizei,
            0,
            ffi::GL_RGBA,
            ffi::GL_UNSIGNED_BYTE,
            ptr::null()
         );
      }

      gl_result("glTexImage2D", ())
   }

   #[inline]
   pub fn null_update(&self, width: u32, height: u32) -> VoidResult {
      unsafe {
         ffi::glTexSubImage2D(
            ffi::GL_TEXTURE_2D,
            0,
            0, 0, width as ffi::GLsizei, height as ffi::GLsizei,
            ffi::GL_RGBA,
            ffi::GL_UNSIGNED_BYTE,
            ptr::null()
         );
      }

      gl_result("glTexSubImage2D(NULL)", ())
   }

   #[inline]
   pub fn update(&self, width: u32, height: u32, data: &[u8]) -> VoidResult {
      unsafe {
         ffi::glTexSubImage2D(
            ffi::GL_TEXTURE_2D,
            0,
            0, 0, width as ffi::GLsizei, height as ffi::GLsizei,
            ffi::GL_RGBA,
            ffi::GL_UNSIGNED_BYTE,
            data.as_ptr() as *const ffi::GLvoid
         );
      }

      gl_result("glTexSubImage2D", ())
   }
}

impl Drop for Texture {
   fn drop (&mut self) {
      unsafe {
         ffi::glDeleteTextures(1, &self.name)
      };
   }
}

pub struct Framebuffer {
   pub name: ffi::GLuint,
}

impl Framebuffer {
   pub fn new(texture: &Texture) -> Result<Self, RuntimeError> {
      let name = try!(Framebuffer::get_framebuffers());

      let framebuffer = Framebuffer {
         name: name,
      };

      try!(framebuffer.bind());
      try!(framebuffer.attach_texture(texture));
      try!(framebuffer.unbind());

      Ok(framebuffer)
   }

   #[inline]
   pub fn get_framebuffers() -> Result<ffi::GLuint, RuntimeError> {
      let mut name: ffi::GLuint = unsafe { mem::uninitialized() };

      unsafe {
         ffi::glGenFramebuffers(1, &mut name)
      };

      gl_result("glGenFramebuffers", name)
   }

   #[inline]
   pub fn bind(&self) -> VoidResult {
      unsafe {
         ffi::glBindFramebuffer(ffi::GL_READ_FRAMEBUFFER, self.name)
      };

      gl_result("glBindFramebuffer(name)", ())
   }

   #[inline]
   pub fn unbind(&self) -> VoidResult {
      unsafe {
         ffi::glBindFramebuffer(ffi::GL_READ_FRAMEBUFFER, 0)
      };

      gl_result("glBindFramebuffer(0)", ())
   }

   #[inline]
   pub fn attach_texture(&self, texture: &Texture) -> VoidResult {
      unsafe {
         ffi::glFramebufferTexture2D(
            ffi::GL_READ_FRAMEBUFFER,
            ffi::GL_COLOR_ATTACHMENT0,
            ffi::GL_TEXTURE_2D,
            texture.name,
            0
         )
      };

      gl_result("glFramebufferTexture2D", ())
   }

   #[inline]
   pub fn blit(&self, width: u32, height: u32) -> VoidResult {
      unsafe {
         ffi::glBlitFramebuffer(
            0, 0, width as ffi::GLint, height as ffi::GLint,
            0, 0, width as ffi::GLint, height as ffi::GLint,
            ffi::GL_COLOR_BUFFER_BIT,
            ffi::GL_NEAREST
         )
      };

      gl_result("glBlitFramebuffer", ())
   }
}

impl Drop for Framebuffer {
   fn drop (&mut self) {
      unsafe {
         ffi::glDeleteFramebuffers(1, &self.name)
      };
   }
}

pub struct Buffer {
   pub name: ffi::GLuint,
   pub ptr: *mut ffi::c_void,
   pub size: ffi::GLsizeiptr,
}

impl Buffer {
   pub fn new() -> Result<Self, RuntimeError> {
      let mut name: ffi::GLuint = unsafe { mem::uninitialized() };

      unsafe {
         ffi::glGenBuffers(1, &mut name)
      };

      gl_result("glGenBuffers", Buffer {
         name: name,
         ptr: ptr::null_mut(),
         size: 0,
      })
   }

   #[inline]
   pub fn bind(&self) -> VoidResult {
      unsafe {
         ffi::glBindBuffer(ffi::GL_PIXEL_UNPACK_BUFFER, self.name)
      };

      gl_result("glBindBuffer(name)", ())
   }

   #[inline]
   pub fn unbind(&self) -> VoidResult {
      unsafe {
         ffi::glBindBuffer(ffi::GL_PIXEL_UNPACK_BUFFER, 0)
      };

      gl_result("glBindBuffer(0)", ())
   }

   #[inline]
   pub fn init_data(&mut self, size: usize) -> VoidResult {
      self.size = size as ffi::GLsizeiptr;
      unsafe {
         ffi::glBufferData(
            ffi::GL_PIXEL_UNPACK_BUFFER,
            self.size,
            ptr::null(),
            ffi::GL_STREAM_DRAW
         )
      };

      gl_result("glBufferData", ())
   }

   #[inline]
   pub fn map(&mut self) -> VoidResult {
      self.ptr = unsafe {
         ffi::glMapBuffer(ffi::GL_PIXEL_UNPACK_BUFFER, ffi::GL_WRITE_ONLY)
      };

      gl_result("glMapBuffer", ())
   }

   #[inline]
   pub fn map_range(&mut self) -> VoidResult {
      self.ptr = unsafe {
         ffi::glMapBufferRange(
            ffi::GL_PIXEL_UNPACK_BUFFER, 0, self.size,
            ffi::GL_MAP_WRITE_BIT// | ffi::GL_MAP_UNSYNCHRONIZED_BIT
         )
      };

      gl_result("glMapBufferRange", ())
   }

   #[inline]
   pub fn unmap(&self) -> VoidResult {
      unsafe {
         ffi::glUnmapBuffer(ffi::GL_PIXEL_UNPACK_BUFFER)
      };

      gl_result("glUnmapBuffer", ())
   }
}

impl Drop for Buffer {
   fn drop (&mut self) {
      unsafe {
         ffi::glDeleteBuffers(1, &self.name)
      };
   }
}

pub struct Shader {
   pub name: ffi::GLuint,
}

impl Shader {
   pub fn new(shader_type: ffi::GLenum) -> Result<Self, RuntimeError> {
      let name = unsafe {
         ffi::glCreateShader(shader_type)
      };

      gl_result("glCreateShader", Shader {
         name: name,
      })
   }

   #[inline]
   pub fn shader_source(&self, string: &str) -> VoidResult {
      let cstring = try!(CString::new(string));

      unsafe {
         ffi::glShaderSource(self.name, 1, [cstring.as_ptr()].as_ptr(), ptr::null())
      };

      gl_result("glShaderSource", ())
   }

   #[inline]
   pub fn compile(&self) -> VoidResult {
      unsafe {
         ffi::glCompileShader(self.name)
      };

      if !try!(self.is_compiled()) {
         return gl_error("glCompileShader", "Shader not compiled")
      }

      Ok(())
   }

   #[inline]
   pub fn is_compiled(&self) -> Result<bool, RuntimeError> {
      let mut compiled: ffi::GLint = unsafe { mem::uninitialized() };

      unsafe {
         ffi::glGetShaderiv(self.name, ffi::GL_COMPILE_STATUS, &mut compiled);
      };

      gl_result("glGetShaderiv", compiled == ffi::GL_TRUE as ffi::GLint)
   }
}

impl Drop for Shader {
   fn drop (&mut self) {
      unsafe {
         ffi::glDeleteShader(self.name)
      };
   }
}

pub struct Program {
   pub name: ffi::GLuint,
}

impl Program {
   pub fn new() -> Result<Self, RuntimeError> {
      let name = unsafe {
         ffi::glCreateProgram()
      };

      gl_result("glCreateProgram", Program {
         name: name,
      })
   }

   #[inline]
   pub fn attach_shader(&self, shader: &Shader) -> VoidResult {
      unsafe {
         ffi::glAttachShader(self.name, shader.name)
      };

      gl_result("glAttachShader", ())
   }

   #[inline]
   pub fn link(&self) -> VoidResult {
      unsafe {
         ffi::glLinkProgram(self.name)
      };

      if !try!(self.is_linked()) {
         return gl_error("glLinkProgram", "Program not linked")
      }

      Ok(())
   }

   #[inline]
   pub fn use_program(&self) -> VoidResult {
      unsafe {
         ffi::glUseProgram(self.name)
      };

      gl_result("glUseProgram", ())
   }

   #[inline]
   pub fn is_linked(&self) -> Result<bool, RuntimeError> {
      let mut linked: ffi::GLint = unsafe { mem::uninitialized() };

      unsafe {
         ffi::glGetProgramiv(self.name, ffi::GL_LINK_STATUS, &mut linked);
      };

      gl_result("glGetProgramiv", linked == ffi::GL_TRUE as ffi::GLint)
   }

   #[inline]
   pub fn get_attrib_location(&self, attrib_name: &str) -> Result<ffi::GLint, RuntimeError> {
      let cname = try!(CString::new(attrib_name));

      let result = unsafe {
         ffi::glGetAttribLocation(self.name, cname.as_ptr())
      };

      gl_result("glGetAttribLocation", result)
   }

   #[inline]
   pub fn get_uniform_location(&self, variable_name: &str) -> Result<ffi::GLint, RuntimeError> {
      let cname = try!(CString::new(variable_name));

      let result = unsafe {
         ffi::glGetUniformLocation(self.name, cname.as_ptr())
      };

      gl_result("glGetUniformLocation", result)
   }
}

impl Drop for Program {
   fn drop (&mut self) {
      unsafe {
         ffi::glDeleteProgram(self.name)
      };
   }
}
<|endoftext|>