<commit_before>use luminance::{FragmentShader, GeometryShader, StageError, ShaderTypeable,
                TessellationControlShader, TessellationEvaluationShader, VertexShader};
use luminance_gl::gl33::{ProgramProxy, Stage};
#[cfg(feature = "hot-shader")]
use notify::{self, RecommendedWatcher, Watcher};
#[cfg(feature = "hot-shader")]
use std::collections::BTreeMap;
use std::fs;
use std::io::Read;
#[cfg(not(feature = "hot-shader"))]
use std::marker::PhantomData;
use std::ops::Deref;
use std::path::PathBuf;
#[cfg(feature = "hot-shader")]
use std::sync::{Arc, Mutex};
#[cfg(feature = "hot-shader")]
use std::sync::mpsc;
#[cfg(feature = "hot-shader")]
use std::thread;

pub use luminance::{ProgramError, UniformUpdate};
pub use luminance::shader::program::UniformWarning;
pub use luminance_gl::gl33::Program;
pub use luminance_gl::gl33::shader::uniform::{Uniform, Uniformable};

#[derive(Debug)]
pub enum ShaderError {
  StageError(StageError),
  ProgramError(ProgramError)
}

fn compile_stages(tess_src: Option<(&str, &str)>, vs_src: &str, gs_src: Option<&str>, fs_src: &str) -> Result<(Option<(Stage<TessellationControlShader>, Stage<TessellationEvaluationShader>)>, Stage<VertexShader>, Option<Stage<GeometryShader>>, Stage<FragmentShader>), StageError> {
  let tess = match tess_src {
    None => None,
    Some((tcs_src, tes_src)) => {
      let tcs = try!(Stage::new(tcs_src));
      let tes = try!(Stage::new(tes_src));
      Some((tcs, tes))
    }
  };
  let vs = try!(Stage::new(vs_src));
  let gs = match gs_src {
    None => None,
    Some(gs_src) => {
      let gs = try!(Stage::new(gs_src));
      Some(gs)
    }
  };
  let fs = try!(Stage::new(fs_src));

  Ok((tess, vs, gs, fs))
}

pub fn new_program<GetUni, T>(tess_src: Option<(&str, &str)>, vs_src: &str, gs_src: Option<&str>, fs_src: &str, get_uni: &GetUni) -> Result<Program<T>, ProgramError> where GetUni: Fn(ProgramProxy) -> Result<T, ProgramError> {
  let stages = compile_stages(tess_src, vs_src, gs_src, fs_src);

  match stages {
    Ok((tess, vs, gs, fs)) => {
      if let Some((tcs, tes)) = tess {
        if let Some(gs) = gs {
          Program::new(Some((&tcs, &tes)), &vs, Some(&gs), &fs, get_uni)
        } else {
          Program::new(Some((&tcs, &tes)), &vs, None, &fs, get_uni)
        }
      } else {
        if let Some(gs) = gs {
          Program::new(None, &vs, Some(&gs), &fs, get_uni)
        } else {
          Program::new(None, &vs, None, &fs, get_uni)
        }
      }
    },
    Err(stage_error) => {
      Err(ProgramError::LinkFailed(format!("{:?}", stage_error)))
    }
  }
}

pub fn new_program_from_disk<GetUni, T>(tess_path: Option<(PathBuf, PathBuf)>, vs_path: PathBuf, gs_path: Option<PathBuf>, fs_path: PathBuf, get_uni: &GetUni) -> Result<Program<T>, ProgramError>
      where GetUni: Fn(ProgramProxy) -> Result<T, ProgramError> {
  // load vertex and fragment shaders first
  let vs = try!(read_stage(vs_path).map_err(|e| ProgramError::LinkFailed(format!("{:?}", e))));
  let fs = try!(read_stage(fs_path).map_err(|e| ProgramError::LinkFailed(format!("{:?}", e))));

  match (tess_path, gs_path) {
    (None, None) => { // no tessellation nor geometry
      Program::new(None, &vs, None, &fs, get_uni)
    },
    (Some((tcs_path, tes_path)), None) => { // tessellation without geometry
      let tcs = try!(read_stage(tcs_path).map_err(|e| ProgramError::LinkFailed(format!("{:?}", e))));
      let tes = try!(read_stage(tes_path).map_err(|e| ProgramError::LinkFailed(format!("{:?}", e))));
      Program::new(Some((&tcs, &tes)), &vs, None, &fs, get_uni)
    },
    (None, Some(gs_path)) => { // geometry without tessellation
      let gs = try!(read_stage(gs_path).map_err(|e| ProgramError::LinkFailed(format!("{:?}", e))));
      Program::new(None, &vs, Some(&gs), &fs, get_uni)
    },
    (Some((tcs_path, tes_path)), Some(gs_path)) => { // tessellation and geometry
      let tcs = try!(read_stage(tcs_path).map_err(|e| ProgramError::LinkFailed(format!("{:?}", e))));
      let tes = try!(read_stage(tes_path).map_err(|e| ProgramError::LinkFailed(format!("{:?}", e))));
      let gs = try!(read_stage(gs_path).map_err(|e| ProgramError::LinkFailed(format!("{:?}", e))));
      Program::new(Some((&tcs, &tes)), &vs, Some(&gs), &fs, get_uni)
    }
  }
}

pub fn read_stage<T>(path: PathBuf) -> Result<Stage<T>, StageError> where T: ShaderTypeable {
  info!("loading {:?} stage: \x1b[35m{:?}", T::shader_type(), path);

  let fh = fs::File::open(path);

  match fh {
    Err(e) => {
      Err(StageError::CompilationFailed(T::shader_type(), format!("{:?}", e)))
    },
    Ok(mut fh) => {
      let mut stage_src = String::new();
      let _ = fh.read_to_string(&mut stage_src);

      Stage::new(stage_src.chars().as_str())
    }
  }
}

/// Shader builder.
#[cfg(feature = "hot-shader")]
pub struct ProgramBuilder {
  watcher: RecommendedWatcher,
  receivers: Arc<Mutex<BTreeMap<PathBuf, mpsc::Sender<()>>>>
}
#[cfg(not(feature = "hot-shader"))]
pub struct ProgramBuilder {}

#[cfg(feature = "hot-shader")]
impl ProgramBuilder {
  pub fn new(shader_root: PathBuf) -> Self {
    let (wsx, wrx) = mpsc::channel();
    let mut watcher: RecommendedWatcher = Watcher::new(wsx).unwrap();
    let receivers: Arc<Mutex<BTreeMap<PathBuf, mpsc::Sender<()>>>> = Arc::new(Mutex::new(BTreeMap::new()));

    let _ = watcher.watch(shader_root);

    let receivers_ = receivers.clone();
    let _ = thread::spawn(move || {
      for event in wrx.iter() {
        match event {
          notify::Event { path: Some(path), op: Ok(notify::op::WRITE) } => {
            if let Some(sx) = receivers_.lock().unwrap().get(&path) {
              sx.send(()).unwrap();
            }
          },
          _ => {}
        }
      }
    });

    ProgramBuilder {
      watcher: watcher,
      receivers: receivers
    }
  }

  pub fn retrieve<'a, T, GetUni>(&mut self, tess_path: Option<(PathBuf, PathBuf)>, vs_path: PathBuf, gs_path: Option<PathBuf>, fs_path: PathBuf, get_uni: GetUni) -> Result<WrappedProgram<'a, T>, ProgramError>
      where GetUni: 'a + Fn(ProgramProxy) -> Result<T, ProgramError> {
    let program = try!(new_program_from_disk(tess_path.clone(), vs_path.clone(), gs_path.clone(), fs_path.clone(), &get_uni));
    let (sx, rx) = mpsc::channel();

    self.monitor_shader(tess_path.clone(), vs_path.clone(), gs_path.clone(), fs_path.clone(), sx);

    Ok(WrappedProgram {
      rx: rx,
      program: program,
      get_uni: Box::new(get_uni),
      vs_path: vs_path,
      fs_path: fs_path,
      tess_path: tess_path,
      gs_path: gs_path,
    })
  }

  /// Add surveillance of a given `Program` by providing the path to all its shaders. When a change
  /// occurs, the `Program` gets notified of the change via its `Receiver` channel part.
  fn monitor_shader(&mut self, tess: Option<(PathBuf, PathBuf)>, vs: PathBuf, gs: Option<PathBuf>, fs: PathBuf, sx: mpsc::Sender<()>) {
    let mut receivers = self.receivers.lock().unwrap();

    // vertex shader
    receivers.insert(vs, sx.clone());

    // fragment shader
    receivers.insert(fs, sx.clone());

    // tessellation, if needed
    if let Some((tcs, tes)) = tess {
      // tessellation control shader
      receivers.insert(tcs, sx.clone());
      // tessellation evaluation shader
      receivers.insert(tes, sx.clone());
    }

    // geometry shader, if needed
    if let Some(gs) = gs {
      receivers.insert(gs, sx.clone());
    }
  }
}
#[cfg(not(feature = "hot-shader"))]
impl ProgramBuilder {
  pub fn new(_: PathBuf) -> Self {
    ProgramBuilder {}
  }

  pub fn retrieve<'a, T, GetUni>(&mut self, tess_path: Option<(PathBuf, PathBuf)>, vs_path: PathBuf, gs_path: Option<PathBuf>, fs_path: PathBuf, get_uni: GetUni) -> Result<WrappedProgram<'a, T>, ProgramError>
      where GetUni: 'a + Fn(ProgramProxy) -> Result<T, ProgramError> {
    let program = try!(new_program_from_disk(tess_path, vs_path, gs_path, fs_path, &get_uni));

    Ok(WrappedProgram {
      program: program,
      _a: PhantomData
    })
  }
}

/// A `Program` wrapped by **ion**.
///
/// That wrapper is used to enable hot-reloading of shader programs.
#[cfg(feature = "hot-shader")]
pub struct WrappedProgram<'a, T> {
  rx: mpsc::Receiver<()>,
  program: Program<T>,
  get_uni: Box<Fn(ProgramProxy) -> Result<T, ProgramError> + 'a>,
  vs_path: PathBuf,
  fs_path: PathBuf,
  tess_path: Option<(PathBuf, PathBuf)>,
  gs_path: Option<PathBuf>
}
#[cfg(not(feature = "hot-shader"))]
pub struct WrappedProgram<'a, T> {
  program: Program<T>,
  _a: PhantomData<&'a ()>
}

