<commit_before>use luminance::shader::program::Program as LProgram;
use luminance::shader::stage::{Stage, StageError, Type};
use std::fmt;
use std::fs::File;
use std::hash;
use std::io::{BufRead, BufReader};
use std::marker::PhantomData;
use std::ops::Deref;
use std::path::{Path, PathBuf};

pub use luminance::shader::program::{ProgramError, Uniform, Uniformable, UniformBuilder,
                                     UniformInterface, UniformWarning};
use luminance::vertex::Vertex;

use sys::resource::{CacheKey, Load, LoadError, LoadResult, Store, StoreKey};

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

/// Create a new luminance::Program from a set of shader strings.
fn new_program<In, Out, Uni>(tcs_src: &str,
                             tes_src: &str,
                             vs_src: &str,
                             gs_src: &str,
                             fs_src: &str)
                             -> Result<(LProgram<In, Out, Uni>, Vec<UniformWarning>), ProgramError>
    where In: Vertex,
          Uni: UniformInterface {
  let stages = compile_stages(tcs_src, tes_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 {
          LProgram::new(Some((&tcs, &tes)), &vs, Some(&gs), &fs)
        } else {
          LProgram::new(Some((&tcs, &tes)), &vs, None, &fs)
        }
      } else if let Some(gs) = gs {
        LProgram::new(None, &vs, Some(&gs), &fs)
      } else {
        LProgram::new(None, &vs, None, &fs)
      }
    },
    Err(stage_error) => {
      Err(ProgramError::LinkFailed(format!("{:?}", stage_error)))
    }
  }
}

/// Take raw shader sources and turn them into stages.
fn compile_stages(tcs_src: &str, tes_src: &str, vs_src: &str, gs_src: &str, fs_src: &str) -> Result<(Option<(Stage, Stage)>, Stage, Option<Stage>, Stage), StageError> {
  let tess = if !tcs_src.is_empty() && !tes_src.is_empty() {
    Some((Stage::new(Type::TessellationControlShader, tcs_src)?,
          Stage::new(Type::TessellationEvaluationShader, tes_src)?))
  } else {
    None
  };

  let vs = Stage::new(Type::VertexShader, vs_src)?;
  let gs = if !gs_src.is_empty() { Some(Stage::new(Type::GeometryShader, gs_src)?) } else { None };
  let fs = Stage::new(Type::FragmentShader, fs_src)?;

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

/// Shader program.
pub struct Program<In, Out, Uni> {
  program: LProgram<In, Out, Uni>
}

impl<In, Out, Uni> Deref for Program<In, Out, Uni> {
  type Target = LProgram<In, Out, Uni>;

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

// Current stage dispatch.
enum CurrentStage {
  VS,
  FS,
  GS,
  TCS,
  TES
}

struct ShaderSources {
  tcs_src: String,
  tes_src: String,
  vs_src: String,
  gs_src: String,
  fs_src: String
}

// Annotate a line with its original line number.
fn annotate_line_src(src: &mut String, line: &str, line_nb: usize) {
  *src += &format!("#line {}\n{}\n", line_nb, line);
}

// Split a single source into several shader sources.
fn split_shader_stages_sources<R>(buffered: R) -> Result<ShaderSources, LoadError> where R: BufRead {
  let mut current_stage: Option<CurrentStage> = None;
  let mut tcs_src = String::new();
  let mut tes_src = String::new();
  let mut vs_src = String::new();
  let mut gs_src = String::new();
  let mut fs_src = String::new();

  for (line_nb, line) in buffered.lines().enumerate() {
    let line_nb = line_nb + 1;
    let line = line.unwrap();
    let trimmed = line.trim();

    if trimmed.starts_with("#vs") {
      if !vs_src.is_empty() {
        return Err(LoadError::ParseFailed(format!("(line {}) several #vs sections", line_nb)));
      }

      info!("  found a vertex shader");

      current_stage = Some(CurrentStage::VS);
      continue;
    } else if trimmed.starts_with("#fs") {
      if !fs_src.is_empty() {
        return Err(LoadError::ParseFailed(format!("(line {}) several #fs sections", line_nb)));
      }

      info!("  found a fragment shader");

      current_stage = Some(CurrentStage::FS);
      continue;
    } else if trimmed.starts_with("#gs") {
      if !gs_src.is_empty() {
        return Err(LoadError::ParseFailed(format!("(line {}) several #gs sections", line_nb)));
      }

      info!("  found a geometry shader");

      current_stage = Some(CurrentStage::GS);
      continue;
    } else if trimmed.starts_with("#tcs") {
      if !tcs_src.is_empty() {
        return Err(LoadError::ParseFailed(format!("(line {}) several #tcs sections", line_nb)));
      }

      info!("  found a tessellation control shader");

      current_stage = Some(CurrentStage::TCS);
      continue;
    } else if trimmed.starts_with("#tes") {
      if !tes_src.is_empty() {
        return Err(LoadError::ParseFailed(format!("(line {}) several #tes sections", line_nb)));
      }

      info!("  found a tessellation evaluation shader");

      current_stage = Some(CurrentStage::TES);
      continue;
    } else if current_stage.is_none() && !trimmed.is_empty() && !trimmed.starts_with("//") && !trimmed.starts_with("\n") {
      return Err(LoadError::ParseFailed(format!("(line {}) not in a shader stage nor a comment", line_nb)));
    }

    match current_stage {
      Some(CurrentStage::TCS) => {
        annotate_line_src(&mut tcs_src, trimmed, line_nb);
      },
      Some(CurrentStage::TES) => {
        annotate_line_src(&mut tes_src, trimmed, line_nb);
      },
      Some(CurrentStage::VS) => {
        annotate_line_src(&mut vs_src, trimmed, line_nb);
      },
      Some(CurrentStage::GS) => {
        annotate_line_src(&mut gs_src, trimmed, line_nb);
      },
      Some(CurrentStage::FS) => {
        annotate_line_src(&mut fs_src, trimmed, line_nb);
      },
      None => {}
    }
  }

  Ok(ShaderSources {
    tcs_src: tcs_src,
    tes_src: tes_src,
    vs_src: vs_src,
    gs_src: gs_src,
    fs_src: fs_src
  })
}

impl<In, Out, Uni> Program<In, Out, Uni> where In: Vertex, Uni: UniformInterface {
  /// Create a `Program` from a `BufRead`.
  pub fn from_bufread<R>(buffered: R) -> Result<Self, LoadError> where R: BufRead {
    let sources = split_shader_stages_sources(buffered)?;
    let (program, warnings) = new_program(&sources.tcs_src,
                                          &sources.tes_src,
                                          &sources.vs_src,
                                          &sources.gs_src,
                                          &sources.fs_src)
      .map_err(|e| LoadError::ConversionFailed(format!("{:#?}", e)))?;

    // check for semantic errors
    for warning in warnings {
      warn!("uniform warning: {:?}", warning);
    }

    Ok(Program {
      program: program
    })
  }

  /// Create a `Program` from a string – you can for instance use `str` or `String`.
  pub fn from_str<'a, S>(s: S) -> Result<Self, LoadError> where S: Into<&'a str> {
    Self::from_bufread(s.into().as_bytes())
  }
}

#[derive(Eq, PartialEq)]
pub struct ProgramKey<In, Out, Uni> {
  pub key: String,
  _in: PhantomData<*const In>,
  _out: PhantomData<*const Out>,
  _uni: PhantomData<*const Uni>
}

impl<In, Out, Uni> ProgramKey<In, Out, Uni> {
  pub fn new(key: &str) -> Self {
    ProgramKey {
      key: key.to_owned(),
      _in: PhantomData,
      _out: PhantomData,
      _uni: PhantomData
    }
  }
}

impl<In, Out, Uni> Clone for ProgramKey<In, Out, Uni> {
  fn clone(&self) -> Self {
    ProgramKey {
      key: self.key.clone(),
      ..*self
    }
  }
}

impl<In, Out, Uni> fmt::Debug for ProgramKey<In, Out, Uni> {
  fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
    self.key.fmt(f)
  }
}

impl<In, Out, Uni> hash::Hash for ProgramKey<In, Out, Uni> {
  fn hash<H>(&self, hasher: &mut H) where H: hash::Hasher {
    self.key.hash(hasher)
  }
}

impl<'a, In, Out, Uni> From<&'a str> for ProgramKey<In, Out, Uni> {
  fn from(key: &str) -> Self {
    ProgramKey::new(key)
  }
}

impl<In, Out, Uni> CacheKey for ProgramKey<In, Out, Uni>
    where In: 'static,
          Out: 'static,
          Uni: 'static {
  type Target = Program<In, Out, Uni>;
}

impl<In, Out, Uni> StoreKey for ProgramKey<In, Out, Uni>
    where In: 'static,
          Out: 'static,
          Uni: 'static {
  fn key_to_path(&self) -> PathBuf {
    self.key.clone().into()
  }
}