impl<'a, T> WrappedProgram<'a, T> {
  #[cfg(feature = "hot-shader")]
  fn reload(&mut self) {
    let program = new_program_from_disk(self.tess_path.clone(), self.vs_path.clone(), self.gs_path.clone(), self.fs_path.clone(), &self.get_uni.as_ref());

    match program {
      Ok(program) => {
        self.program = program;
      },
      Err(err) => {
        err!("reloading program has failed: {:?}", err);
      }
    }
  }

  /// Sync the embedded `Program`.
  #[cfg(feature = "hot-shader")]
  pub fn sync(&mut self) {
    if self.rx.try_recv().is_ok() {
      self.reload();
    }
  }
  #[cfg(not(feature = "hot-shader"))]
  pub fn sync(&mut self) {}
}

impl<'a, T> Deref for WrappedProgram<'a, T> {
  type Target = Program<T>;

  fn deref(&self) -> &Self::Target {
    &self.program
  }
}

/// A helper function used to make uniforms optionable. If there’s a warning, it’s printed out.
fn opt_uni<T>(uni: (Uniform<T>, Option<UniformWarning>)) -> Uniform<T> where T: Uniformable {
  if let Some(warning) = uni.1 {
    warn!("{:?}", warning);
  }

  uni.0
}
<commit_msg>Made opt_uni public.<commit_after>use luminance::{FragmentShader, GeometryShader, StageError, ShaderTypeable,
                TessellationControlShader, TessellationEvaluationShader, VertexShader};
use luminance_gl::gl33::{ProgramProxy, Stage};
#[cfg(feature = "hot-shader")]
use notify::{self, RecommendedWatcher, Watcher};
#[cfg(feature = "hot-shader")]
use std::collections::BTreeMap;
use std::fs;
use std::io::Read;
#[cfg(not(feature = "hot-shader"))]
use std::marker::PhantomData;
use std::ops::Deref;
use std::path::PathBuf;
#[cfg(feature = "hot-shader")]
use std::sync::{Arc, Mutex};
#[cfg(feature = "hot-shader")]
use std::sync::mpsc;
#[cfg(feature = "hot-shader")]
use std::thread;

pub use luminance::{ProgramError, UniformUpdate};
pub use luminance::shader::program::UniformWarning;
pub use luminance_gl::gl33::Program;
pub use luminance_gl::gl33::shader::uniform::{Uniform, Uniformable};

#[derive(Debug)]
pub enum ShaderError {
  StageError(StageError),
  ProgramError(ProgramError)
}

fn compile_stages(tess_src: Option<(&str, &str)>, vs_src: &str, gs_src: Option<&str>, fs_src: &str) -> Result<(Option<(Stage<TessellationControlShader>, Stage<TessellationEvaluationShader>)>, Stage<VertexShader>, Option<Stage<GeometryShader>>, Stage<FragmentShader>), StageError> {
  let tess = match tess_src {
    None => None,
    Some((tcs_src, tes_src)) => {
      let tcs = try!(Stage::new(tcs_src));
      let tes = try!(Stage::new(tes_src));
      Some((tcs, tes))
    }
  };
  let vs = try!(Stage::new(vs_src));
  let gs = match gs_src {
    None => None,
    Some(gs_src) => {
      let gs = try!(Stage::new(gs_src));
      Some(gs)
    }
  };
  let fs = try!(Stage::new(fs_src));

  Ok((tess, vs, gs, fs))
}

pub fn new_program<GetUni, T>(tess_src: Option<(&str, &str)>, vs_src: &str, gs_src: Option<&str>, fs_src: &str, get_uni: &GetUni) -> Result<Program<T>, ProgramError> where GetUni: Fn(ProgramProxy) -> Result<T, ProgramError> {
  let stages = compile_stages(tess_src, vs_src, gs_src, fs_src);

  match stages {
    Ok((tess, vs, gs, fs)) => {
      if let Some((tcs, tes)) = tess {
        if let Some(gs) = gs {
          Program::new(Some((&tcs, &tes)), &vs, Some(&gs), &fs, get_uni)
        } else {
          Program::new(Some((&tcs, &tes)), &vs, None, &fs, get_uni)
        }
      } else {
        if let Some(gs) = gs {
          Program::new(None, &vs, Some(&gs), &fs, get_uni)
        } else {
          Program::new(None, &vs, None, &fs, get_uni)
        }
      }
    },
    Err(stage_error) => {
      Err(ProgramError::LinkFailed(format!("{:?}", stage_error)))
    }
  }
}

pub fn new_program_from_disk<GetUni, T>(tess_path: Option<(PathBuf, PathBuf)>, vs_path: PathBuf, gs_path: Option<PathBuf>, fs_path: PathBuf, get_uni: &GetUni) -> Result<Program<T>, ProgramError>
      where GetUni: Fn(ProgramProxy) -> Result<T, ProgramError> {
  // load vertex and fragment shaders first
  let vs = try!(read_stage(vs_path).map_err(|e| ProgramError::LinkFailed(format!("{:?}", e))));
  let fs = try!(read_stage(fs_path).map_err(|e| ProgramError::LinkFailed(format!("{:?}", e))));

  match (tess_path, gs_path) {
    (None, None) => { // no tessellation nor geometry
      Program::new(None, &vs, None, &fs, get_uni)
    },
    (Some((tcs_path, tes_path)), None) => { // tessellation without geometry
      let tcs = try!(read_stage(tcs_path).map_err(|e| ProgramError::LinkFailed(format!("{:?}", e))));
      let tes = try!(read_stage(tes_path).map_err(|e| ProgramError::LinkFailed(format!("{:?}", e))));
      Program::new(Some((&tcs, &tes)), &vs, None, &fs, get_uni)
    },
    (None, Some(gs_path)) => { // geometry without tessellation
      let gs = try!(read_stage(gs_path).map_err(|e| ProgramError::LinkFailed(format!("{:?}", e))));
      Program::new(None, &vs, Some(&gs), &fs, get_uni)
    },
    (Some((tcs_path, tes_path)), Some(gs_path)) => { // tessellation and geometry
      let tcs = try!(read_stage(tcs_path).map_err(|e| ProgramError::LinkFailed(format!("{:?}", e))));
      let tes = try!(read_stage(tes_path).map_err(|e| ProgramError::LinkFailed(format!("{:?}", e))));
      let gs = try!(read_stage(gs_path).map_err(|e| ProgramError::LinkFailed(format!("{:?}", e))));
      Program::new(Some((&tcs, &tes)), &vs, Some(&gs), &fs, get_uni)
    }
  }
}

pub fn read_stage<T>(path: PathBuf) -> Result<Stage<T>, StageError> where T: ShaderTypeable {
  info!("loading {:?} stage: \x1b[35m{:?}", T::shader_type(), path);

  let fh = fs::File::open(path);

  match fh {
    Err(e) => {
      Err(StageError::CompilationFailed(T::shader_type(), format!("{:?}", e)))
    },
    Ok(mut fh) => {
      let mut stage_src = String::new();
      let _ = fh.read_to_string(&mut stage_src);

      Stage::new(stage_src.chars().as_str())
    }
  }
}

/// Shader builder.
#[cfg(feature = "hot-shader")]
pub struct ProgramBuilder {
  watcher: RecommendedWatcher,
  receivers: Arc<Mutex<BTreeMap<PathBuf, mpsc::Sender<()>>>>
}
#[cfg(not(feature = "hot-shader"))]
pub struct ProgramBuilder {}

#[cfg(feature = "hot-shader")]
impl ProgramBuilder {
  pub fn new(shader_root: PathBuf) -> Self {
    let (wsx, wrx) = mpsc::channel();
    let mut watcher: RecommendedWatcher = Watcher::new(wsx).unwrap();
    let receivers: Arc<Mutex<BTreeMap<PathBuf, mpsc::Sender<()>>>> = Arc::new(Mutex::new(BTreeMap::new()));

    let _ = watcher.watch(shader_root);

    let receivers_ = receivers.clone();
    let _ = thread::spawn(move || {
      for event in wrx.iter() {
        match event {
          notify::Event { path: Some(path), op: Ok(notify::op::WRITE) } => {
            if let Some(sx) = receivers_.lock().unwrap().get(&path) {
              sx.send(()).unwrap();
            }
          },
          _ => {}
        }
      }
    });

    ProgramBuilder {
      watcher: watcher,
      receivers: receivers
    }
  }

  pub fn retrieve<'a, T, GetUni>(&mut self, tess_path: Option<(PathBuf, PathBuf)>, vs_path: PathBuf, gs_path: Option<PathBuf>, fs_path: PathBuf, get_uni: GetUni) -> Result<WrappedProgram<'a, T>, ProgramError>
      where GetUni: 'a + Fn(ProgramProxy) -> Result<T, ProgramError> {
    let program = try!(new_program_from_disk(tess_path.clone(), vs_path.clone(), gs_path.clone(), fs_path.clone(), &get_uni));
    let (sx, rx) = mpsc::channel();

    self.monitor_shader(tess_path.clone(), vs_path.clone(), gs_path.clone(), fs_path.clone(), sx);

    Ok(WrappedProgram {
      rx: rx,
      program: program,
      get_uni: Box::new(get_uni),
      vs_path: vs_path,
      fs_path: fs_path,
      tess_path: tess_path,
      gs_path: gs_path,
    })
  }

  /// Add surveillance of a given `Program` by providing the path to all its shaders. When a change
  /// occurs, the `Program` gets notified of the change via its `Receiver` channel part.
  fn monitor_shader(&mut self, tess: Option<(PathBuf, PathBuf)>, vs: PathBuf, gs: Option<PathBuf>, fs: PathBuf, sx: mpsc::Sender<()>) {
    let mut receivers = self.receivers.lock().unwrap();

    // vertex shader
    receivers.insert(vs, sx.clone());

    // fragment shader
    receivers.insert(fs, sx.clone());

    // tessellation, if needed
    if let Some((tcs, tes)) = tess {
      // tessellation control shader
      receivers.insert(tcs, sx.clone());
      // tessellation evaluation shader
      receivers.insert(tes, sx.clone());
    }

    // geometry shader, if needed
    if let Some(gs) = gs {
      receivers.insert(gs, sx.clone());
    }
  }
}
#[cfg(not(feature = "hot-shader"))]
impl ProgramBuilder {
  pub fn new(_: PathBuf) -> Self {
    ProgramBuilder {}
  }