impl<In, Out, Uni> Load for Program<In, Out, Uni>
    where In: 'static + Vertex,
          Out: 'static,
          Uni: 'static + UniformInterface {
  fn load<P>(path: P, _: &mut Store) -> Result<LoadResult<Self>, LoadError> where P: AsRef<Path> {
    let path = path.as_ref();

    enum CurrentStage {
      VS,
      FS,
      GS,
      TCS,
      TES
    }

    fn annotate_line_src(src: &mut String, line: &str, line_nb: usize) {
      *src += &format!("#line {}\n{}\n", line_nb, line);
    }

    match File::open(path) {
      Ok(fh) => {
        let buffered = BufReader::new(fh);
        let mut tcs_src = String::new();
        let mut tes_src = String::new();
        let mut vs_src = String::new();
        let mut gs_src = String::new();
        let mut fs_src = String::new();
        let mut current_stage: Option<CurrentStage> = None;

        for (line_nb, line) in buffered.lines().enumerate() {
          let line_nb = line_nb + 1;
          let line = line.unwrap();
          let trimmed = line.trim();

          if trimmed.starts_with("#vs") {
            if !vs_src.is_empty() {
              return Err(LoadError::ParseFailed(format!("(line {}) several #vs sections", line_nb)));
            }

            info!("  found a vertex shader");

            current_stage = Some(CurrentStage::VS);
            continue;
          } else if trimmed.starts_with("#fs") {
            if !fs_src.is_empty() {
              return Err(LoadError::ParseFailed(format!("(line {}) several #fs sections", line_nb)));
            }

            info!("  found a fragment shader");

            current_stage = Some(CurrentStage::FS);
            continue;
          } else if trimmed.starts_with("#gs") {
            if !gs_src.is_empty() {
              return Err(LoadError::ParseFailed(format!("(line {}) several #gs sections", line_nb)));
            }

            info!("  found a geometry shader");

            current_stage = Some(CurrentStage::GS);
            continue;
          } else if trimmed.starts_with("#tcs") {
            if !tcs_src.is_empty() {
              return Err(LoadError::ParseFailed(format!("(line {}) several #tcs sections", line_nb)));
            }

            info!("  found a tessellation control shader");

            current_stage = Some(CurrentStage::TCS);
            continue;
          } else if trimmed.starts_with("#tes") {
            if !tes_src.is_empty() {
              return Err(LoadError::ParseFailed(format!("(line {}) several #tes sections", line_nb)));
            }

            info!("  found a tessellation evaluation shader");

            current_stage = Some(CurrentStage::TES);
            continue;
          } else if current_stage.is_none() && !trimmed.is_empty() && !trimmed.starts_with("//") && !trimmed.starts_with("\n") {
            return Err(LoadError::ParseFailed(format!("(line {}) not in a shader stage nor a comment", line_nb)));
          }

          match current_stage {
            Some(CurrentStage::VS) => {
              annotate_line_src(&mut vs_src, trimmed, line_nb);
            },
            Some(CurrentStage::FS) => {
              annotate_line_src(&mut fs_src, trimmed, line_nb);
            },
            Some(CurrentStage::GS) => {
              annotate_line_src(&mut gs_src, trimmed, line_nb);
            },
            Some(CurrentStage::TCS) => {
              annotate_line_src(&mut tcs_src, trimmed, line_nb);
            },
            Some(CurrentStage::TES) => {
              annotate_line_src(&mut tes_src, trimmed, line_nb);
            },
            None => {}
          }
        }

        let (program, warnings) = new_program(&tcs_src, &tes_src, &vs_src, &gs_src, &fs_src)
          .map_err(|e| LoadError::ConversionFailed(format!("{:#?}", e)))?;

        // check for semantic errors
        for warning in warnings {
          warn!("uniform warning: {:?}", warning);
        }

        Ok(
          (Program {
            program: program
          }).into()
        )
      },
      Err(_) => Err(LoadError::FileNotFound(path.to_owned()))
    }
  }
}

pub trait UnwrapOrUnbound<T> {
  fn unwrap_or_unbound(self, builder: &UniformBuilder, warnings: &mut Vec<UniformWarning>) -> Uniform<T> where T: Uniformable;
}

impl<T> UnwrapOrUnbound<T> for Result<Uniform<T>, UniformWarning> {
  fn unwrap_or_unbound(self, builder: &UniformBuilder, warnings: &mut Vec<UniformWarning>) -> Uniform<T> where T: Uniformable {
    self.unwrap_or_else(|w| {
      warnings.push(w);
      builder.unbound()
    })
  }
}

<commit_msg>Doc about programs.<commit_after>//! Shader programs.
//!
//! A shader program is a piece of code that runs on GPU, consuming vertices, primitives, geometry
//! patches and fragments and output into a framebuffer colors and depth information.
//!
//! Shaders are abstracted by *shader modules* – see the `render::shader::module` module for further
//! details. You are not supposed to write *shader programs* directly, but instead, you write one or
//! several *shader modules* and are automatically handed a *shader program* based on the analysis
//! of the *shader modules* you wrote and how you combined them.
use luminance::shader::program::Program as LProgram;
pub use luminance::shader::program::{ProgramError, Uniform, Uniformable, UniformBuilder,
                                     UniformInterface, UniformWarning};
use luminance::shader::stage::{Stage, StageError, Type};
use luminance::vertex::Vertex;
use std::fmt;
use std::fs::File;
use std::hash;
use std::io::{BufRead, BufReader};
use std::marker::PhantomData;
use std::ops::Deref;
use std::path::{Path, PathBuf};

use render::shader::module::{Module, ModuleKey};
use sys::resource::{CacheKey, Load, LoadError, LoadResult, Store, StoreKey};

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

/// Create a new luminance::Program from a set of shader strings.
fn new_program<In, Out, Uni>(tcs_src: &str,
                             tes_src: &str,
                             vs_src: &str,
                             gs_src: &str,
                             fs_src: &str)
                             -> Result<(LProgram<In, Out, Uni>, Vec<UniformWarning>), ProgramError>
    where In: Vertex,
          Uni: UniformInterface {
  let stages = compile_stages(tcs_src, tes_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 {
          LProgram::new(Some((&tcs, &tes)), &vs, Some(&gs), &fs)
        } else {
          LProgram::new(Some((&tcs, &tes)), &vs, None, &fs)
        }
      } else if let Some(gs) = gs {
        LProgram::new(None, &vs, Some(&gs), &fs)
      } else {
        LProgram::new(None, &vs, None, &fs)
      }
    },
    Err(stage_error) => {
      Err(ProgramError::LinkFailed(format!("{:?}", stage_error)))
    }
  }
}

/// Take raw shader sources and turn them into stages.
fn compile_stages(tcs_src: &str, tes_src: &str, vs_src: &str, gs_src: &str, fs_src: &str) -> Result<(Option<(Stage, Stage)>, Stage, Option<Stage>, Stage), StageError> {
  let tess = if !tcs_src.is_empty() && !tes_src.is_empty() {
    Some((Stage::new(Type::TessellationControlShader, tcs_src)?,
          Stage::new(Type::TessellationEvaluationShader, tes_src)?))
  } else {
    None
  };

  let vs = Stage::new(Type::VertexShader, vs_src)?;
  let gs = if !gs_src.is_empty() { Some(Stage::new(Type::GeometryShader, gs_src)?) } else { None };
  let fs = Stage::new(Type::FragmentShader, fs_src)?;

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

/// Shader program.
pub struct Program<In, Out, Uni> {
  program: LProgram<In, Out, Uni>
}

impl<In, Out, Uni> Deref for Program<In, Out, Uni> {
  type Target = LProgram<In, Out, Uni>;

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

// Current stage dispatch.
enum CurrentStage {
  VS,
  FS,
  GS,
  TCS,
  TES
}

struct ShaderSources {
  tcs_src: String,
  tes_src: String,
  vs_src: String,
  gs_src: String,
  fs_src: String
}

// Annotate a line with its original line number.
fn annotate_line_src(src: &mut String, line: &str, line_nb: usize) {
  *src += &format!("#line {}\n{}\n", line_nb, line);
}

// Split a single source into several shader sources.
fn split_shader_stages_sources<R>(buffered: R) -> Result<ShaderSources, LoadError> where R: BufRead {
  let mut current_stage: Option<CurrentStage> = None;
  let mut tcs_src = String::new();
  let mut tes_src = String::new();
  let mut vs_src = String::new();
  let mut gs_src = String::new();
  let mut fs_src = String::new();

  for (line_nb, line) in buffered.lines().enumerate() {
    let line_nb = line_nb + 1;
    let line = line.unwrap();
    let trimmed = line.trim();

    if trimmed.starts_with("#vs") {
      if !vs_src.is_empty() {
        return Err(LoadError::ParseFailed(format!("(line {}) several #vs sections", line_nb)));
      }

      info!("  found a vertex shader");

      current_stage = Some(CurrentStage::VS);
      continue;
    } else if trimmed.starts_with("#fs") {
      if !fs_src.is_empty() {
        return Err(LoadError::ParseFailed(format!("(line {}) several #fs sections", line_nb)));
      }

      info!("  found a fragment shader");

      current_stage = Some(CurrentStage::FS);
      continue;
    } else if trimmed.starts_with("#gs") {
      if !gs_src.is_empty() {
        return Err(LoadError::ParseFailed(format!("(line {}) several #gs sections", line_nb)));
      }

      info!("  found a geometry shader");

      current_stage = Some(CurrentStage::GS);
      continue;
    } else if trimmed.starts_with("#tcs") {
      if !tcs_src.is_empty() {
        return Err(LoadError::ParseFailed(format!("(line {}) several #tcs sections", line_nb)));
      }

      info!("  found a tessellation control shader");

      current_stage = Some(CurrentStage::TCS);
      continue;
    } else if trimmed.starts_with("#tes") {
      if !tes_src.is_empty() {
        return Err(LoadError::ParseFailed(format!("(line {}) several #tes sections", line_nb)));
      }

      info!("  found a tessellation evaluation shader");

      current_stage = Some(CurrentStage::TES);
      continue;
    } else if current_stage.is_none() && !trimmed.is_empty() && !trimmed.starts_with("//") && !trimmed.starts_with("\n") {
      return Err(LoadError::ParseFailed(format!("(line {}) not in a shader stage nor a comment", line_nb)));
    }

    match current_stage {
      Some(CurrentStage::TCS) => {
        annotate_line_src(&mut tcs_src, trimmed, line_nb);
      },
      Some(CurrentStage::TES) => {
        annotate_line_src(&mut tes_src, trimmed, line_nb);
      },
      Some(CurrentStage::VS) => {
        annotate_line_src(&mut vs_src, trimmed, line_nb);
      },
      Some(CurrentStage::GS) => {
        annotate_line_src(&mut gs_src, trimmed, line_nb);
      },
      Some(CurrentStage::FS) => {
        annotate_line_src(&mut fs_src, trimmed, line_nb);
      },
      None => {}
    }
  }

  Ok(ShaderSources {
    tcs_src: tcs_src,
    tes_src: tes_src,
    vs_src: vs_src,
    gs_src: gs_src,
    fs_src: fs_src
  })
}

impl<In, Out, Uni> Program<In, Out, Uni> where In: Vertex, Uni: UniformInterface {
  /// Create a `Program` from a `BufRead`.
  pub fn from_bufread<R>(buffered: R) -> Result<Self, LoadError> where R: BufRead {
    let sources = split_shader_stages_sources(buffered)?;
    let (program, warnings) = new_program(&sources.tcs_src,
                                          &sources.tes_src,
                                          &sources.vs_src,
                                          &sources.gs_src,
                                          &sources.fs_src)
      .map_err(|e| LoadError::ConversionFailed(format!("{:#?}", e)))?;

    // check for semantic errors
    for warning in warnings {
      warn!("uniform warning: {:?}", warning);
    }

    Ok(Program {
      program: program
    })
  }

  /// Create a `Program` from a string – you can for instance use `str` or `String`.
  pub fn from_str<'a, S>(s: S) -> Result<Self, LoadError> where S: Into<&'a str> {
    Self::from_bufread(s.into().as_bytes())
  }
}

#[derive(Eq, PartialEq)]
pub struct ProgramKey<In, Out, Uni> {
  pub key: String,
  _in: PhantomData<*const In>,
  _out: PhantomData<*const Out>,
  _uni: PhantomData<*const Uni>
}

impl<In, Out, Uni> ProgramKey<In, Out, Uni> {
  pub fn new(key: &str) -> Self {
    ProgramKey {
      key: key.to_owned(),
      _in: PhantomData,
      _out: PhantomData,
      _uni: PhantomData
    }
  }
}

impl<In, Out, Uni> Clone for ProgramKey<In, Out, Uni> {
  fn clone(&self) -> Self {
    ProgramKey {
      key: self.key.clone(),
      ..*self
    }
  }
}

impl<In, Out, Uni> fmt::Debug for ProgramKey<In, Out, Uni> {
  fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
    self.key.fmt(f)
  }
}

impl<In, Out, Uni> hash::Hash for ProgramKey<In, Out, Uni> {
  fn hash<H>(&self, hasher: &mut H) where H: hash::Hasher {
    self.key.hash(hasher)
  }
}

impl<'a, In, Out, Uni> From<&'a str> for ProgramKey<In, Out, Uni> {
  fn from(key: &str) -> Self {
    ProgramKey::new(key)
  }
}

impl<In, Out, Uni> CacheKey for ProgramKey<In, Out, Uni>
    where In: 'static,
          Out: 'static,
          Uni: 'static {
  type Target = Program<In, Out, Uni>;
}