  pub fn retrieve<'a, T, GetUni>(&mut self, tess_path: Option<(PathBuf, PathBuf)>, vs_path: PathBuf, gs_path: Option<PathBuf>, fs_path: PathBuf, get_uni: GetUni) -> Result<WrappedProgram<'a, T>, ProgramError>
      where GetUni: 'a + Fn(ProgramProxy) -> Result<T, ProgramError> {
    let program = try!(new_program_from_disk(tess_path, vs_path, gs_path, fs_path, &get_uni));

    Ok(WrappedProgram {
      program: program,
      _a: PhantomData
    })
  }
}

/// A `Program` wrapped by **ion**.
///
/// That wrapper is used to enable hot-reloading of shader programs.
#[cfg(feature = "hot-shader")]
pub struct WrappedProgram<'a, T> {
  rx: mpsc::Receiver<()>,
  program: Program<T>,
  get_uni: Box<Fn(ProgramProxy) -> Result<T, ProgramError> + 'a>,
  vs_path: PathBuf,
  fs_path: PathBuf,
  tess_path: Option<(PathBuf, PathBuf)>,
  gs_path: Option<PathBuf>
}
#[cfg(not(feature = "hot-shader"))]
pub struct WrappedProgram<'a, T> {
  program: Program<T>,
  _a: PhantomData<&'a ()>
}

impl<'a, T> WrappedProgram<'a, T> {
  #[cfg(feature = "hot-shader")]
  fn reload(&mut self) {
    let program = new_program_from_disk(self.tess_path.clone(), self.vs_path.clone(), self.gs_path.clone(), self.fs_path.clone(), &self.get_uni.as_ref());

    match program {
      Ok(program) => {
        self.program = program;
      },
      Err(err) => {
        err!("reloading program has failed: {:?}", err);
      }
    }
  }

  /// Sync the embedded `Program`.
  #[cfg(feature = "hot-shader")]
  pub fn sync(&mut self) {
    if self.rx.try_recv().is_ok() {
      self.reload();
    }
  }
  #[cfg(not(feature = "hot-shader"))]
  pub fn sync(&mut self) {}
}

impl<'a, T> Deref for WrappedProgram<'a, T> {
  type Target = Program<T>;

  fn deref(&self) -> &Self::Target {
    &self.program
  }
}

/// A helper function used to make uniforms optionable. If there’s a warning, it’s printed out.
pub fn opt_uni<T>(uni: (Uniform<T>, Option<UniformWarning>)) -> Uniform<T> where T: Uniformable {
  if let Some(warning) = uni.1 {
    warn!("{:?}", warning);
  }

  uni.0
}
<|endoftext|>


<commit_before>use super::gl;
use super::gl_lib::types::*;

pub struct Shader {
    gl_id: GLuint
}

impl Shader {
    pub unsafe fn from_id(id: GLuint) -> Shader {
        Shader { gl_id: id }
    }

    pub fn source(&mut self, shader_source: &str) {
        unsafe {
            let source = shader_source.as_ptr() as *const GLchar;
            let length = shader_source.len() as GLint;

            gl::ShaderSource(self.gl_id, 1,
                             &source as *const *const GLchar,
                             &length as *const GLint);
        }
    }
}

impl Drop for Shader {
    fn drop(&mut self) {
        unsafe {
            gl::DeleteShader(self.gl_id);
        }
    }
}

#[derive(Debug, Clone, Copy)]
#[allow(non_camel_case_types)]
pub enum ShaderType {
    VERTEX_SHADER = gl::VERTEX_SHADER as isize,
    FRAGMENT_SHADER = gl::FRAGMENT_SHADER as isize
}
pub use self::ShaderType::*;
<commit_msg>Add `Shader.compile`<commit_after>use std::mem;
use std::ptr;
use super::gl;
use super::gl_lib::types::*;
use super::types::GLError;

pub struct Shader {
    gl_id: GLuint
}

impl Shader {
    pub unsafe fn from_id(id: GLuint) -> Shader {
        Shader { gl_id: id }
    }

    pub fn source(&mut self, shader_source: &str) {
        unsafe {
            let source = shader_source.as_ptr() as *const GLchar;
            let length = shader_source.len() as GLint;

            gl::ShaderSource(self.gl_id, 1,
                             &source as *const *const GLchar,
                             &length as *const GLint);
        }
    }

    pub fn compile(&mut self) -> Result<(), GLError> {
        let success = unsafe {
            gl::CompileShader(self.gl_id);
            let mut compile_status : GLint = mem::uninitialized();
            gl::GetShaderiv(self.gl_id,
                            gl::COMPILE_STATUS,
                            &mut compile_status as *mut GLint);

            compile_status == gl::TRUE as GLint
        };

        if success {
            Ok(())
        }
        else {
            unsafe {
                let mut info_length : GLint = mem::uninitialized();
                gl::GetShaderiv(self.gl_id,
                                gl::INFO_LOG_LENGTH,
                                &mut info_length as *mut GLint);

                let mut bytes = Vec::<u8>::with_capacity(info_length as usize);

                gl::GetShaderInfoLog(self.gl_id,
                                     info_length,
                                     ptr::null_mut(),
                                     bytes.as_mut_ptr() as *mut GLchar);

                let msg = String::from_utf8(bytes)
                                 .unwrap_or(String::from("<Unknown error>"));

                Err(GLError { message: msg })
            }
        }
    }
}

impl Drop for Shader {
    fn drop(&mut self) {
        unsafe {
            gl::DeleteShader(self.gl_id);
        }
    }
}

#[derive(Debug, Clone, Copy)]
#[allow(non_camel_case_types)]
pub enum ShaderType {
    VERTEX_SHADER = gl::VERTEX_SHADER as isize,
    FRAGMENT_SHADER = gl::FRAGMENT_SHADER as isize
}
pub use self::ShaderType::*;
<|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 euclid::Point2D;

/// Some arbitrarily small positive number used as threshold value.
pub const EPSILON: f32 = 0.001;

/// The default stiffness factor.
pub const STIFFNESS: f32 = 0.2;

/// The default damping factor.
pub const DAMPING: f32 = 1.0;

#[derive(Copy, Clone, Debug)]
pub struct Spring {
    /// The current position of spring.
    cur: Point2D<f32>,
    /// The position of spring at previous tick.
    prev: Point2D<f32>,
    /// The destination of spring.
    dest: Point2D<f32>,
    /// How hard it springs back.
    stiffness: f32,
    /// Friction. 1.0 means no bounce.
    damping: f32,
}

impl Spring {
    /// Create a new spring at location.
    pub fn at(pos: Point2D<f32>, stiffness: f32, damping: f32) -> Spring {
        Spring {
            cur: pos,
            prev: pos,
            dest: pos,
            stiffness: stiffness,
            damping: damping,
        }
    }

    /// Set coords on a spring, mutating spring
    pub fn coords(&mut self, cur: Point2D<f32>, prev: Point2D<f32>, dest: Point2D<f32>) {
        self.cur = cur;
        self.prev = prev;
        self.dest = dest
    }

    pub fn current(&self) -> Point2D<f32> {
        self.cur
    }

    /// Run one tick of the spring animation. Return true if the animation is complete.
    pub fn animate(&mut self) -> bool {
        if !is_resting(self.cur.x, self.prev.x, self.dest.x) ||
                !is_resting(self.cur.y, self.prev.y, self.dest.y) {
            let next = Point2D::new(next(self.cur.x,
                                         self.prev.x,
                                         self.dest.x,
                                         self.stiffness,
                                         self.damping),
                                    next(self.cur.y,
                                         self.prev.y,
                                         self.dest.y,
                                         self.stiffness,
                                         self.damping));
            let (cur, dest) = (self.cur, self.dest);
            self.coords(next, cur, dest);
            false
        } else {
            let dest = self.dest;
            self.coords(dest, dest, dest);
            true
        }
    }
}

/// Given numbers, calculate the next position for a spring.
fn next(cur: f32, prev: f32, dest: f32, stiffness: f32, damping: f32) -> f32 {
    // Calculate spring force
    let fspring = -stiffness * (cur - dest);

    // Calculate velocity
    let vel = cur - prev;

    // Calculate damping force.
    let fdamping = -damping * vel;

    // Calc acceleration by adjusting spring force to damping force
    let acc = fspring + fdamping;

    // Calculate new velocity after adding acceleration. Scale to framerate.
    let nextv = vel + acc;

    // Calculate next position by integrating velocity. Scale to framerate.
    let next = cur + nextv;
    next
}

/// Given numbers, calcluate if a spring is at rest.
fn is_resting(cur: f32, prev: f32, dest: f32) -> bool {
    (cur - prev).abs() < EPSILON && (cur - dest).abs() < EPSILON
}

<commit_msg>Make the epsilon for the spring simulation larger.<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 euclid::Point2D;

/// Some arbitrarily small positive number used as threshold value.
pub const EPSILON: f32 = 0.1;

/// The default stiffness factor.
pub const STIFFNESS: f32 = 0.2;

/// The default damping factor.
pub const DAMPING: f32 = 1.0;

#[derive(Copy, Clone, Debug)]
pub struct Spring {
    /// The current position of spring.
    cur: Point2D<f32>,
    /// The position of spring at previous tick.
    prev: Point2D<f32>,
    /// The destination of spring.
    dest: Point2D<f32>,
    /// How hard it springs back.
    stiffness: f32,
    /// Friction. 1.0 means no bounce.
    damping: f32,
}

impl Spring {
    /// Create a new spring at location.
    pub fn at(pos: Point2D<f32>, stiffness: f32, damping: f32) -> Spring {
        Spring {
            cur: pos,
            prev: pos,
            dest: pos,
            stiffness: stiffness,
            damping: damping,
        }
    }

    /// Set coords on a spring, mutating spring
    pub fn coords(&mut self, cur: Point2D<f32>, prev: Point2D<f32>, dest: Point2D<f32>) {
        self.cur = cur;
        self.prev = prev;
        self.dest = dest
    }

    pub fn current(&self) -> Point2D<f32> {
        self.cur
    }

    /// Run one tick of the spring animation. Return true if the animation is complete.
    pub fn animate(&mut self) -> bool {
        if !is_resting(self.cur.x, self.prev.x, self.dest.x) ||
                !is_resting(self.cur.y, self.prev.y, self.dest.y) {
            let next = Point2D::new(next(self.cur.x,
                                         self.prev.x,
                                         self.dest.x,
                                         self.stiffness,
                                         self.damping),
                                    next(self.cur.y,
                                         self.prev.y,
                                         self.dest.y,
                                         self.stiffness,
                                         self.damping));
            let (cur, dest) = (self.cur, self.dest);
            self.coords(next, cur, dest);
            false
        } else {
            let dest = self.dest;
            self.coords(dest, dest, dest);
            true
        }
    }
}

/// Given numbers, calculate the next position for a spring.
fn next(cur: f32, prev: f32, dest: f32, stiffness: f32, damping: f32) -> f32 {
    // Calculate spring force
    let fspring = -stiffness * (cur - dest);

    // Calculate velocity
    let vel = cur - prev;

    // Calculate damping force.
    let fdamping = -damping * vel;

    // Calc acceleration by adjusting spring force to damping force
    let acc = fspring + fdamping;

    // Calculate new velocity after adding acceleration. Scale to framerate.
    let nextv = vel + acc;

    // Calculate next position by integrating velocity. Scale to framerate.
    let next = cur + nextv;
    next
}

/// Given numbers, calcluate if a spring is at rest.
fn is_resting(cur: f32, prev: f32, dest: f32) -> bool {
    (cur - prev).abs() < EPSILON && (cur - dest).abs() < EPSILON
}

<|endoftext|>


<commit_before>use callback::Callable;
use frame::Frameable;
use color::{ Color, Colorable };
use label::{ FontSize, Labelable };
use dimensions::Dimensions;
use mouse::Mouse;
use point::Point;
use rectangle;
use graphics::Graphics;
use graphics::character::CharacterCache;
use ui_context::{ UIID, UiContext };
use widget::Widget;

/// Represents the state of the Toggle widget.
#[derive(PartialEq, Clone, Copy)]
pub enum State {
    Normal,
    Highlighted,
    Clicked,
}

impl State {
    /// Return the associated Rectangle state.
    fn as_rectangle_state(&self) -> rectangle::State {
        match self {
            &State::Normal => rectangle::State::Normal,
            &State::Highlighted => rectangle::State::Highlighted,
            &State::Clicked => rectangle::State::Clicked,
        }
    }
}

widget_fns!(Toggle, State, Widget::Toggle(State::Normal));

/// Check the current state of the button.
fn get_new_state(is_over: bool,
                 prev: State,
                 mouse: Mouse) -> State {
    use mouse::ButtonState::{Down, Up};
    use self::State::{Normal, Highlighted, Clicked};
    match (is_over, prev, mouse.left) {
        (true,  Normal,  Down) => Normal,
        (true,  _,       Down) => Clicked,
        (true,  _,       Up)   => Highlighted,
        (false, Clicked, Down) => Clicked,
        _                      => Normal,
    }
}

/// A context on which the builder pattern can be implemented.
pub struct Toggle<'a, F> {
    ui_id: UIID,
    pos: Point,
    dim: Dimensions,
    maybe_callback: Option<F>,
    maybe_color: Option<Color>,
    maybe_frame: Option<f64>,
    maybe_frame_color: Option<Color>,
    maybe_label: Option<&'a str>,
    maybe_label_color: Option<Color>,
    maybe_label_font_size: Option<u32>,
    value: bool,
}

impl<'a, F> Toggle<'a, F> {

    /// Create a toggle context to be built upon.
    pub fn new(ui_id: UIID, value: bool) -> Toggle<'a, F> {
        Toggle {
            ui_id: ui_id,
            pos: [0.0, 0.0],
            dim: [64.0, 64.0],
            maybe_callback: None,
            maybe_color: None,
            maybe_frame: None,
            maybe_frame_color: None,
            maybe_label: None,
            maybe_label_color: None,
            maybe_label_font_size: None,
            value: value,
        }
    }

}

impl<'a, F> Colorable for Toggle<'a, F> {
    fn color(mut self, color: Color) -> Self {
        self.maybe_color = Some(color);
        self
    }
}

impl<'a, F> Frameable for Toggle<'a, F> {
    fn frame(mut self, width: f64) -> Self {
        self.maybe_frame = Some(width);
        self
    }
    fn frame_color(mut self, color: Color) -> Self {
        self.maybe_frame_color = Some(color);
        self
    }
}

impl<'a, F> Callable<F> for Toggle<'a, F> {
    fn callback(mut self, cb: F) -> Self {
        self.maybe_callback = Some(cb);
        self
    }
}

impl<'a, F> Labelable<'a> for Toggle<'a, F>
{
    fn label(mut self, text: &'a str) -> Self {
        self.maybe_label = Some(text);
        self
    }

    fn label_color(mut self, color: Color) -> Self {
        self.maybe_label_color = Some(color);
        self
    }

    fn label_font_size(mut self, size: FontSize) -> Self {
        self.maybe_label_font_size = Some(size);
        self
    }
}

/*
quack! {
    toggle: Toggle['a, F]
    get:
        fn () -> Size [] { Size(toggle.dim) }
        fn () -> DefaultWidgetState [] {
            DefaultWidgetState(Widget::Toggle(State::Normal))
        }
        fn () -> Id [] { Id(toggle.ui_id) }
    set:
        fn (val: Position) [] { toggle.pos = val.0 }
        fn (val: Size) [] { toggle.dim = val.0 }
    action:
}
*/

impl<'a, F> ::draw::Drawable for Toggle<'a, F> where F: FnMut(bool) + 'a {
    fn draw<B, C>(&mut self, uic: &mut UiContext<C>, graphics: &mut B)
        where
            B: Graphics<Texture = <C as CharacterCache>::Texture>,
            C: CharacterCache
    {
        let color = self.maybe_color.unwrap_or(uic.theme.shape_color);
        let color = match self.value {
            true => color,
            false => color * Color::new(0.1, 0.1, 0.1, 1.0)
        };
        let state = *get_state(uic, self.ui_id);
        let mouse = uic.get_mouse_state();
        let is_over = rectangle::is_over(self.pos, mouse.pos, self.dim);
        let new_state = get_new_state(is_over, state, mouse);
        let rect_state = new_state.as_rectangle_state();
        match self.maybe_callback {
            Some(ref mut callback) => {
                match (is_over, state, new_state) {
                    (true, State::Clicked, State::Highlighted) =>
                        (*callback)(match self.value { true => false, false => true }),
                    _ => (),
                }
            }, None => (),
        }
        let frame_w = self.maybe_frame.unwrap_or(uic.theme.frame_width);
        let maybe_frame = match frame_w > 0.0 {
            true => Some((frame_w, self.maybe_frame_color.unwrap_or(uic.theme.frame_color))),
            false => None,
        };
        match self.maybe_label {
            None => {
                rectangle::draw(
                    uic.win_w, uic.win_h, graphics, rect_state, self.pos,
                    self.dim, maybe_frame, color
                )
            },
            Some(text) => {
                let text_color = self.maybe_label_color.unwrap_or(uic.theme.label_color);
                let size = self.maybe_label_font_size.unwrap_or(uic.theme.font_size_medium);
                rectangle::draw_with_centered_label(
                    uic.win_w, uic.win_h, graphics, uic, rect_state,
                    self.pos, self.dim, maybe_frame, color,
                    text, size, text_color
                )
            },
        }

        set_state(uic, self.ui_id, Widget::Toggle(new_state), self.pos, self.dim);

    }
}
<commit_msg>Implemented `Positionable` for `Toggle`<commit_after>use callback::Callable;
use frame::Frameable;
use color::{ Color, Colorable };
use label::{ FontSize, Labelable };
use dimensions::Dimensions;
use mouse::Mouse;
use point::Point;
use position::Positionable;
use rectangle;
use graphics::Graphics;
use graphics::character::CharacterCache;
use ui_context::{ UIID, UiContext };
use widget::Widget;

/// Represents the state of the Toggle widget.
#[derive(PartialEq, Clone, Copy)]
pub enum State {
    Normal,
    Highlighted,
    Clicked,
}

impl State {
    /// Return the associated Rectangle state.
    fn as_rectangle_state(&self) -> rectangle::State {
        match self {
            &State::Normal => rectangle::State::Normal,
            &State::Highlighted => rectangle::State::Highlighted,
            &State::Clicked => rectangle::State::Clicked,
        }
    }
}

widget_fns!(Toggle, State, Widget::Toggle(State::Normal));

/// Check the current state of the button.
fn get_new_state(is_over: bool,
                 prev: State,
                 mouse: Mouse) -> State {
    use mouse::ButtonState::{Down, Up};
    use self::State::{Normal, Highlighted, Clicked};
    match (is_over, prev, mouse.left) {
        (true,  Normal,  Down) => Normal,
        (true,  _,       Down) => Clicked,
        (true,  _,       Up)   => Highlighted,
        (false, Clicked, Down) => Clicked,
        _                      => Normal,
    }
}

/// A context on which the builder pattern can be implemented.
pub struct Toggle<'a, F> {
    ui_id: UIID,
    pos: Point,
    dim: Dimensions,
    maybe_callback: Option<F>,
    maybe_color: Option<Color>,
    maybe_frame: Option<f64>,
    maybe_frame_color: Option<Color>,
    maybe_label: Option<&'a str>,
    maybe_label_color: Option<Color>,
    maybe_label_font_size: Option<u32>,
    value: bool,
}

impl<'a, F> Toggle<'a, F> {

    /// Create a toggle context to be built upon.
    pub fn new(ui_id: UIID, value: bool) -> Toggle<'a, F> {
        Toggle {
            ui_id: ui_id,
            pos: [0.0, 0.0],
            dim: [64.0, 64.0],
            maybe_callback: None,
            maybe_color: None,
            maybe_frame: None,
            maybe_frame_color: None,
            maybe_label: None,
            maybe_label_color: None,
            maybe_label_font_size: None,
            value: value,
        }
    }

}