impl<In, Out, Uni> StoreKey for ProgramKey<In, Out, Uni>
    where In: 'static,
          Out: 'static,
          Uni: 'static {
  fn key_to_path(&self) -> PathBuf {
    self.key.clone().into()
  }
}

impl<In, Out, Uni> Load for Program<In, Out, Uni>
    where In: 'static + Vertex,
          Out: 'static,
          Uni: 'static + UniformInterface {
  fn load<P>(path: P, _: &mut Store) -> Result<LoadResult<Self>, LoadError> where P: AsRef<Path> {
    let path = path.as_ref();

    enum CurrentStage {
      VS,
      FS,
      GS,
      TCS,
      TES
    }

    fn annotate_line_src(src: &mut String, line: &str, line_nb: usize) {
      *src += &format!("#line {}\n{}\n", line_nb, line);
    }

    match File::open(path) {
      Ok(fh) => {
        let buffered = BufReader::new(fh);
        let mut tcs_src = String::new();
        let mut tes_src = String::new();
        let mut vs_src = String::new();
        let mut gs_src = String::new();
        let mut fs_src = String::new();
        let mut current_stage: Option<CurrentStage> = None;

        for (line_nb, line) in buffered.lines().enumerate() {
          let line_nb = line_nb + 1;
          let line = line.unwrap();
          let trimmed = line.trim();

          if trimmed.starts_with("#vs") {
            if !vs_src.is_empty() {
              return Err(LoadError::ParseFailed(format!("(line {}) several #vs sections", line_nb)));
            }

            info!("  found a vertex shader");

            current_stage = Some(CurrentStage::VS);
            continue;
          } else if trimmed.starts_with("#fs") {
            if !fs_src.is_empty() {
              return Err(LoadError::ParseFailed(format!("(line {}) several #fs sections", line_nb)));
            }

            info!("  found a fragment shader");

            current_stage = Some(CurrentStage::FS);
            continue;
          } else if trimmed.starts_with("#gs") {
            if !gs_src.is_empty() {
              return Err(LoadError::ParseFailed(format!("(line {}) several #gs sections", line_nb)));
            }

            info!("  found a geometry shader");

            current_stage = Some(CurrentStage::GS);
            continue;
          } else if trimmed.starts_with("#tcs") {
            if !tcs_src.is_empty() {
              return Err(LoadError::ParseFailed(format!("(line {}) several #tcs sections", line_nb)));
            }

            info!("  found a tessellation control shader");

            current_stage = Some(CurrentStage::TCS);
            continue;
          } else if trimmed.starts_with("#tes") {
            if !tes_src.is_empty() {
              return Err(LoadError::ParseFailed(format!("(line {}) several #tes sections", line_nb)));
            }

            info!("  found a tessellation evaluation shader");

            current_stage = Some(CurrentStage::TES);
            continue;
          } else if current_stage.is_none() && !trimmed.is_empty() && !trimmed.starts_with("//") && !trimmed.starts_with("\n") {
            return Err(LoadError::ParseFailed(format!("(line {}) not in a shader stage nor a comment", line_nb)));
          }

          match current_stage {
            Some(CurrentStage::VS) => {
              annotate_line_src(&mut vs_src, trimmed, line_nb);
            },
            Some(CurrentStage::FS) => {
              annotate_line_src(&mut fs_src, trimmed, line_nb);
            },
            Some(CurrentStage::GS) => {
              annotate_line_src(&mut gs_src, trimmed, line_nb);
            },
            Some(CurrentStage::TCS) => {
              annotate_line_src(&mut tcs_src, trimmed, line_nb);
            },
            Some(CurrentStage::TES) => {
              annotate_line_src(&mut tes_src, trimmed, line_nb);
            },
            None => {}
          }
        }

        let (program, warnings) = new_program(&tcs_src, &tes_src, &vs_src, &gs_src, &fs_src)
          .map_err(|e| LoadError::ConversionFailed(format!("{:#?}", e)))?;

        // check for semantic errors
        for warning in warnings {
          warn!("uniform warning: {:?}", warning);
        }

        Ok(
          (Program {
            program: program
          }).into()
        )
      },
      Err(_) => Err(LoadError::FileNotFound(path.to_owned()))
    }
  }
}

pub trait UnwrapOrUnbound<T> {
  fn unwrap_or_unbound(self, builder: &UniformBuilder, warnings: &mut Vec<UniformWarning>) -> Uniform<T> where T: Uniformable;
}

impl<T> UnwrapOrUnbound<T> for Result<Uniform<T>, UniformWarning> {
  fn unwrap_or_unbound(self, builder: &UniformBuilder, warnings: &mut Vec<UniformWarning>) -> Uniform<T> where T: Uniformable {
    self.unwrap_or_else(|w| {
      warnings.push(w);
      builder.unbound()
    })
  }
}

<|endoftext|>


<commit_before>use structs::{ Map, SnakeInfo };
use util;

#[derive(PartialEq, Debug)]
pub enum Tile<'a> {
    Food { coordinate: (i32,i32) },
    Obstacle { coordinate: (i32,i32) },
    Empty { coordinate: (i32,i32) },
    SnakeHead { coordinate: (i32,i32), snake: &'a SnakeInfo },
    SnakeBody { coordinate: (i32,i32), snake: &'a SnakeInfo }
}

#[derive(Debug)]
pub enum Direction {
    Down,
    Up,
    Left,
    Right
}

pub fn direction_as_string(direction: &Direction) -> String {
    let s = match direction {
        &Direction::Down => "DOWN",
        &Direction::Up => "UP",
        &Direction::Left => "LEFT",
        &Direction::Right => "RIGHT"
    };

    String::from(s)
}

pub fn direction_as_movement_delta(direction: &Direction) -> (i32,i32) {
    match direction {
        &Direction::Down => (0, 1),
        &Direction::Up => (0, -1),
        &Direction::Left => (-1, 0),
        &Direction::Right => (1, 0)
    }
}

impl Map {
    pub fn get_snake_by_id<'a>(&'a self, id: &String) -> Option<&'a SnakeInfo> {
        self.snakeInfos.iter().find(|s| &s.id == id)
    }

    pub fn get_tile_at(&self, coordinate: (i32,i32)) -> Tile {
        let position = util::translate_coordinate(coordinate, self.width);
        let snake_at_tile = self.snakeInfos.iter().find(|s| s.positions.contains(&position));

        if self.obstaclePositions.contains(&position) {
            Tile::Obstacle { coordinate: coordinate }
        } else if self.foodPositions.contains(&position) {
            Tile::Food { coordinate: coordinate }
        } else if snake_at_tile.is_some() {
            let s = snake_at_tile.unwrap();
            if s.positions[0] == position {
                Tile::SnakeHead { coordinate: coordinate, snake: s }
            } else {
                Tile::SnakeBody { coordinate: coordinate, snake: s }
            }
        } else {
            Tile::Empty { coordinate: coordinate }
        }
    }

    pub fn is_tile_available_for_movement(&self, coordinate: (i32,i32)) -> bool {
        let tile = self.get_tile_at(coordinate);
        match tile {
            Tile::Empty { coordinate: _ } => true,
            Tile::Food { coordinate: _ } => true,
            _ => false
        }
    }

    pub fn can_snake_move_in_direction(&self, snake: &SnakeInfo, direction: Direction) -> bool {
        let (xd,yd) = direction_as_movement_delta(&direction);
        let (x,y) = util::translate_position(snake.positions[0], self.width);

        self.is_tile_available_for_movement((x+xd,y+yd))
    }

    #[allow(dead_code)]
    pub fn is_coordinate_out_of_bounds(&self, coordinate: (i32,i32)) -> bool {
        let (x,y) = coordinate;
        x < 0 || x >= self.width || y < 0 || y >= self.height
    }
}

#[cfg(test)]
mod test {
    use util::{ translate_coordinate };
    use maputil::{ Direction, Tile };
    use structs::{ Map, SnakeInfo };

    const MAP_WIDTH: i32 = 3;

    fn get_snake_one() -> SnakeInfo {
        SnakeInfo {
            name: String::from("1"),
            points: 0,
            tailProtectedForGameTicks: 0,
            positions: vec![translate_coordinate((1,1), MAP_WIDTH),
                            translate_coordinate((0,1), MAP_WIDTH)],
            id: String::from("1")
        }
    }

    fn get_snake_two() -> SnakeInfo {
        SnakeInfo {
            name: String::from("2"),
            points: 0,
            tailProtectedForGameTicks: 0,
            positions: vec![translate_coordinate((1,2), MAP_WIDTH)],
            id: String::from("2")
        }
    }

    // The map used for testing, 1 and 2 represents the snakes
    //yx012
    //0  F
    //1 11#
    //2  2
    fn get_test_map() -> Map {
        Map {
            type_: String::from("type"),
            width: MAP_WIDTH,
            height: MAP_WIDTH,
            worldTick: 0,
            snakeInfos: vec![get_snake_one(), get_snake_two()],
            foodPositions: vec![translate_coordinate((1,0),  MAP_WIDTH)],
            obstaclePositions: vec![translate_coordinate((2,1), MAP_WIDTH)]
        }
    }

    #[test]
    fn snake_can_be_found_by_id() {
        let map = get_test_map();
        let id = &get_snake_one().id;
        let s = map.get_snake_by_id(id);
        let found_id = &s.unwrap().id;
        assert_eq!(id, found_id);
    }

    #[test]
    fn tile_is_correctly_found() {
        let map = get_test_map();
        let snake_one = get_snake_one();
        let snake_two = get_snake_two();
        let tiles =
            vec![
                vec![Tile::Empty{ coordinate: (0,0) },
                     Tile::Food{ coordinate: (1,0) },
                     Tile::Empty{ coordinate: (2,0) }],
                vec![Tile::SnakeBody{ coordinate: (0,1), snake: &snake_one },
                     Tile::SnakeHead{ coordinate: (1,1), snake: &snake_one },
                     Tile::Obstacle{ coordinate: (2,1)}],
                vec![Tile::Empty{ coordinate: (0,2) },
                     Tile::SnakeHead{ coordinate: (1,2), snake: &snake_two },
                     Tile::Empty{ coordinate:(2,2) }]];
        for y in 0..map.width {
            for x in 0..map.height {
                assert_eq!(tiles[y as usize][x as usize],
                           map.get_tile_at((x,y)));
            }
        }
    }

    #[test]
    fn tile_is_correctly_marked_as_movable() {
        let map = get_test_map();
        let tiles = vec![vec![true, true, true],
                         vec![false, false, false],
                         vec![true, false, true]];

        for y in 0..map.height {
            for x in 0..map.width {
                assert_eq!(tiles[y as usize][x as usize],
                           map.is_tile_available_for_movement((x,y)));
            }
        }
    }

    #[test]
    fn can_snake_move_identifies_correctly() {
        let map = get_test_map();
        let id = &get_snake_one().id;
        let snake = map.get_snake_by_id(id).unwrap();

        assert_eq!(true, map.can_snake_move_in_direction(&snake, Direction::Up));
        assert_eq!(false, map.can_snake_move_in_direction(&snake, Direction::Down));
        assert_eq!(false, map.can_snake_move_in_direction(&snake, Direction::Left));
        assert_eq!(false, map.can_snake_move_in_direction(&snake, Direction::Right));
    }
}
<commit_msg>Update tests according to updated API<commit_after>use structs::{ Map, SnakeInfo };
use util;

#[derive(PartialEq, Debug)]
pub enum Tile<'a> {
    Food { coordinate: (i32,i32) },
    Obstacle { coordinate: (i32,i32) },
    Empty { coordinate: (i32,i32) },
    SnakeHead { coordinate: (i32,i32), snake: &'a SnakeInfo },
    SnakeBody { coordinate: (i32,i32), snake: &'a SnakeInfo }
}

#[derive(Debug)]
pub enum Direction {
    Down,
    Up,
    Left,
    Right
}

pub fn direction_as_string(direction: &Direction) -> String {
    let s = match direction {
        &Direction::Down => "DOWN",
        &Direction::Up => "UP",
        &Direction::Left => "LEFT",
        &Direction::Right => "RIGHT"
    };

    String::from(s)
}

pub fn direction_as_movement_delta(direction: &Direction) -> (i32,i32) {
    match direction {
        &Direction::Down => (0, 1),
        &Direction::Up => (0, -1),
        &Direction::Left => (-1, 0),
        &Direction::Right => (1, 0)
    }
}

impl Map {
    pub fn get_snake_by_id<'a>(&'a self, id: &String) -> Option<&'a SnakeInfo> {
        self.snakeInfos.iter().find(|s| &s.id == id)
    }

    pub fn get_tile_at(&self, coordinate: (i32,i32)) -> Tile {
        let position = util::translate_coordinate(coordinate, self.width);
        let snake_at_tile = self.snakeInfos.iter().find(|s| s.positions.contains(&position));

        if self.obstaclePositions.contains(&position) {
            Tile::Obstacle { coordinate: coordinate }
        } else if self.foodPositions.contains(&position) {
            Tile::Food { coordinate: coordinate }
        } else if snake_at_tile.is_some() {
            let s = snake_at_tile.unwrap();
            if s.positions[0] == position {
                Tile::SnakeHead { coordinate: coordinate, snake: s }
            } else {
                Tile::SnakeBody { coordinate: coordinate, snake: s }
            }
        } else {
            Tile::Empty { coordinate: coordinate }
        }
    }

    pub fn is_tile_available_for_movement(&self, coordinate: (i32,i32)) -> bool {
        let tile = self.get_tile_at(coordinate);
        match tile {
            Tile::Empty { coordinate: _ } => true,
            Tile::Food { coordinate: _ } => true,
            _ => false
        }
    }

    pub fn can_snake_move_in_direction(&self, snake: &SnakeInfo, direction: Direction) -> bool {
        let (xd,yd) = direction_as_movement_delta(&direction);
        let (x,y) = util::translate_position(snake.positions[0], self.width);

        self.is_tile_available_for_movement((x+xd,y+yd))
    }

    #[allow(dead_code)]
    pub fn is_coordinate_out_of_bounds(&self, coordinate: (i32,i32)) -> bool {
        let (x,y) = coordinate;
        x < 0 || x >= self.width || y < 0 || y >= self.height
    }
}

#[cfg(test)]
mod test {
    use util::{ translate_coordinate };
    use maputil::{ Direction, Tile };
    use structs::{ Map, SnakeInfo };

    const MAP_WIDTH: i32 = 3;

    fn get_snake_one() -> SnakeInfo {
        SnakeInfo {
            name: String::from("1"),
            points: 0,
            tailProtectedForGameTicks: 0,
            positions: vec![translate_coordinate((1,1), MAP_WIDTH),
                            translate_coordinate((0,1), MAP_WIDTH)],
            id: String::from("1")
        }
    }

    fn get_snake_two() -> SnakeInfo {
        SnakeInfo {
            name: String::from("2"),
            points: 0,
            tailProtectedForGameTicks: 0,
            positions: vec![translate_coordinate((1,2), MAP_WIDTH)],
            id: String::from("2")
        }
    }

    // The map used for testing, 1 and 2 represents the snakes
    //yx012
    //0  F
    //1 11#
    //2  2
    fn get_test_map() -> Map {
        Map {
            width: MAP_WIDTH,
            height: MAP_WIDTH,
            worldTick: 0,
            snakeInfos: vec![get_snake_one(), get_snake_two()],
            foodPositions: vec![translate_coordinate((1,0),  MAP_WIDTH)],
            obstaclePositions: vec![translate_coordinate((2,1), MAP_WIDTH)]
        }
    }

    #[test]
    fn snake_can_be_found_by_id() {
        let map = get_test_map();
        let id = &get_snake_one().id;
        let s = map.get_snake_by_id(id);
        let found_id = &s.unwrap().id;
        assert_eq!(id, found_id);
    }