impl<'a, F> Colorable for Toggle<'a, F> {
    fn color(mut self, color: Color) -> Self {
        self.maybe_color = Some(color);
        self
    }
}

impl<'a, F> Frameable for Toggle<'a, F> {
    fn frame(mut self, width: f64) -> Self {
        self.maybe_frame = Some(width);
        self
    }
    fn frame_color(mut self, color: Color) -> Self {
        self.maybe_frame_color = Some(color);
        self
    }
}

impl<'a, F> Callable<F> for Toggle<'a, F> {
    fn callback(mut self, cb: F) -> Self {
        self.maybe_callback = Some(cb);
        self
    }
}

impl<'a, F> Labelable<'a> for Toggle<'a, F>
{
    fn label(mut self, text: &'a str) -> Self {
        self.maybe_label = Some(text);
        self
    }

    fn label_color(mut self, color: Color) -> Self {
        self.maybe_label_color = Some(color);
        self
    }

    fn label_font_size(mut self, size: FontSize) -> Self {
        self.maybe_label_font_size = Some(size);
        self
    }
}

impl<'a, F> Positionable for Toggle<'a, F> {
    fn point(mut self, pos: Point) -> Self {
        self.pos = pos;
        self
    }
}

/*
quack! {
    toggle: Toggle['a, F]
    get:
        fn () -> Size [] { Size(toggle.dim) }
        fn () -> DefaultWidgetState [] {
            DefaultWidgetState(Widget::Toggle(State::Normal))
        }
        fn () -> Id [] { Id(toggle.ui_id) }
    set:
        fn (val: Size) [] { toggle.dim = val.0 }
    action:
}
*/

impl<'a, F> ::draw::Drawable for Toggle<'a, F> where F: FnMut(bool) + 'a {
    fn draw<B, C>(&mut self, uic: &mut UiContext<C>, graphics: &mut B)
        where
            B: Graphics<Texture = <C as CharacterCache>::Texture>,
            C: CharacterCache
    {
        let color = self.maybe_color.unwrap_or(uic.theme.shape_color);
        let color = match self.value {
            true => color,
            false => color * Color::new(0.1, 0.1, 0.1, 1.0)
        };
        let state = *get_state(uic, self.ui_id);
        let mouse = uic.get_mouse_state();
        let is_over = rectangle::is_over(self.pos, mouse.pos, self.dim);
        let new_state = get_new_state(is_over, state, mouse);
        let rect_state = new_state.as_rectangle_state();
        match self.maybe_callback {
            Some(ref mut callback) => {
                match (is_over, state, new_state) {
                    (true, State::Clicked, State::Highlighted) =>
                        (*callback)(match self.value { true => false, false => true }),
                    _ => (),
                }
            }, None => (),
        }
        let frame_w = self.maybe_frame.unwrap_or(uic.theme.frame_width);
        let maybe_frame = match frame_w > 0.0 {
            true => Some((frame_w, self.maybe_frame_color.unwrap_or(uic.theme.frame_color))),
            false => None,
        };
        match self.maybe_label {
            None => {
                rectangle::draw(
                    uic.win_w, uic.win_h, graphics, rect_state, self.pos,
                    self.dim, maybe_frame, color
                )
            },
            Some(text) => {
                let text_color = self.maybe_label_color.unwrap_or(uic.theme.label_color);
                let size = self.maybe_label_font_size.unwrap_or(uic.theme.font_size_medium);
                rectangle::draw_with_centered_label(
                    uic.win_w, uic.win_h, graphics, uic, rect_state,
                    self.pos, self.dim, maybe_frame, color,
                    text, size, text_color
                )
            },
        }

        set_state(uic, self.ui_id, Widget::Toggle(new_state), self.pos, self.dim);

    }
}
<|endoftext|>


<commit_before>use super::ToTokens;
use std::fmt::{self, Display};

#[derive(Debug, Clone)]
pub struct Tokens(String);

impl Tokens {
    pub fn new() -> Self {
        Tokens(String::new())
    }

    pub fn append(&mut self, token: &str) {
        if !self.0.is_empty() && !token.is_empty() {
            self.0.push(' ');
        }
        self.0.push_str(token);
    }

    pub fn append_all<T, I>(&mut self, iter: I)
        where T: ToTokens,
              I: IntoIterator<Item = T>
    {
        for token in iter {
            token.to_tokens(self);
        }
    }

    pub fn append_separated<T, I>(&mut self, iter: I, sep: &str)
        where T: ToTokens,
              I: IntoIterator<Item = T>
    {
        for (i, token) in iter.into_iter().enumerate() {
            if i > 0 {
                self.append(sep);
            }
            token.to_tokens(self);
        }
    }

    pub fn append_terminated<T, I>(&mut self, iter: I, term: &str)
        where T: ToTokens,
              I: IntoIterator<Item = T>
    {
        for token in iter {
            token.to_tokens(self);
            self.append(term);
        }
    }
}

impl Default for Tokens {
    fn default() -> Self {
        Tokens::new()
    }
}

impl Display for Tokens {
    fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        self.0.fmt(formatter)
    }
}
<commit_msg>Allow equality comparison of Tokens<commit_after>use super::ToTokens;
use std::fmt::{self, Display};

#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Tokens(String);

impl Tokens {
    pub fn new() -> Self {
        Tokens(String::new())
    }

    pub fn append(&mut self, token: &str) {
        if !self.0.is_empty() && !token.is_empty() {
            self.0.push(' ');
        }
        self.0.push_str(token);
    }

    pub fn append_all<T, I>(&mut self, iter: I)
        where T: ToTokens,
              I: IntoIterator<Item = T>
    {
        for token in iter {
            token.to_tokens(self);
        }
    }

    pub fn append_separated<T, I>(&mut self, iter: I, sep: &str)
        where T: ToTokens,
              I: IntoIterator<Item = T>
    {
        for (i, token) in iter.into_iter().enumerate() {
            if i > 0 {
                self.append(sep);
            }
            token.to_tokens(self);
        }
    }

    pub fn append_terminated<T, I>(&mut self, iter: I, term: &str)
        where T: ToTokens,
              I: IntoIterator<Item = T>
    {
        for token in iter {
            token.to_tokens(self);
            self.append(term);
        }
    }
}

impl Default for Tokens {
    fn default() -> Self {
        Tokens::new()
    }
}

impl Display for Tokens {
    fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        self.0.fmt(formatter)
    }
}
<|endoftext|>


<commit_before>// The MIT License (MIT)
//
// Copyright (c) 2014 Jeremy Letang
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

//! A syntax extension to handle route attributes

#![crate_id = "github.com/JeremyLetang/web_dispatcher#route_macros:0.0.1"]
#![desc = "route macros for Rust"]
#![license = "mit"]
#![crate_type = "rlib"]
#![crate_type = "dylib"]
#![experimental]
#![allow(missing_doc)]
#![feature(macro_registrar, managed_boxes, quote)]

extern crate syntax;

use std::local_data;

use syntax::ast;
use syntax::ast::Path;
use syntax::parse::token;
use syntax::ast::ExprPath;
use syntax::codemap::Span;
use syntax::ast::PathSegment;
use syntax::owned_slice::OwnedSlice;
use syntax::ast::{Ident,
                  TokenTree,
                  Expr,
                  Name,
                  ExprVec,
                  MetaItem,
                  MetaNameValue,
                  LitStr,
                  Item,
                  ItemFn};
use syntax::ext::base::{ExtCtxt,
                        MacResult,
                        SyntaxExtension,
                        BasicMacroExpander,
                        NormalTT,
                        ItemModifier,
                        MacExpr};

// Store routes in a local data (vector of path ident, associated route, method as a string)
static routes: local_data::Key<Vec<(Vec<Ident>, ~str, ~str)>> = &local_data::Key;

fn local_data_get_or_init() -> Vec<(Vec<Ident>, ~str,~str)> {
    local_data::get(routes, |d| {
        match d {
            Some(v) =>  v.clone(),
            None => Vec::new()
        }
    })
}

#[doc(hidden)]
#[macro_registrar]
pub fn registrar(register: |Name, SyntaxExtension|) {
    register(token::intern("route"), ItemModifier(expand_route));
    register(token::intern("method"), ItemModifier(expand_method));
    register(token::intern("routes"),
             NormalTT(box BasicMacroExpander {
                expander: expand_get_routes,
                span: None,
             },
             None));
}

fn expand_get_routes(cx: &mut ExtCtxt, sp: Span, _: &[TokenTree]) -> Box<MacResult> {
    let v = local_data_get_or_init();
    let v = v.iter().map(|&(ref f, ref s, ref m)| {
        let p = create_func_path_expr(f, sp);
        quote_expr!(&*cx, ($p, $s, $m))
    }).collect();
    let v = create_slice_expr(v, sp);
    MacExpr::new(quote_expr!(cx, Vec::from_slice($v.to_owned())))
    // MacExpr::new(quote_expr!(cx, &'static $v))
}

// create the path expression
fn create_func_path_expr(vec_ident: &Vec<Ident>, sp: Span) -> @Expr {
    // create the list of path segment from the idents
    let segs = vec_ident.iter().fold(Vec::new(), |mut v, &i| {
        v.push(PathSegment {
            identifier: i,
            lifetimes: Vec::new(),
            types: OwnedSlice::empty(),
        });
        v
    });
    // create the complete Path from the segments
    let func_path = Path {
        span: sp,
        global: false,
        segments: segs,
    };
    @Expr {
        id: ast::DUMMY_NODE_ID,
        node: ExprPath(func_path),
        span: sp
    }
}

// create a slice from the vector of (path / routes / method)
fn create_slice_expr(vec: Vec<@Expr>, sp: Span) -> @Expr {
    @Expr {
        id: ast::DUMMY_NODE_ID,
        node: ExprVec(vec),
        span: sp
    }
}

fn expand_route(cx: &mut ExtCtxt,
                sp: Span,
                meta_item: @MetaItem,
                item: @Item)
                -> @Item {
    match item.node {
        ItemFn(_, _, _, _, _) => {
            get_route_attr_value(cx, sp, meta_item, item);
            item
        },
        _ => {
            cx.span_err(sp, "route attribute can only be used on functions");
            item
        }
    }
}

fn get_route_attr_value(cx: &mut ExtCtxt,
                        sp: Span,
                        meta_item: @MetaItem,
                        item: @Item) {
    match meta_item.node {
        MetaNameValue(_, ref l) => {
            match l.node {
                LitStr(ref s, _) => {
                    // check if the route already exist.
                    let route_attr = s.get().to_owned();
                    if !route_already_exist(&route_attr) {
                        insert_route(cx, item, route_attr);
                    } else {
                        cx.span_err(sp, "this route already exist for an other function")
                    }
                },
                _ => cx.span_err(sp, "route attribute can only use literal str")
            }
        }
        _ => cx.span_err(sp, "route attribute must be on the form: \
            #[route = \"my/route/\"] pub fn my_route()")
    }
}

// insert the route in the local data
fn insert_route(cx: &mut ExtCtxt,
                item: @Item,
                route_attr: ~str) {
    let v = local_data_get_or_init();
    // retrieve the complete path of the function
    let mut vec_ident = cx.mod_path.clone();
    // concatenate the name of the function
    vec_ident.push(item.ident);
    // insert the route and save
    let mut method = "GET".to_owned();
    let mut v: Vec<(Vec<Ident>, ~str, ~str)> = v.move_iter().filter(|&(ref v_i, _, ref m)| {
        if v_i == &vec_ident {
            method = m.clone();
            false
        } else { true }
    }).collect();
    v.push((vec_ident, route_attr, method));
    local_data::set(routes, v);
}

// check if a route is already defined for an other function
fn route_already_exist(route: &~str) -> bool {
    let v = local_data_get_or_init();
    v.iter().fold(false, |b, &(_, ref s, _)| { b || s == route })
}

fn expand_method(cx: &mut ExtCtxt, sp: Span, meta_item: @MetaItem, item: @Item) -> @Item {
    match item.node {
        ItemFn(_, _, _, _, _) => {
            get_method_attr_value(cx, sp, meta_item, item);
            item
        },
        _ => {
            cx.span_err(sp, "method attribute can only be used on functions");
            item
        }
    }
}

fn get_method_attr_value(cx: &mut ExtCtxt,
                         sp: Span,
                         meta_item: @MetaItem,
                         item: @Item) {
    match meta_item.node {
        MetaNameValue(_, ref l) => {
            match l.node {
                LitStr(ref s, _) => {
                    let method_attr = s.get();
                    if is_method_attribute_valid(method_attr) {
                        insert_method(cx, item, method_attr.to_owned());
                    } else {
                        cx.span_err(sp, "this method attribut don't exist. Here is a list of \
                            available attribute: [GET, POST]")
                    }
                },
                _ => cx.span_err(sp, "method attribute can only use literal str")
            }
        }
        _ => cx.span_err(sp, "method attribute must be on the form: \
            #[method = \"GET\"] pub fn my_route()")
    }
}

fn is_method_attribute_valid(attr: &str) -> bool {
    match attr {
        "GET" | "POST" => true,
        _ => false
    }
}

// insert the method in the local data
fn insert_method(cx: &mut ExtCtxt,
                 item: @Item,
                 method_attr: ~str) {
    let v = local_data_get_or_init();
    // retrieve the complete path of the function
    let mut vec_ident = cx.mod_path.clone();
    // concatenate the name of the function
    vec_ident.push(item.ident);
    // insert the route and save
    let mut route = "".to_owned();
    let mut v: Vec<(Vec<Ident>, ~str, ~str)> = v.move_iter().filter(|&(ref v_i, ref r, _)| {
        if v_i == &vec_ident {
            route = r.clone();
            false
        } else { true }
    }).collect();
    v.push((vec_ident, route, method_attr));
    local_data::set(routes, v);
}
<commit_msg>update to last Rust - local_data api changes<commit_after>// The MIT License (MIT)
//
// Copyright (c) 2014 Jeremy Letang
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

//! A syntax extension to handle route attributes

#![crate_id = "github.com/JeremyLetang/web_dispatcher#route_macros:0.0.1"]
#![desc = "route macros for Rust"]
#![license = "mit"]
#![crate_type = "rlib"]
#![crate_type = "dylib"]
#![experimental]
#![allow(missing_doc)]
#![feature(macro_registrar, managed_boxes, quote)]

extern crate syntax;

use std::local_data;

use syntax::ast;
use syntax::ast::Path;
use syntax::parse::token;
use syntax::ast::ExprPath;
use syntax::codemap::Span;
use syntax::ast::PathSegment;
use syntax::owned_slice::OwnedSlice;
use syntax::ast::{Ident,
                  TokenTree,
                  Expr,
                  Name,
                  ExprVec,
                  MetaItem,
                  MetaNameValue,
                  LitStr,
                  Item,
                  ItemFn};
use syntax::ext::base::{ExtCtxt,
                        MacResult,
                        SyntaxExtension,
                        BasicMacroExpander,
                        NormalTT,
                        ItemModifier,
                        MacExpr};

// Store routes in a local data (vector of path ident, associated route, method as a string)
static routes: local_data::Key<Vec<(Vec<Ident>, ~str, ~str)>> = &local_data::Key;

fn local_data_get_or_init() -> Vec<(Vec<Ident>, ~str,~str)> {
    match routes.get() {
        Some(v) =>  v.clone(),
        None => Vec::new()
    }
}

#[doc(hidden)]
#[macro_registrar]
pub fn registrar(register: |Name, SyntaxExtension|) {
    register(token::intern("route"), ItemModifier(expand_route));
    register(token::intern("method"), ItemModifier(expand_method));
    register(token::intern("routes"),
             NormalTT(box BasicMacroExpander {
                expander: expand_get_routes,
                span: None,
             },
             None));
}

fn expand_get_routes(cx: &mut ExtCtxt, sp: Span, _: &[TokenTree]) -> Box<MacResult> {
    let v = local_data_get_or_init();
    let v = v.iter().map(|&(ref f, ref s, ref m)| {
        let p = create_func_path_expr(f, sp);
        quote_expr!(&*cx, ($p, $s, $m))
    }).collect();
    let v = create_slice_expr(v, sp);
    MacExpr::new(quote_expr!(cx, Vec::from_slice($v.to_owned())))
    // MacExpr::new(quote_expr!(cx, &'static $v))
}

// create the path expression
fn create_func_path_expr(vec_ident: &Vec<Ident>, sp: Span) -> @Expr {
    // create the list of path segment from the idents
    let segs = vec_ident.iter().fold(Vec::new(), |mut v, &i| {
        v.push(PathSegment {
            identifier: i,
            lifetimes: Vec::new(),
            types: OwnedSlice::empty(),
        });
        v
    });
    // create the complete Path from the segments
    let func_path = Path {
        span: sp,
        global: false,
        segments: segs,
    };
    @Expr {
        id: ast::DUMMY_NODE_ID,
        node: ExprPath(func_path),
        span: sp
    }
}

// create a slice from the vector of (path / routes / method)
fn create_slice_expr(vec: Vec<@Expr>, sp: Span) -> @Expr {
    @Expr {
        id: ast::DUMMY_NODE_ID,
        node: ExprVec(vec),
        span: sp
    }
}

fn expand_route(cx: &mut ExtCtxt,
                sp: Span,
                meta_item: @MetaItem,
                item: @Item)
                -> @Item {
    match item.node {
        ItemFn(_, _, _, _, _) => {
            get_route_attr_value(cx, sp, meta_item, item);
            item
        },
        _ => {
            cx.span_err(sp, "route attribute can only be used on functions");
            item
        }
    }
}

fn get_route_attr_value(cx: &mut ExtCtxt,
                        sp: Span,
                        meta_item: @MetaItem,
                        item: @Item) {
    match meta_item.node {
        MetaNameValue(_, ref l) => {
            match l.node {
                LitStr(ref s, _) => {
                    // check if the route already exist.
                    let route_attr = s.get().to_owned();
                    if !route_already_exist(&route_attr) {
                        insert_route(cx, item, route_attr);
                    } else {
                        cx.span_err(sp, "this route already exist for an other function")
                    }
                },
                _ => cx.span_err(sp, "route attribute can only use literal str")
            }
        }
        _ => cx.span_err(sp, "route attribute must be on the form: \
            #[route = \"my/route/\"] pub fn my_route()")
    }
}

// insert the route in the local data
fn insert_route(cx: &mut ExtCtxt,
                item: @Item,
                route_attr: ~str) {
    let v = local_data_get_or_init();
    // retrieve the complete path of the function
    let mut vec_ident = cx.mod_path.clone();
    // concatenate the name of the function
    vec_ident.push(item.ident);
    // insert the route and save
    let mut method = "GET".to_owned();
    let mut v: Vec<(Vec<Ident>, ~str, ~str)> = v.move_iter().filter(|&(ref v_i, _, ref m)| {
        if v_i == &vec_ident {
            method = m.clone();
            false
        } else { true }
    }).collect();
    v.push((vec_ident, route_attr, method));
    routes.replace(Some(v));
}

// check if a route is already defined for an other function
fn route_already_exist(route: &~str) -> bool {
    let v = local_data_get_or_init();
    v.iter().fold(false, |b, &(_, ref s, _)| { b || s == route })
}

fn expand_method(cx: &mut ExtCtxt, sp: Span, meta_item: @MetaItem, item: @Item) -> @Item {
    match item.node {
        ItemFn(_, _, _, _, _) => {
            get_method_attr_value(cx, sp, meta_item, item);
            item
        },
        _ => {
            cx.span_err(sp, "method attribute can only be used on functions");
            item
        }
    }
}

fn get_method_attr_value(cx: &mut ExtCtxt,
                         sp: Span,
                         meta_item: @MetaItem,
                         item: @Item) {
    match meta_item.node {
        MetaNameValue(_, ref l) => {
            match l.node {
                LitStr(ref s, _) => {
                    let method_attr = s.get();
                    if is_method_attribute_valid(method_attr) {
                        insert_method(cx, item, method_attr.to_owned());
                    } else {
                        cx.span_err(sp, "this method attribut don't exist. Here is a list of \
                            available attribute: [GET, POST]")
                    }
                },
                _ => cx.span_err(sp, "method attribute can only use literal str")
            }
        }
        _ => cx.span_err(sp, "method attribute must be on the form: \
            #[method = \"GET\"] pub fn my_route()")
    }
}

fn is_method_attribute_valid(attr: &str) -> bool {
    match attr {
        "GET" | "POST" => true,
        _ => false
    }
}