    #[test]
    fn tile_is_correctly_found() {
        let map = get_test_map();
        let snake_one = get_snake_one();
        let snake_two = get_snake_two();
        let tiles =
            vec![
                vec![Tile::Empty{ coordinate: (0,0) },
                     Tile::Food{ coordinate: (1,0) },
                     Tile::Empty{ coordinate: (2,0) }],
                vec![Tile::SnakeBody{ coordinate: (0,1), snake: &snake_one },
                     Tile::SnakeHead{ coordinate: (1,1), snake: &snake_one },
                     Tile::Obstacle{ coordinate: (2,1)}],
                vec![Tile::Empty{ coordinate: (0,2) },
                     Tile::SnakeHead{ coordinate: (1,2), snake: &snake_two },
                     Tile::Empty{ coordinate:(2,2) }]];
        for y in 0..map.width {
            for x in 0..map.height {
                assert_eq!(tiles[y as usize][x as usize],
                           map.get_tile_at((x,y)));
            }
        }
    }

    #[test]
    fn tile_is_correctly_marked_as_movable() {
        let map = get_test_map();
        let tiles = vec![vec![true, true, true],
                         vec![false, false, false],
                         vec![true, false, true]];

        for y in 0..map.height {
            for x in 0..map.width {
                assert_eq!(tiles[y as usize][x as usize],
                           map.is_tile_available_for_movement((x,y)));
            }
        }
    }

    #[test]
    fn can_snake_move_identifies_correctly() {
        let map = get_test_map();
        let id = &get_snake_one().id;
        let snake = map.get_snake_by_id(id).unwrap();

        assert_eq!(true, map.can_snake_move_in_direction(&snake, Direction::Up));
        assert_eq!(false, map.can_snake_move_in_direction(&snake, Direction::Down));
        assert_eq!(false, map.can_snake_move_in_direction(&snake, Direction::Left));
        assert_eq!(false, map.can_snake_move_in_direction(&snake, Direction::Right));
    }
}
<|endoftext|>


<commit_before>//! A JSON-RPC 2.0 messages
//!
//! The main entrypoint here is the [Message](enum.Message.html). The others are just building
//! blocks and you should generally work with `Message` instead.

use std::str::FromStr;

use serde::ser::{Serialize, Serializer, SerializeStruct};
use serde::de::{Deserialize, Deserializer, Unexpected, Error};
use serde_json::{Value, from_value};

#[derive(Debug, Clone, PartialEq, Eq)]
struct Version;

impl Serialize for Version {
    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
        serializer.serialize_str("2.0")
    }
}

impl Deserialize for Version {
    fn deserialize<D: Deserializer>(deserializer: D) -> Result<Self, D::Error> {
        // The version is actually a string
        let parsed: String = Deserialize::deserialize(deserializer)?;
        if parsed == "2.0" {
            Ok(Version)
        } else {
            Err(D::Error::invalid_value(Unexpected::Str(&parsed), &"value 2.0"))
        }
    }
}

/// An RPC request
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
pub struct Request {
    jsonrpc: Version,
    pub method: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub params: Option<Value>,
    // TODO: Make private?
    pub id: Value,
}

/// An error code
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
pub struct RPCError {
    pub code: i64,
    pub message: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub data: Option<Value>,
}

/// A response to RPC
#[derive(Debug, Deserialize, Clone, PartialEq)]
pub struct Response {
    pub result: Result<Value, RPCError>,
    pub id: Value,
}

impl Serialize for Response {
    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
        let mut sub = serializer.serialize_struct("Response", 2)?;
        sub.serialize_field("id", &self.id)?;
        match self.result {
            Ok(ref value) => sub.serialize_field("result", value),
            Err(ref err) => sub.serialize_field("error", err),
        }?;
        sub.end()
    }
}

/// A notification (doesn't expect an answer)
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
pub struct Notification {
    jsonrpc: Version,
    pub method: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub params: Option<Value>,
}

/// One message of the JSON RPC protocol
///
/// One message, directly mapped from the structures of the protocol. See the
/// [specification](http://www.jsonrpc.org/specification) for more details.
///
/// Since the protocol allows one endpoint to be both client and server at the same time, the
/// message can decode and encode both directions of the protocol.
///
/// The `Unmatched` variant is for cases when the message that arrived is valid JSON, but doesn't
/// match the protocol. It allows for handling these non-fatal errors on higher level than the
/// parser.
///
/// It can be serialized and deserialized, or converted to and from a string.
#[derive(Debug, Clone, PartialEq)]
pub enum Message {
    Request(Request),
    Response(Response),
    Notification(Notification),
    Batch(Vec<Message>),
    Unmatched(Value),
}

impl Message {
    /// A constructor for a request.
    ///
    /// The ID is auto-generated.
    pub fn request(method: String, params: Option<Value>) -> Self {
        Message::Request(Request {
            jsonrpc: Version,
            method: method,
            params: params,
            // TODO!
            id: Value::Null,
        })
    }
    /// A constructor for a notification.
    pub fn notification(method: String, params: Option<Value>) -> Self {
        Message::Notification(Notification {
            jsonrpc: Version,
            method: method,
            params: params,
        })
    }
    // TODO: Other constructors
}

impl Serialize for Message {
    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
        match *self {
            Message::Request(ref req) => req.serialize(serializer),
            Message::Response(ref resp) => resp.serialize(serializer),
            Message::Notification(ref notif) => notif.serialize(serializer),
            Message::Batch(ref batch) => batch.serialize(serializer),
            Message::Unmatched(ref val) => val.serialize(serializer),
        }
    }
}

macro_rules! deser_branch {
    ($src:expr, $branch:ident) => {
        match from_value($src.clone()) {
            Ok(parsed) => return Message::$branch(parsed),
            Err(_) => (),
        }
    };
}

// TODO: This must be possible to do in less wasteful way. The cloning is stupid :-(
// There seems to be a feature coming in a future release that'd make all this unnecessary.
// https://github.com/serde-rs/serde/pull/739.
impl From<Value> for Message {
    fn from(v: Value) -> Self {
        // Try decoding it by each branch in sequence, taking the first one that matches
        deser_branch!(v, Request);
        deser_branch!(v, Response);
        deser_branch!(v, Notification);
        deser_branch!(v, Batch);
        Message::Unmatched(v)
    }
}

impl Deserialize for Message {
    fn deserialize<D: Deserializer>(deserializer: D) -> Result<Self, D::Error> {
        // Read it as a JSON (delegate the deserialization)
        let preparsed: Value = Deserialize::deserialize(deserializer)?;
        // Convert it
        Ok(Self::from(preparsed))
    }
}

impl FromStr for Message {
    type Err = ::serde_json::error::Error;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        ::serde_json::de::from_str(s)
    }
}

impl Into<String> for Message {
    fn into(self) -> String {
        ::serde_json::ser::to_string(&self).unwrap()
    }
}
<commit_msg>Some more constructors<commit_after>//! A JSON-RPC 2.0 messages
//!
//! The main entrypoint here is the [Message](enum.Message.html). The others are just building
//! blocks and you should generally work with `Message` instead.

use std::str::FromStr;

use serde::ser::{Serialize, Serializer, SerializeStruct};
use serde::de::{Deserialize, Deserializer, Unexpected, Error};
use serde_json::{Value, from_value};

#[derive(Debug, Clone, PartialEq, Eq)]
struct Version;

impl Serialize for Version {
    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
        serializer.serialize_str("2.0")
    }
}

impl Deserialize for Version {
    fn deserialize<D: Deserializer>(deserializer: D) -> Result<Self, D::Error> {
        // The version is actually a string
        let parsed: String = Deserialize::deserialize(deserializer)?;
        if parsed == "2.0" {
            Ok(Version)
        } else {
            Err(D::Error::invalid_value(Unexpected::Str(&parsed), &"value 2.0"))
        }
    }
}

/// An RPC request
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
pub struct Request {
    jsonrpc: Version,
    pub method: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub params: Option<Value>,
    // TODO: Make private?
    pub id: Value,
}

/// An error code
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
pub struct RPCError {
    pub code: i64,
    pub message: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub data: Option<Value>,
}

/// A response to RPC
#[derive(Debug, Deserialize, Clone, PartialEq)]
pub struct Response {
    pub result: Result<Value, RPCError>,
    pub id: Value,
}

impl Serialize for Response {
    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
        let mut sub = serializer.serialize_struct("Response", 2)?;
        sub.serialize_field("id", &self.id)?;
        match self.result {
            Ok(ref value) => sub.serialize_field("result", value),
            Err(ref err) => sub.serialize_field("error", err),
        }?;
        sub.end()
    }
}

/// A notification (doesn't expect an answer)
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
pub struct Notification {
    jsonrpc: Version,
    pub method: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub params: Option<Value>,
}

/// One message of the JSON RPC protocol
///
/// One message, directly mapped from the structures of the protocol. See the
/// [specification](http://www.jsonrpc.org/specification) for more details.
///
/// Since the protocol allows one endpoint to be both client and server at the same time, the
/// message can decode and encode both directions of the protocol.
///
/// The `Unmatched` variant is for cases when the message that arrived is valid JSON, but doesn't
/// match the protocol. It allows for handling these non-fatal errors on higher level than the
/// parser.
///
/// It can be serialized and deserialized, or converted to and from a string.
///
/// The `Batch` variant is supposed to be created directly, without a constructor. The `Unmatched`
/// is something you may get from parsing but it is not expected you'd need to create it (though it
/// can be created directly as well).
#[derive(Debug, Clone, PartialEq)]
pub enum Message {
    Request(Request),
    Response(Response),
    Notification(Notification),
    Batch(Vec<Message>),
    Unmatched(Value),
}