// insert the method in the local data
fn insert_method(cx: &mut ExtCtxt,
                 item: @Item,
                 method_attr: ~str) {
    let v = local_data_get_or_init();
    // retrieve the complete path of the function
    let mut vec_ident = cx.mod_path.clone();
    // concatenate the name of the function
    vec_ident.push(item.ident);
    // insert the route and save
    let mut route = "".to_owned();
    let mut v: Vec<(Vec<Ident>, ~str, ~str)> = v.move_iter().filter(|&(ref v_i, ref r, _)| {
        if v_i == &vec_ident {
            route = r.clone();
            false
        } else { true }
    }).collect();
    v.push((vec_ident, route, method_attr));
    routes.replace(Some(v));
}
<|endoftext|>


<commit_before>// Copyright 2017 The gltf Library Developers
//
// 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 serde_json;
use std::fs::File;
use std::io;
use std::io::Read;
use std::path::Path;
use std::collections::HashMap;

pub mod accessor;
pub mod animation;
pub mod asset;
pub mod buffer;
pub mod camera;
pub mod image;
pub mod material;
pub mod mesh;
pub mod node;
pub mod program;
pub mod sampler;
pub mod scene;
pub mod shader;
pub mod skin;
pub mod technique;
pub mod texture;

#[derive(Debug)]
pub enum Error {
    /// Standard input / output error
    Io(io::Error),
    /// Failure when parsing a .gltf metadata file
    Parse(serde_json::error::Error),
}

impl From<io::Error> for Error {
    fn from(err: io::Error) -> Error {
        Error::Io(err)
    }
}

impl From<serde_json::error::Error> for Error {
    fn from(err: serde_json::error::Error) -> Error {
        Error::Parse(err)
    }
}

#[derive(Debug, Default, Deserialize, Serialize)]
pub struct Gltf {
    /// A dictionary object of accessor objects.
    ///
    /// The name of each accessor is an ID in the global glTF namespace that is
    /// used to reference the accessor. An accessor is a typed view into a
    /// bufferView.
    #[serde(default)]
    pub accessors: HashMap<String, accessor::Accessor>,

    /// A dictionary object of keyframe animation objects.
    ///
    /// The name of each animation is an ID in the global glTF namespace that is
    /// used to reference the animation.
    #[serde(default)]
    pub animation: HashMap<String, animation::Animation>,

    /// Metadata about the glTF asset.
    #[serde(default)]
    pub asset: asset::Asset,

    /// A dictionary object of buffer objects.
    ///
    /// The name of each buffer is an ID in the global glTF namespace that is
    /// used to reference the buffer. A buffer points to binary geometry,
    /// animation, or skins.
    #[serde(default)]
    pub buffers: HashMap<String, buffer::Buffer>,

    /// A dictionary object of bufferView objects.
    ///
    /// The name of each bufferView is an ID in the global glTF namespace that
    /// is used to reference the bufferView. A bufferView is a view into a
    /// buffer generally representing a subset of the buffer.
    #[serde(rename = "bufferViews")]
    #[serde(default)]
    pub buffer_views: HashMap<String, buffer::BufferView>,

    /// A dictionary object of camera objects.
    ///
    /// The name of each camera is an ID in the global glTF namespace that is
    /// used to reference the camera. A camera defines a projection matrix.
    #[serde(default)]
    pub cameras: HashMap<String, camera::Camera>,

    /// A dictionary object of image objects.
    ///
    /// The name of each image is an ID in the global glTF namespace that is
    /// used to reference the image. An image defines data used to create a
    /// texture.
    #[serde(default)]
    pub images: HashMap<String, image::Image>,

    /// A dictionary object of material objects.
    ///
    /// The name of each material is an ID in the global glTF namespace that is
    /// used to reference the material. A material defines the appearance of a
    /// primitive.
    #[serde(default)]
    pub materials: HashMap<String, material::Material>,

    /// A dictionary object of mesh objects.
    ///
    /// The name of each mesh is an ID in the global glTF namespace that is used
    /// to reference the mesh. A mesh is a set of primitives to be rendered.
    #[serde(default)]
    pub meshes: HashMap<String, mesh::Mesh>,

    /// A dictionary object of node objects in the node hierarchy.
    ///
    /// The name of each node is an ID in the global glTF namespace that is used
    /// to reference the node.
    #[serde(default)]
    pub nodes: HashMap<String, node::Node>,

    /// A dictionary object of shader program objects.
    ///
    /// The name of each program is an ID in the global glTF namespace that is
    /// used to reference the program.
    #[serde(default)]
    pub programs: HashMap<String, program::Program>,

    /// A dictionary object of sampler objects.
    ///
    /// The name of each sampler is an ID in the global glTF namespace that is
    /// used to reference the sampler. A sampler contains properties for texture
    /// filtering and wrapping modes.
    #[serde(default)]
    pub samplers: HashMap<String, sampler::Sampler>,

    /// The ID of the default scene.
    pub scene: Option<String>,

    /// A dictionary object of scene objects.
    ///
    /// The name of each scene is an ID in the global glTF namespace that is
    /// used to reference the scene.
    #[serde(default)]
    pub scenes: HashMap<String, scene::Scene>,

    /// A dictionary object of shader objects.
    ///
    /// The name of each shader is an ID in the global glTF namespace that is
    /// used to reference the shader.
    #[serde(default)]
    pub shaders: HashMap<String, shader::Shader>,

    /// A dictionary object of skin objects.
    ///
    /// The name of each skin is an ID in the global glTF namespace that is used
    /// to reference the skin. A skin is defined by joints and matrices.
    #[serde(default)]
    pub skins: HashMap<String, skin::Skin>,

    /// A dictionary object of technique objects.
    ///
    /// The name of each technique is an ID in the global glTF namespace that is
    /// used to reference the technique. A technique is a template for a
    /// material appearance.
    #[serde(default)]
    pub techniques: HashMap<String, technique::Technique>,

    /// A dictionary object of texture objects.
    ///
    /// The name of each texture is an ID in the global glTF namespace that is
    /// used to reference the texture.
    #[serde(default)]
    pub textures: HashMap<String, texture::Texture>,

    // TODO: extension
    // TODO: extras
}

impl Gltf {
    pub fn open<P: AsRef<Path>>(path: P) -> Result<Self, Error> {
        let mut file = File::open(path)?;
        let mut json = String::new();
        file.read_to_string(&mut json)?;

        serde_json::from_str(&json).map_err(|cause| Error::Parse(cause))
    }
}
<commit_msg>Made `Gltf::open` leaner<commit_after>// Copyright 2017 The gltf Library Developers
//
// 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 serde_json;
use std::fs::File;
use std::io;
use std::io::Read;
use std::path::Path;
use std::collections::HashMap;

pub mod accessor;
pub mod animation;
pub mod asset;
pub mod buffer;
pub mod camera;
pub mod image;
pub mod material;
pub mod mesh;
pub mod node;
pub mod program;
pub mod sampler;
pub mod scene;
pub mod shader;
pub mod skin;
pub mod technique;
pub mod texture;

#[derive(Debug)]
pub enum Error {
    /// Standard input / output error
    Io(io::Error),
    /// Failure when parsing a .gltf metadata file
    Parse(serde_json::error::Error),
}

impl From<io::Error> for Error {
    fn from(err: io::Error) -> Error {
        Error::Io(err)
    }
}

impl From<serde_json::error::Error> for Error {
    fn from(err: serde_json::error::Error) -> Error {
        Error::Parse(err)
    }
}

#[derive(Debug, Default, Deserialize, Serialize)]
pub struct Gltf {
    /// A dictionary object of accessor objects.
    ///
    /// The name of each accessor is an ID in the global glTF namespace that is
    /// used to reference the accessor. An accessor is a typed view into a
    /// bufferView.
    #[serde(default)]
    pub accessors: HashMap<String, accessor::Accessor>,

    /// A dictionary object of keyframe animation objects.
    ///
    /// The name of each animation is an ID in the global glTF namespace that is
    /// used to reference the animation.
    #[serde(default)]
    pub animation: HashMap<String, animation::Animation>,

    /// Metadata about the glTF asset.
    #[serde(default)]
    pub asset: asset::Asset,

    /// A dictionary object of buffer objects.
    ///
    /// The name of each buffer is an ID in the global glTF namespace that is
    /// used to reference the buffer. A buffer points to binary geometry,
    /// animation, or skins.
    #[serde(default)]
    pub buffers: HashMap<String, buffer::Buffer>,

    /// A dictionary object of bufferView objects.
    ///
    /// The name of each bufferView is an ID in the global glTF namespace that
    /// is used to reference the bufferView. A bufferView is a view into a
    /// buffer generally representing a subset of the buffer.
    #[serde(rename = "bufferViews")]
    #[serde(default)]
    pub buffer_views: HashMap<String, buffer::BufferView>,

    /// A dictionary object of camera objects.
    ///
    /// The name of each camera is an ID in the global glTF namespace that is
    /// used to reference the camera. A camera defines a projection matrix.
    #[serde(default)]
    pub cameras: HashMap<String, camera::Camera>,

    /// A dictionary object of image objects.
    ///
    /// The name of each image is an ID in the global glTF namespace that is
    /// used to reference the image. An image defines data used to create a
    /// texture.
    #[serde(default)]
    pub images: HashMap<String, image::Image>,

    /// A dictionary object of material objects.
    ///
    /// The name of each material is an ID in the global glTF namespace that is
    /// used to reference the material. A material defines the appearance of a
    /// primitive.
    #[serde(default)]
    pub materials: HashMap<String, material::Material>,

    /// A dictionary object of mesh objects.
    ///
    /// The name of each mesh is an ID in the global glTF namespace that is used
    /// to reference the mesh. A mesh is a set of primitives to be rendered.
    #[serde(default)]
    pub meshes: HashMap<String, mesh::Mesh>,

    /// A dictionary object of node objects in the node hierarchy.
    ///
    /// The name of each node is an ID in the global glTF namespace that is used
    /// to reference the node.
    #[serde(default)]
    pub nodes: HashMap<String, node::Node>,

    /// A dictionary object of shader program objects.
    ///
    /// The name of each program is an ID in the global glTF namespace that is
    /// used to reference the program.
    #[serde(default)]
    pub programs: HashMap<String, program::Program>,