impl Message {
    /// A constructor for a request.
    ///
    /// The ID is auto-generated.
    pub fn request(method: String, params: Option<Value>) -> Self {
        Message::Request(Request {
            jsonrpc: Version,
            method: method,
            params: params,
            // TODO!
            id: Value::Null,
        })
    }
    /// Answer the request with a (positive) reply.
    ///
    /// The ID is taken from the request.
    pub fn reply(request: &Request, reply: Value) -> Self {
        Message::Response(Response {
            result: Ok(reply),
            id: request.id.clone(),
        })
    }
    /// Answer the request with an error.
    ///
    /// The ID is taken from the request and the error structure is constructed.
    pub fn error(request: &Request, code: i64, message: String, data: Option<Value>) -> Self {
        Message::Response(Response {
            result: Err(RPCError {
                code: code,
                message: message,
                data: data,
            }),
            id: request.id.clone(),
        })
    }
    /// Create an error without a request.
    ///
    /// Create a top-level/free-standing error (one without an ID). This is the required answer for
    /// less serious protocol errors.
    pub fn top_error(code: i64, message: String, data: Option<Value>) -> Self {
        Message::Response(Response {
            result: Err(RPCError {
                code: code,
                message: message,
                data: data,
            }),
            id: Value::Null,
        })
    }
    /// A constructor for a notification.
    pub fn notification(method: String, params: Option<Value>) -> Self {
        Message::Notification(Notification {
            jsonrpc: Version,
            method: method,
            params: params,
        })
    }
}

impl Serialize for Message {
    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
        match *self {
            Message::Request(ref req) => req.serialize(serializer),
            Message::Response(ref resp) => resp.serialize(serializer),
            Message::Notification(ref notif) => notif.serialize(serializer),
            Message::Batch(ref batch) => batch.serialize(serializer),
            Message::Unmatched(ref val) => val.serialize(serializer),
        }
    }
}

macro_rules! deser_branch {
    ($src:expr, $branch:ident) => {
        match from_value($src.clone()) {
            Ok(parsed) => return Message::$branch(parsed),
            Err(_) => (),
        }
    };
}

// TODO: This must be possible to do in less wasteful way. The cloning is stupid :-(
// There seems to be a feature coming in a future release that'd make all this unnecessary.
// https://github.com/serde-rs/serde/pull/739.
impl From<Value> for Message {
    fn from(v: Value) -> Self {
        // Try decoding it by each branch in sequence, taking the first one that matches
        deser_branch!(v, Request);
        deser_branch!(v, Response);
        deser_branch!(v, Notification);
        deser_branch!(v, Batch);
        Message::Unmatched(v)
    }
}

impl Deserialize for Message {
    fn deserialize<D: Deserializer>(deserializer: D) -> Result<Self, D::Error> {
        // Read it as a JSON (delegate the deserialization)
        let preparsed: Value = Deserialize::deserialize(deserializer)?;
        // Convert it
        Ok(Self::from(preparsed))
    }
}

impl FromStr for Message {
    type Err = ::serde_json::error::Error;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        ::serde_json::de::from_str(s)
    }
}

impl Into<String> for Message {
    fn into(self) -> String {
        ::serde_json::ser::to_string(&self).unwrap()
    }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::convert::From;
use std::io::{self, Write};

use super::format::{Format, format};

/// Minimal template for any kind of plain text.
#[derive(Clone, Debug, PartialEq)]
pub struct Template {
    pub body: String,
}

impl Template {
    pub fn read_str<S: AsRef<str>>(template: S) -> Template {
        Template { body: String::from(template.as_ref()) }
    }

    pub fn compile_inline<'a, S, W>(template: S,
                                writer: &'a mut W,
                                context: HashMap<String, String>)
                                -> Result<&'a mut W, io::Error>
        where S: AsRef<str>,
              W: Write
    {
        let mut template = Template::read_str(template);
        Template::write(&mut template, writer, context)
    }

    fn process(&self, ph: &str, context: &HashMap<String, String>) -> Option<String> {

        let args = ph.split(';').collect::<Vec<_>>();

        if args.len() == 1 {
            match context.get(args[0]) {
                Some(v) => Some(v.clone()),
                _ => None,
            }
        } else {
            None
        }
    }

    pub fn write<'a, W: Write>(&mut self,
                           writer: &'a mut W,
                           context: HashMap<String, String>)
                           -> Result<&'a mut W, io::Error> {

        let chars = self.body.as_bytes().into_iter();
        let mut found_opening = false;
        let mut marker = 0;
        let mut last_written = 0;

        for (pos, ch) in chars.enumerate() {
            if *ch == b'$' && found_opening {

                // flush out the chunk that are not placeholder
                try!(writer.write(&self.body[last_written..marker - 1].as_bytes()));

                let placeholder = &self.body[marker..pos];
                if let Some(value) = self.process(placeholder, &context) {
                    try!(writer.write(&value.as_bytes()));
                } else {
                    try!(writer.write(&self.body[marker..pos].as_bytes()));
                }
                marker = pos + 1;
                last_written = pos + 1;
                found_opening = false;

            } else if *ch == b'$' {
                marker = pos + 1;
                found_opening = true;
            }

            if pos == self.body.as_bytes().len() - 1 {
                try!(writer.write(&self.body[last_written..pos + 1].as_bytes()));
            }
        }

        writer.flush().unwrap();

        Ok(writer)
    }
}
<commit_msg>did rustfmt on template.rs<commit_after>use std::collections::HashMap;
use std::convert::From;
use std::io::{self, Write};

use super::format::{Format, format};

/// Minimal template for any kind of plain text.
#[derive(Clone, Debug, PartialEq)]
pub struct Template {
    pub body: String,
}

impl Template {
    pub fn read_str<S: AsRef<str>>(template: S) -> Template {
        Template { body: String::from(template.as_ref()) }
    }

    pub fn compile_inline<'a, S, W>(template: S,
                                    writer: &'a mut W,
                                    context: HashMap<String, String>)
                                    -> Result<&'a mut W, io::Error>
        where S: AsRef<str>,
              W: Write
    {
        let mut template = Template::read_str(template);
        Template::write(&mut template, writer, context)
    }

    fn process(&self, ph: &str, context: &HashMap<String, String>) -> Option<String> {

        let args = ph.split(';').collect::<Vec<_>>();

        if args.len() == 1 {
            match context.get(args[0]) {
                Some(v) => Some(v.clone()),
                _ => None,
            }
        } else {
            None
        }
    }

    pub fn write<'a, W: Write>(&mut self,
                               writer: &'a mut W,
                               context: HashMap<String, String>)
                               -> Result<&'a mut W, io::Error> {

        let chars = self.body.as_bytes().into_iter();
        let mut found_opening = false;
        let mut marker = 0;
        let mut last_written = 0;

        for (pos, ch) in chars.enumerate() {
            if *ch == b'$' && found_opening {

                // flush out the chunk that are not placeholder
                try!(writer.write(&self.body[last_written..marker - 1].as_bytes()));

                let placeholder = &self.body[marker..pos];
                if let Some(value) = self.process(placeholder, &context) {
                    try!(writer.write(&value.as_bytes()));
                } else {
                    try!(writer.write(&self.body[marker..pos].as_bytes()));
                }
                marker = pos + 1;
                last_written = pos + 1;
                found_opening = false;

            } else if *ch == b'$' {
                marker = pos + 1;
                found_opening = true;
            }

            if pos == self.body.as_bytes().len() - 1 {
                try!(writer.write(&self.body[last_written..pos + 1].as_bytes()));
            }
        }

        writer.flush().unwrap();

        Ok(writer)
    }
}
<|endoftext|>


<commit_before>extern crate rand;
extern crate itertools;
use self::itertools::Itertools;

use std::collections::BTreeMap;
use std::collections::btree_map::Entry;
use std::rc::Rc;
use std::cell::RefCell;
use std::cell::Ref;
use std::sync::mpsc::Receiver;

type WordCell = Rc<RefCell<Word>>;
pub type AuthorCell = Rc<RefCell<Author>>;
pub type SourceCell = Rc<RefCell<Source>>;
type MessageCell = Rc<RefCell<Message>>;
type CategoryCell = Rc<RefCell<Category>>;
type ConversationCell = Rc<RefCell<Conversation>>;
type InstanceCell = Rc<RefCell<WordInstance>>;

enum Mismatch<T> {
    // Incompatible for matching or no mismatch (exactly the same)
    None,
    // Exactly one mismatch
    One(T),
    // Multiple mismatches
    Multiple,
}

fn wrap<T>(t: T) -> Rc<RefCell<T>> {
    Rc::new(RefCell::new(t))
}

// const RATIO_CONTAINED_BEFORE_COMBINATION: f64 = 0.8;

#[derive(Default)]
pub struct Lexicon<R: rand::Rng> {
    rng: R,
    words: BTreeMap<String, WordCell>,
    sources: BTreeMap<String, SourceCell>,
    conversations: Vec<ConversationCell>,
    messages: Vec<MessageCell>,

    active_conversations: BTreeMap<*const Source, ConversationCell>,
}

impl<R: rand::Rng> Lexicon<R> {
    pub fn new(rng: R) -> Lexicon<R> {
        Lexicon{
            rng: rng,
            words: Default::default(),
            sources: Default::default(),
            conversations: Default::default(),
            messages: Default::default(),
            active_conversations: Default::default(),
        }
    }

    pub fn source(&mut self, name: String) -> SourceCell {
        match self.sources.entry(name.clone()) {
            Entry::Vacant(v) => v.insert(wrap(Source{
                name: name,
                messages: 0,
                authors: BTreeMap::default(),
            })).clone(),
            Entry::Occupied(o) => o.get().clone(),
        }
    }