    /// A dictionary object of sampler objects.
    ///
    /// The name of each sampler is an ID in the global glTF namespace that is
    /// used to reference the sampler. A sampler contains properties for texture
    /// filtering and wrapping modes.
    #[serde(default)]
    pub samplers: HashMap<String, sampler::Sampler>,

    /// The ID of the default scene.
    pub scene: Option<String>,

    /// A dictionary object of scene objects.
    ///
    /// The name of each scene is an ID in the global glTF namespace that is
    /// used to reference the scene.
    #[serde(default)]
    pub scenes: HashMap<String, scene::Scene>,

    /// A dictionary object of shader objects.
    ///
    /// The name of each shader is an ID in the global glTF namespace that is
    /// used to reference the shader.
    #[serde(default)]
    pub shaders: HashMap<String, shader::Shader>,

    /// A dictionary object of skin objects.
    ///
    /// The name of each skin is an ID in the global glTF namespace that is used
    /// to reference the skin. A skin is defined by joints and matrices.
    #[serde(default)]
    pub skins: HashMap<String, skin::Skin>,

    /// A dictionary object of technique objects.
    ///
    /// The name of each technique is an ID in the global glTF namespace that is
    /// used to reference the technique. A technique is a template for a
    /// material appearance.
    #[serde(default)]
    pub techniques: HashMap<String, technique::Technique>,

    /// A dictionary object of texture objects.
    ///
    /// The name of each texture is an ID in the global glTF namespace that is
    /// used to reference the texture.
    #[serde(default)]
    pub textures: HashMap<String, texture::Texture>,

    // TODO: extension
    // TODO: extras
}

impl Gltf {
    fn open_impl(path: &Path) -> Result<Self, Error> {
        let mut file = File::open(path)?;
        let mut json = String::new();
        file.read_to_string(&mut json)?;
        serde_json::from_str(&json).map_err(|err| Error::Parse(err))
    }

    pub fn open<P: AsRef<Path>>(path: P) -> Result<Self, Error> {
        Gltf::open_impl(path.as_ref())
    }
}
<|endoftext|>


<commit_before>use std::ops::{Deref, DerefMut};
use std::mem;
use std::collections::HashMap;
use property::Property;

use std::rc::Rc;
use edge::{RawEdge,Edge};
use graph::TraversableToVertex;

/*
* storing in & out edges seperately should cut down on the number of "things" i have to traverse
* if a vertex has 10k edges (5k in and out 5k out) then doing something like*   g(v).outV()
* should be ok
*/
#[derive(Debug, Clone)]
pub struct RawVertex {
    pub id: i64,
    pub properties: HashMap<String, Property>,
    // pointers on both sides, yay
    pub out_edges: Vec<Edge>,
    pub in_edges:  Vec<Edge>,
}

impl RawVertex {
    pub fn new(id: i64) -> Rc<Box<RawVertex>> {
        let mut props  = HashMap::new();
        Rc::new(Box::new(RawVertex{id:id,
                        properties: props,
                        out_edges: Vec::new(),
                        in_edges: Vec::new()}))
    }
}

#[derive(Clone, Debug)]
pub struct Vertex {
    pub id: i64,
    pub v: Rc<Box<RawVertex>>,
}

impl Vertex {

	pub fn new(id: i64) -> Vertex {
        let raw = RawVertex::new(id);
        Vertex{id:raw.id, v:raw}
	}

    pub fn add_edge(&mut self, to_vertex: &mut Vertex, label: &str) -> Edge {
        println!("add_edge() with label {}", label);

        // let in_vertex =  &mut *(self.v);
        // let out_vertex = &mut *(to_vertex.v);

        // why do i need the raw shit?
        let in_vertex = self;
        let out_vertex = to_vertex;
        // create the edge
        println!("adding vertex of edge {}", label.to_string());

        let e = Box::new(RawEdge{from_vertex: self.clone(),
                                 to_vertex: to_vertex.clone(),
                                 label: label.to_string() });

        // keep it on the heap but manage it myself
        let edge = Edge::new(in_vertex.clone(),
                             out_vertex.clone(),
                             label.to_string());

        in_vertex.out_edges.push(edge.clone());
        out_vertex.in_edges.push(edge.clone());
        edge
    }

    // TODO switch to accepting a &str
    pub fn set_property(&mut self, field: &str, value: Property) {
        self.properties.insert(field.to_string(), value);
    }

    pub fn get_property(&self, field:&str) -> Option<&Property> {
        self.properties.get(&field.to_string())
    }

	pub fn outE(&self) -> Vec<Edge> {
		let mut result = Vec::new();
		for &edge in self.out_edges.iter() {
			result.push(edge.clone());
		}
		result
	}

	pub fn inE(&self) -> Vec<Edge> {
		let mut result = Vec::new();
		for &edge in self.in_edges.iter() {
			result.push(edge.clone());
		}
		result
	}

}

impl TraversableToVertex for Vertex {
	/* basic traversal operations
	   cannot be chained
	   look at traversal.rs for breadth first chaining
	*/

	/* returns all the outV vertex proxies
	   mainly for internal use
	*/
	fn outV(&self, labels: &[&str]) -> Vec<Vertex> {
		let mut result = Vec::new();

        // convert our labels to a vector of strings
        let mut labels_as_strings : Vec<String> = Vec::new();

        for l in labels {
            let s = l.to_string();
            labels_as_strings.push(l.to_string());
        }

		for &edge in self.out_edges.iter() {
            if labels.is_empty() || labels_as_strings.contains(&edge.label) {
				result.push(edge.to_vertex.clone());
            }
		}
		result
	}
	fn inV(&self) -> Vec<Vertex> {
		let mut result = Vec::new();
		for &edge in self.in_edges.iter() {
			result.push(edge.from_vertex.clone());
		}
		result
	}

}

impl Deref for Vertex {
    type Target = Rc<Box<RawVertex>>;

    fn deref<'a>(&'a self) -> &'a Rc<Box<RawVertex>> {
        self.v
    }
}

impl DerefMut for Vertex {
    fn deref_mut<'a>(&'a mut self) -> &'a mut Rc<Box<RawVertex>> {
        self.v
    }
}
<commit_msg>fixed ref return<commit_after>use std::ops::{Deref, DerefMut};
use std::mem;
use std::collections::HashMap;
use property::Property;

use std::rc::Rc;
use edge::{RawEdge,Edge};
use graph::TraversableToVertex;

/*
* storing in & out edges seperately should cut down on the number of "things" i have to traverse
* if a vertex has 10k edges (5k in and out 5k out) then doing something like*   g(v).outV()
* should be ok
*/
#[derive(Debug, Clone)]
pub struct RawVertex {
    pub id: i64,
    pub properties: HashMap<String, Property>,
    // pointers on both sides, yay
    pub out_edges: Vec<Edge>,
    pub in_edges:  Vec<Edge>,
}

impl RawVertex {
    pub fn new(id: i64) -> Rc<Box<RawVertex>> {
        let mut props  = HashMap::new();
        Rc::new(Box::new(RawVertex{id:id,
                        properties: props,
                        out_edges: Vec::new(),
                        in_edges: Vec::new()}))
    }
}

#[derive(Clone, Debug)]
pub struct Vertex {
    pub id: i64,
    pub v: Rc<Box<RawVertex>>,
}

impl Vertex {

	pub fn new(id: i64) -> Vertex {
        let raw = RawVertex::new(id);
        Vertex{id:raw.id, v:raw}
	}

    pub fn add_edge(&mut self, to_vertex: &mut Vertex, label: &str) -> Edge {
        println!("add_edge() with label {}", label);

        // let in_vertex =  &mut *(self.v);
        // let out_vertex = &mut *(to_vertex.v);

        // why do i need the raw shit?
        let in_vertex = self;
        let out_vertex = to_vertex;
        // create the edge
        println!("adding vertex of edge {}", label.to_string());

        let e = Box::new(RawEdge{from_vertex: self.clone(),
                                 to_vertex: to_vertex.clone(),
                                 label: label.to_string() });

        // keep it on the heap but manage it myself
        let edge = Edge::new(in_vertex.clone(),
                             out_vertex.clone(),
                             label.to_string());

        in_vertex.out_edges.push(edge.clone());
        out_vertex.in_edges.push(edge.clone());
        edge
    }

    // TODO switch to accepting a &str
    pub fn set_property(&mut self, field: &str, value: Property) {
        self.properties.insert(field.to_string(), value);
    }

    pub fn get_property(&self, field:&str) -> Option<&Property> {
        self.properties.get(&field.to_string())
    }

	pub fn outE(&self) -> Vec<Edge> {
		let mut result = Vec::new();
		for &edge in self.out_edges.iter() {
			result.push(edge.clone());
		}
		result
	}

	pub fn inE(&self) -> Vec<Edge> {
		let mut result = Vec::new();
		for &edge in self.in_edges.iter() {
			result.push(edge.clone());
		}
		result
	}

}

impl TraversableToVertex for Vertex {
	/* basic traversal operations
	   cannot be chained
	   look at traversal.rs for breadth first chaining
	*/

	/* returns all the outV vertex proxies
	   mainly for internal use
	*/
	fn outV(&self, labels: &[&str]) -> Vec<Vertex> {
		let mut result = Vec::new();

        // convert our labels to a vector of strings
        let mut labels_as_strings : Vec<String> = Vec::new();

        for l in labels {
            let s = l.to_string();
            labels_as_strings.push(l.to_string());
        }

		for &edge in self.out_edges.iter() {
            if labels.is_empty() || labels_as_strings.contains(&edge.label) {
				result.push(edge.to_vertex.clone());
            }
		}
		result
	}
	fn inV(&self) -> Vec<Vertex> {
		let mut result = Vec::new();
		for &edge in self.in_edges.iter() {
			result.push(edge.from_vertex.clone());
		}
		result
	}

}

impl Deref for Vertex {
    type Target = Rc<Box<RawVertex>>;

    fn deref<'a>(&'a self) -> &'a Rc<Box<RawVertex>> {
        &self.v
    }
}

impl DerefMut for Vertex {
    fn deref_mut<'a>(&'a mut self) -> &'a mut Rc<Box<RawVertex>> {
        self.v
    }
}
<|endoftext|>