    pub fn author(&mut self, source: SourceCell, name: String) -> AuthorCell {
        match source.borrow_mut().authors.entry(name.clone()) {
            Entry::Vacant(v) => {
                let a = wrap(Author{
                    source: source.clone(),
                    name: name.clone(),
                });
                v.insert(a.clone());
                a
            },
            Entry::Occupied(o) => o.get().clone(),
        }
    }
    pub fn tell(&mut self, source: SourceCell, author: AuthorCell, content: String) -> Option<String> {
        let conversation = match self.active_conversations.entry(&*source.borrow() as *const Source) {
            Entry::Vacant(v) => {
                let c = wrap(Conversation{
                    source: source.clone(),
                    messages: Vec::new(),
                });
                v.insert(c.clone());
                self.conversations.push(c.clone());
                c
            },
            Entry::Occupied(o) => {
                o.get().clone()
            },
        };

        let message = wrap(Message{
            author: author.clone(),
            conversation: conversation.clone(),
            index: conversation.borrow().messages.len(),
            instances: Vec::new(),
        });

        self.messages.push(message.clone());

        for word in content.split(' ').map(|s| {
            let string = s.to_string();
            match self.words.entry(string.clone()) {
                Entry::Vacant(v) => {
                    let w = wrap(Word{
                        name: string,
                        instances: Vec::new(),
                    });
                    v.insert(w.clone());
                    w
                },
                Entry::Occupied(o) => {
                    o.get().clone()
                },
            }
        }) {
            // Create empty category for the word
            let category = wrap(Category::default());
            // Create instance of the word
            let instance = wrap(WordInstance{
                word: word.clone(),
                category: category.clone(),
                message: message.clone(),
                index: message.borrow().instances.len(),
            });
            // Insert word instance into the word, category, and message for future reference
            message.borrow_mut().instances.push(instance.clone());
            category.borrow_mut().instances.push(instance.clone());
            word.borrow_mut().instances.push(instance);
        }

        // Increment the messages by 1 for the source
        source.borrow_mut().messages += 1;

        // TODO: Determine what to say back
        None
    }

    pub fn initiate(&mut self, source: SourceCell) -> Option<String> {
        let conversation = wrap(Conversation{
            source: source.clone(),
            messages: Vec::new(),
        });

        match self.active_conversations.entry(&*source.borrow() as *const Source) {
            Entry::Vacant(v) => {
                v.insert(conversation.clone());
            },
            Entry::Occupied(mut o) => {
                o.insert(conversation.clone());
            },
        };

        self.conversations.push(conversation.clone());

        let base = match self.rng.choose(&self.messages[..]) {
            Some(m) => m.clone(),
            None => return None,
        };

        let bborrow = base.borrow();

        Some(format!("{} ~ {}",
            bborrow.instances.iter()
                .map(|instance| instance.borrow().category.clone())
                // TODO: Allow random choosing from co-category instances as well
                .map(|category| self.rng.choose(&category.borrow().instances[..]).unwrap().clone())
                .map(|instance| {
                    let ins = instance.borrow();
                    let word = ins.word.borrow();
                    word.name.clone()
                })
                .join(" "),
            bborrow.instances.iter()
                .map(|i| {
                    let b = i.borrow();
                    let b = b.word.borrow();
                    b.name.clone()
                })
                .join(" "))
        )
    }

    pub fn think(&mut self, end: Receiver<()>) {
        use std::sync::mpsc::TryRecvError::Empty;
        while end.try_recv() == Err(Empty) {
            // Choose a random message or break if there are none
            let message = match self.rng.choose(&self.messages[..]) {
                Some(m) => m.clone(),
                None => break,
            };

            // Vector of absolute perfect matches
            let mut vones = Vec::new();

            // Look through each word in the message
            for word in &message.borrow().instances {
                let borrow = word.borrow();
                // Check each instance in that words instances
                for instance in &borrow.word.borrow().instances {
                    // Get the message for each instance
                    let omessage = instance.borrow().message.clone();
                    // Find what kind of matches exist between the messages
                    if let Mismatch::One(best) =
                        Message::category_and_word_mismatch((message.clone(), omessage.clone())) {
                        vones.push(best);
                    }
                }
            }

            // Now that we have perfect matches, merge them into the same Category
            for ms in vones {
                // We only want to combine if they aren't already in the same category
                if ms.0.borrow().category != ms.1.borrow().category {
                    let cats = (ms.0.borrow().category.clone(), ms.1.borrow().category.clone());
                    Category::merge(cats);
                }
            }
        }
    }

    /// Print all multiple categories and return the amount of categories total
    pub fn show_categories(&self) -> usize {
        use std::collections::BTreeSet;
        let mut set = BTreeSet::new();
        for message in &self.messages {
            for instance in &message.borrow().instances {
                let ib = instance.borrow();
                set.insert(&*ib.category.borrow() as *const Category);
            }
        }

        let len = set.len();

        for cat in set {
            let catr = unsafe{&*cat};
            if catr.instances.len() != 1 {
                println!("Category:");
                for instance in &catr.instances {
                    let ib = instance.borrow();
                    println!("\t{}", ib.word.borrow().name);
                }
            }
        }
        len
    }
}

macro_rules! pointer_eq {
    ($s:ident) => {
        impl PartialEq for $s {
            fn eq(&self, other: &Self) -> bool {
                self as *const Self == other as *const Self
            }

            fn ne(&self, other: &Self) -> bool {
                self as *const Self != other as *const Self
            }
        }
    };
}

struct Conversation {
    source: SourceCell,
    messages: Vec<MessageCell>,
}

pointer_eq!(Conversation);

pub struct Author {
    source: SourceCell,
    name: String,
}

pointer_eq!(Author);

pub struct Source {
    name: String,
    messages: u64,
    authors: BTreeMap<String, AuthorCell>,
}

pointer_eq!(Source);

struct WordInstance {
    word: WordCell,
    category: CategoryCell,
    message: MessageCell,
    index: usize,
}

pointer_eq!(WordInstance);

struct Message {
    author: AuthorCell,
    conversation: ConversationCell,
    index: usize,
    instances: Vec<InstanceCell>,
}

impl Message {
    fn mismatch<F>(messages: (MessageCell, MessageCell), diff: F) -> Mismatch<(InstanceCell, InstanceCell)>
        where F: Fn((&WordInstance, &WordInstance)) -> bool
    {
        if messages.0 == messages.1 ||
            messages.0.borrow().instances.len() != messages.1.borrow().instances.len() {
            return Mismatch::None;
        }
        messages.0.borrow().instances.iter()
            .zip(messages.1.borrow().instances.iter())
            .fold(Mismatch::None, |acc, ws| {
                let bs = (ws.0.borrow(), ws.1.borrow());
                match acc {
                    Mismatch::None => {
                        if diff((&*bs.0, &*bs.1)) {
                            Mismatch::One((ws.0.clone(), ws.1.clone()))
                        } else {
                            Mismatch::None
                        }
                    },
                    Mismatch::One(best) => {
                        if diff((&*bs.0, &*bs.1)) {
                            Mismatch::Multiple
                        } else {
                            Mismatch::One(best)
                        }
                    },
                    Mismatch::Multiple => Mismatch::Multiple,
                }
            })
    }
    /*fn category_mismatch(messages: (MessageCell, MessageCell)) ->
        Mismatch<(InstanceCell, InstanceCell)> {
        Self::mismatch(messages, |ins| ins.0.category != ins.1.category)
    }

    fn word_mismatch(messages: (MessageCell, MessageCell)) ->
        Mismatch<(InstanceCell, InstanceCell)> {
        Self::mismatch(messages, |ins| ins.0.word != ins.1.word)
    }*/

    fn category_and_word_mismatch(messages: (MessageCell, MessageCell)) ->
        Mismatch<(InstanceCell, InstanceCell)> {
        Self::mismatch(messages, |ins| ins.0.word != ins.1.word && ins.0.category != ins.1.category)
    }
}

pointer_eq!(Message);

#[derive(Default)]
struct Category {
    instances: Vec<InstanceCell>,
    cocategories: Vec<CategoryCell>,
}

impl Category {
    fn merge(cs: (CategoryCell, CategoryCell)) -> CategoryCell {
        let cc = wrap(Self::default());
        {
            let clos = |cat: &CategoryCell| {
                for instance in cat.borrow().instances.iter().cloned() {
                    instance.borrow_mut().category = cc.clone();
                }
            };
            clos(&cs.0);
            clos(&cs.1);
            let mut ccm = cc.borrow_mut();
            ccm.instances.append(&mut cs.0.borrow_mut().instances);
            ccm.instances.append(&mut cs.1.borrow_mut().instances);
        }
        cc
    }
}

pointer_eq!(Category);

struct Word {
    name: String,
    instances: Vec<InstanceCell>,
}

pointer_eq!(Word);
<commit_msg>context for categories<commit_after>extern crate rand;
extern crate itertools;
use self::itertools::Itertools;

use std::collections::BTreeMap;
use std::collections::btree_map::Entry;
use std::rc::Rc;
use std::cell::RefCell;
use std::cell::Ref;
use std::sync::mpsc::Receiver;

use std::fmt;

type WordCell = Rc<RefCell<Word>>;
pub type AuthorCell = Rc<RefCell<Author>>;
pub type SourceCell = Rc<RefCell<Source>>;
type MessageCell = Rc<RefCell<Message>>;
type CategoryCell = Rc<RefCell<Category>>;
type ConversationCell = Rc<RefCell<Conversation>>;
type InstanceCell = Rc<RefCell<WordInstance>>;

enum Mismatch<T> {
    // Incompatible for matching or no mismatch (exactly the same)
    None,
    // Exactly one mismatch
    One(T),
    // Multiple mismatches
    Multiple,
}

fn wrap<T>(t: T) -> Rc<RefCell<T>> {
    Rc::new(RefCell::new(t))
}

// const RATIO_CONTAINED_BEFORE_COMBINATION: f64 = 0.8;

#[derive(Default)]
pub struct Lexicon<R: rand::Rng> {
    rng: R,
    words: BTreeMap<String, WordCell>,
    sources: BTreeMap<String, SourceCell>,
    conversations: Vec<ConversationCell>,
    messages: Vec<MessageCell>,

    active_conversations: BTreeMap<*const Source, ConversationCell>,
}

impl<R: rand::Rng> Lexicon<R> {
    pub fn new(rng: R) -> Lexicon<R> {
        Lexicon{
            rng: rng,
            words: Default::default(),
            sources: Default::default(),
            conversations: Default::default(),
            messages: Default::default(),
            active_conversations: Default::default(),
        }
    }

    pub fn source(&mut self, name: String) -> SourceCell {
        match self.sources.entry(name.clone()) {
            Entry::Vacant(v) => v.insert(wrap(Source{
                name: name,
                messages: 0,
                authors: BTreeMap::default(),
            })).clone(),
            Entry::Occupied(o) => o.get().clone(),
        }
    }

    pub fn author(&mut self, source: SourceCell, name: String) -> AuthorCell {
        match source.borrow_mut().authors.entry(name.clone()) {
            Entry::Vacant(v) => {
                let a = wrap(Author{
                    source: source.clone(),
                    name: name.clone(),
                });
                v.insert(a.clone());
                a
            },
            Entry::Occupied(o) => o.get().clone(),
        }
    }
    pub fn tell(&mut self, source: SourceCell, author: AuthorCell, content: String) -> Option<String> {
        let conversation = match self.active_conversations.entry(&*source.borrow() as *const Source) {
            Entry::Vacant(v) => {
                let c = wrap(Conversation{
                    source: source.clone(),
                    messages: Vec::new(),
                });
                v.insert(c.clone());
                self.conversations.push(c.clone());
                c
            },
            Entry::Occupied(o) => {
                o.get().clone()
            },
        };

        let message = wrap(Message{
            author: author.clone(),
            conversation: conversation.clone(),
            index: conversation.borrow().messages.len(),
            instances: Vec::new(),
        });

        self.messages.push(message.clone());

        for word in content.split(' ').map(|s| {
            let string = s.to_string();
            match self.words.entry(string.clone()) {
                Entry::Vacant(v) => {
                    let w = wrap(Word{
                        name: string,
                        instances: Vec::new(),
                    });
                    v.insert(w.clone());
                    w
                },
                Entry::Occupied(o) => {
                    o.get().clone()
                },
            }
        }) {
            // Create empty category for the word
            let category = wrap(Category::default());
            // Create instance of the word
            let instance = wrap(WordInstance{
                word: word.clone(),
                category: category.clone(),
                message: message.clone(),
                index: message.borrow().instances.len(),
            });
            // Insert word instance into the word, category, and message for future reference
            message.borrow_mut().instances.push(instance.clone());
            category.borrow_mut().instances.push(instance.clone());
            word.borrow_mut().instances.push(instance);
        }

        // Increment the messages by 1 for the source
        source.borrow_mut().messages += 1;

        // TODO: Determine what to say back
        None
    }

    pub fn initiate(&mut self, source: SourceCell) -> Option<String> {
        let conversation = wrap(Conversation{
            source: source.clone(),
            messages: Vec::new(),
        });

        match self.active_conversations.entry(&*source.borrow() as *const Source) {
            Entry::Vacant(v) => {
                v.insert(conversation.clone());
            },
            Entry::Occupied(mut o) => {
                o.insert(conversation.clone());
            },
        };

        self.conversations.push(conversation.clone());

        let base = match self.rng.choose(&self.messages[..]) {
            Some(m) => m.clone(),
            None => return None,
        };

        let bborrow = base.borrow();

        Some(format!("{} ~ {}",
            bborrow.instances.iter()
                .map(|instance| instance.borrow().category.clone())
                // TODO: Allow random choosing from co-category instances as well
                .map(|category| self.rng.choose(&category.borrow().instances[..]).unwrap().clone())
                .map(|instance| {
                    let ins = instance.borrow();
                    let word = ins.word.borrow();
                    word.name.clone()
                })
                .join(" "),
            bborrow.instances.iter()
                .map(|i| {
                    let b = i.borrow();
                    let b = b.word.borrow();
                    b.name.clone()
                })
                .join(" "))
        )
    }

    pub fn think(&mut self, end: Receiver<()>) {
        use std::sync::mpsc::TryRecvError::Empty;
        while end.try_recv() == Err(Empty) {
            // Choose a random message or break if there are none
            let message = match self.rng.choose(&self.messages[..]) {
                Some(m) => m.clone(),
                None => break,
            };

            // Vector of absolute perfect matches
            let mut vones = Vec::new();

            // Look through each word in the message
            for word in &message.borrow().instances {
                let borrow = word.borrow();
                // Check each instance in that words instances
                for instance in &borrow.word.borrow().instances {
                    // Get the message for each instance
                    let omessage = instance.borrow().message.clone();
                    // Find what kind of matches exist between the messages
                    if let Mismatch::One(best) =
                        Message::category_and_word_mismatch((message.clone(), omessage.clone())) {
                        vones.push(best);
                    }
                }
            }

            // Now that we have perfect matches, merge them into the same Category
            for ms in vones {
                // We only want to combine if they aren't already in the same category
                if ms.0.borrow().category != ms.1.borrow().category {
                    let cats = (ms.0.borrow().category.clone(), ms.1.borrow().category.clone());
                    Category::merge(cats);
                }
            }
        }
    }

    /// Print all multiple categories and return the amount of categories total
    pub fn show_categories(&self) -> usize {
        use std::collections::BTreeSet;
        let mut set = BTreeSet::new();
        for message in &self.messages {
            for instance in &message.borrow().instances {
                let ib = instance.borrow();
                set.insert(&*ib.category.borrow() as *const Category);
            }
        }

        let len = set.len();

        for cat in set {
            let catr = unsafe{&*cat};
            if catr.instances.len() != 1 {
                println!("Category:");
                for instance in &catr.instances {
                    let ib = instance.borrow();
                    println!("\t{} ~ {}", ib.word.borrow().name, &*ib.message.borrow());
                }
            }
        }
        len
    }
}

macro_rules! pointer_eq {
    ($s:ident) => {
        impl PartialEq for $s {
            fn eq(&self, other: &Self) -> bool {
                self as *const Self == other as *const Self
            }

            fn ne(&self, other: &Self) -> bool {
                self as *const Self != other as *const Self
            }
        }
    };
}

struct Conversation {
    source: SourceCell,
    messages: Vec<MessageCell>,
}

pointer_eq!(Conversation);

pub struct Author {
    source: SourceCell,
    name: String,
}

pointer_eq!(Author);

pub struct Source {
    name: String,
    messages: u64,
    authors: BTreeMap<String, AuthorCell>,
}

pointer_eq!(Source);

struct WordInstance {
    word: WordCell,
    category: CategoryCell,
    message: MessageCell,
    index: usize,
}

pointer_eq!(WordInstance);

struct Message {
    author: AuthorCell,
    conversation: ConversationCell,
    index: usize,
    instances: Vec<InstanceCell>,
}

impl Message {
    fn string(&self) -> String {
        self.instances.iter()
            .map(|i| {
                let b = i.borrow();
                let b = b.word.borrow();
                b.name.clone()
            })
            .join(" ")
    }
    fn mismatch<F>(messages: (MessageCell, MessageCell), diff: F) -> Mismatch<(InstanceCell, InstanceCell)>
        where F: Fn((&WordInstance, &WordInstance)) -> bool
    {
        if messages.0 == messages.1 ||
            messages.0.borrow().instances.len() != messages.1.borrow().instances.len() {
            return Mismatch::None;
        }
        messages.0.borrow().instances.iter()
            .zip(messages.1.borrow().instances.iter())
            .fold(Mismatch::None, |acc, ws| {
                let bs = (ws.0.borrow(), ws.1.borrow());
                match acc {
                    Mismatch::None => {
                        if diff((&*bs.0, &*bs.1)) {
                            Mismatch::One((ws.0.clone(), ws.1.clone()))
                        } else {
                            Mismatch::None
                        }
                    },
                    Mismatch::One(best) => {
                        if diff((&*bs.0, &*bs.1)) {
                            Mismatch::Multiple
                        } else {
                            Mismatch::One(best)
                        }
                    },
                    Mismatch::Multiple => Mismatch::Multiple,
                }
            })
    }
    /*fn category_mismatch(messages: (MessageCell, MessageCell)) ->
        Mismatch<(InstanceCell, InstanceCell)> {
        Self::mismatch(messages, |ins| ins.0.category != ins.1.category)
    }

    fn word_mismatch(messages: (MessageCell, MessageCell)) ->
        Mismatch<(InstanceCell, InstanceCell)> {
        Self::mismatch(messages, |ins| ins.0.word != ins.1.word)
    }*/

    fn category_and_word_mismatch(messages: (MessageCell, MessageCell)) ->
        Mismatch<(InstanceCell, InstanceCell)> {
        Self::mismatch(messages, |ins| ins.0.word != ins.1.word && ins.0.category != ins.1.category)
    }
}

impl fmt::Display for Message {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.string())
    }
}

pointer_eq!(Message);

#[derive(Default)]
struct Category {
    instances: Vec<InstanceCell>,
    cocategories: Vec<CategoryCell>,
}

impl Category {
    fn merge(cs: (CategoryCell, CategoryCell)) -> CategoryCell {
        let cc = wrap(Self::default());
        {
            let clos = |cat: &CategoryCell| {
                for instance in cat.borrow().instances.iter().cloned() {
                    instance.borrow_mut().category = cc.clone();
                }
            };
            clos(&cs.0);
            clos(&cs.1);
            let mut ccm = cc.borrow_mut();
            ccm.instances.append(&mut cs.0.borrow_mut().instances);
            ccm.instances.append(&mut cs.1.borrow_mut().instances);
        }
        cc
    }
}

pointer_eq!(Category);

struct Word {
    name: String,
    instances: Vec<InstanceCell>,
}

pointer_eq!(Word);
<|endoftext|>