<commit_before>//! Copy-pasted from the internet

use std::fmt;

/// Available encoding character sets
#[derive(Clone, Copy, Debug)]
enum _CharacterSet {
    /// The standard character set (uses `+` and `/`)
    _Standard,
    /// The URL safe character set (uses `-` and `_`)
    _UrlSafe,
}

static STANDARD_CHARS: &'static [u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ\
                                        abcdefghijklmnopqrstuvwxyz\
                                        0123456789+/";

static _URLSAFE_CHARS: &'static [u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ\
                                       abcdefghijklmnopqrstuvwxyz\
                                       0123456789-_";

pub fn encode(input: &[u8]) -> String {
    let bytes = STANDARD_CHARS;

    let len = input.len();

    // Preallocate memory.
    let prealloc_len = (len + 2) / 3 * 4;
    let mut out_bytes = vec![b'='; prealloc_len];

    // Deal with padding bytes
    let mod_len = len % 3;

    // Use iterators to reduce branching
    {
        let mut s_in = input[..len - mod_len].iter().map(|&x| x as u32);
        let mut s_out = out_bytes.iter_mut();

        // Convenient shorthand
        let enc = |val| bytes[val as usize];
        let mut write = |val| *s_out.next().unwrap() = val;

        // Iterate though blocks of 4
        while let (Some(first), Some(second), Some(third)) = (s_in.next(), s_in.next(), s_in.next())
        {
            let n = first << 16 | second << 8 | third;

            // This 24-bit number gets separated into four 6-bit numbers.
            write(enc((n >> 18) & 63));
            write(enc((n >> 12) & 63));
            write(enc((n >> 6) & 63));
            write(enc((n >> 0) & 63));
        }

        // Heh, would be cool if we knew this was exhaustive
        // (the dream of bounded integer types)
        match mod_len {
            0 => (),
            1 => {
                let n = (input[len - 1] as u32) << 16;
                write(enc((n >> 18) & 63));
                write(enc((n >> 12) & 63));
            }
            2 => {
                let n = (input[len - 2] as u32) << 16 | (input[len - 1] as u32) << 8;
                write(enc((n >> 18) & 63));
                write(enc((n >> 12) & 63));
                write(enc((n >> 6) & 63));
            }
            _ => panic!("Algebra is broken, please alert the math police"),
        }
    }

    // `out_bytes` vec is prepopulated with `=` symbols and then only updated
    // with base64 chars, so this unsafe is safe.
    unsafe { String::from_utf8_unchecked(out_bytes) }
}

/// Errors that can occur when decoding a base64 encoded string
#[derive(Clone, Copy, Debug, thiserror::Error)]
pub enum FromBase64Error {
    /// The input contained a character not part of the base64 format
    #[error("Invalid base64 byte")]
    InvalidBase64Byte(u8, usize),
    /// The input had an invalid length
    #[error("Invalid base64 length")]
    InvalidBase64Length,
}

pub fn decode(input: &str) -> Result<Vec<u8>, FromBase64Error> {
    let mut r = Vec::with_capacity(input.len());
    let mut buf: u32 = 0;
    let mut modulus = 0;

    let mut it = input.as_bytes().iter();
    for byte in it.by_ref() {
        let code = DECODE_TABLE[*byte as usize];
        if code >= SPECIAL_CODES_START {
            match code {
                NEWLINE_CODE => continue,
                EQUALS_CODE => break,
                INVALID_CODE => {
                    return Err(FromBase64Error::InvalidBase64Byte(
                        *byte,
                        (byte as *const _ as usize) - input.as_ptr() as usize,
                    ))
                }
                _ => unreachable!(),
            }
        }
        buf = (buf | code as u32) << 6;
        modulus += 1;
        if modulus == 4 {
            modulus = 0;
            r.push((buf >> 22) as u8);
            r.push((buf >> 14) as u8);
            r.push((buf >> 6) as u8);
        }
    }

    for byte in it {
        match *byte {
            b'=' | b'\r' | b'\n' => continue,
            _ => {
                return Err(FromBase64Error::InvalidBase64Byte(
                    *byte,
                    (byte as *const _ as usize) - input.as_ptr() as usize,
                ))
            }
        }
    }

    match modulus {
        2 => {
            r.push((buf >> 10) as u8);
        }
        3 => {
            r.push((buf >> 16) as u8);
            r.push((buf >> 8) as u8);
        }
        0 => (),
        _ => return Err(FromBase64Error::InvalidBase64Length),
    }

    Ok(r)
}

const DECODE_TABLE: [u8; 256] = [
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFD, 0xFF, 0xFF, 0xFD, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3E, 0xFF, 0x3E, 0xFF, 0x3F,
    0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF,
    0xFF, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E,
    0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0xFF, 0xFF, 0xFF, 0xFF, 0x3F,
    0xFF, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
    0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
];
const INVALID_CODE: u8 = 0xFF;
const EQUALS_CODE: u8 = 0xFE;
const NEWLINE_CODE: u8 = 0xFD;
const SPECIAL_CODES_START: u8 = NEWLINE_CODE;

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

    #[test]
    fn test_encode_basic() {
        assert_eq!(encode(b""), "");
        assert_eq!(encode(b"f"), "Zg==");
        assert_eq!(encode(b"fo"), "Zm8=");
        assert_eq!(encode(b"foo"), "Zm9v");
        assert_eq!(encode(b"foob"), "Zm9vYg==");
        assert_eq!(encode(b"fooba"), "Zm9vYmE=");
        assert_eq!(encode(b"foobar"), "Zm9vYmFy");
    }

    #[test]
    fn test_encode_standard_safe() {
        assert_eq!(encode(&[251, 255]), "+/8=");
    }

    #[test]
    fn test_decode_basic() {
        assert_eq!(decode("").unwrap(), b"");
        assert_eq!(decode("Zg==").unwrap(), b"f");
        assert_eq!(decode("Zm8=").unwrap(), b"fo");
        assert_eq!(decode("Zm9v").unwrap(), b"foo");
        assert_eq!(decode("Zm9vYg==").unwrap(), b"foob");
        assert_eq!(decode("Zm9vYmE=").unwrap(), b"fooba");
        assert_eq!(decode("Zm9vYmFy").unwrap(), b"foobar");
    }

    #[test]
    fn test_decode() {
        assert_eq!(decode("Zm9vYmFy").unwrap(), b"foobar");
    }

    #[test]
    fn test_decode_newlines() {
        assert_eq!(decode("Zm9v\r\nYmFy").unwrap(), b"foobar");
        assert_eq!(decode("Zm9vYg==\r\n").unwrap(), b"foob");
        assert_eq!(decode("Zm9v\nYmFy").unwrap(), b"foobar");
        assert_eq!(decode("Zm9vYg==\n").unwrap(), b"foob");
    }

    #[test]
    fn test_decode_urlsafe() {
        assert_eq!(decode("-_8").unwrap(), decode("+/8=").unwrap());
    }

    #[test]
    fn test_from_base64_invalid_char() {
        assert!(decode("Zm$=").is_err());
        assert!(decode("Zg==$").is_err());
    }

    #[test]
    fn test_decode_invalid_padding() {
        assert!(decode("Z===").is_err());
    }
}
<commit_msg>Fix unused import<commit_after>//! Copy-pasted from the internet
/// Available encoding character sets
#[derive(Clone, Copy, Debug)]
enum _CharacterSet {
    /// The standard character set (uses `+` and `/`)
    _Standard,
    /// The URL safe character set (uses `-` and `_`)
    _UrlSafe,
}

static STANDARD_CHARS: &'static [u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ\
                                        abcdefghijklmnopqrstuvwxyz\
                                        0123456789+/";

static _URLSAFE_CHARS: &'static [u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ\
                                       abcdefghijklmnopqrstuvwxyz\
                                       0123456789-_";

pub fn encode(input: &[u8]) -> String {
    let bytes = STANDARD_CHARS;

    let len = input.len();

    // Preallocate memory.
    let prealloc_len = (len + 2) / 3 * 4;
    let mut out_bytes = vec![b'='; prealloc_len];

    // Deal with padding bytes
    let mod_len = len % 3;

    // Use iterators to reduce branching
    {
        let mut s_in = input[..len - mod_len].iter().map(|&x| x as u32);
        let mut s_out = out_bytes.iter_mut();

        // Convenient shorthand
        let enc = |val| bytes[val as usize];
        let mut write = |val| *s_out.next().unwrap() = val;

        // Iterate though blocks of 4
        while let (Some(first), Some(second), Some(third)) = (s_in.next(), s_in.next(), s_in.next())
        {
            let n = first << 16 | second << 8 | third;

            // This 24-bit number gets separated into four 6-bit numbers.
            write(enc((n >> 18) & 63));
            write(enc((n >> 12) & 63));
            write(enc((n >> 6) & 63));
            write(enc((n >> 0) & 63));
        }

        // Heh, would be cool if we knew this was exhaustive
        // (the dream of bounded integer types)
        match mod_len {
            0 => (),
            1 => {
                let n = (input[len - 1] as u32) << 16;
                write(enc((n >> 18) & 63));
                write(enc((n >> 12) & 63));
            }
            2 => {
                let n = (input[len - 2] as u32) << 16 | (input[len - 1] as u32) << 8;
                write(enc((n >> 18) & 63));
                write(enc((n >> 12) & 63));
                write(enc((n >> 6) & 63));
            }
            _ => panic!("Algebra is broken, please alert the math police"),
        }
    }

    // `out_bytes` vec is prepopulated with `=` symbols and then only updated
    // with base64 chars, so this unsafe is safe.
    unsafe { String::from_utf8_unchecked(out_bytes) }
}

/// Errors that can occur when decoding a base64 encoded string
#[derive(Clone, Copy, Debug, thiserror::Error)]
pub enum FromBase64Error {
    /// The input contained a character not part of the base64 format
    #[error("Invalid base64 byte")]
    InvalidBase64Byte(u8, usize),
    /// The input had an invalid length
    #[error("Invalid base64 length")]
    InvalidBase64Length,
}

pub fn decode(input: &str) -> Result<Vec<u8>, FromBase64Error> {
    let mut r = Vec::with_capacity(input.len());
    let mut buf: u32 = 0;
    let mut modulus = 0;

    let mut it = input.as_bytes().iter();
    for byte in it.by_ref() {
        let code = DECODE_TABLE[*byte as usize];
        if code >= SPECIAL_CODES_START {
            match code {
                NEWLINE_CODE => continue,
                EQUALS_CODE => break,
                INVALID_CODE => {
                    return Err(FromBase64Error::InvalidBase64Byte(
                        *byte,
                        (byte as *const _ as usize) - input.as_ptr() as usize,
                    ))
                }
                _ => unreachable!(),
            }
        }
        buf = (buf | code as u32) << 6;
        modulus += 1;
        if modulus == 4 {
            modulus = 0;
            r.push((buf >> 22) as u8);
            r.push((buf >> 14) as u8);
            r.push((buf >> 6) as u8);
        }
    }

    for byte in it {
        match *byte {
            b'=' | b'\r' | b'\n' => continue,
            _ => {
                return Err(FromBase64Error::InvalidBase64Byte(
                    *byte,
                    (byte as *const _ as usize) - input.as_ptr() as usize,
                ))
            }
        }
    }

    match modulus {
        2 => {
            r.push((buf >> 10) as u8);
        }
        3 => {
            r.push((buf >> 16) as u8);
            r.push((buf >> 8) as u8);
        }
        0 => (),
        _ => return Err(FromBase64Error::InvalidBase64Length),
    }

    Ok(r)
}

const DECODE_TABLE: [u8; 256] = [
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFD, 0xFF, 0xFF, 0xFD, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3E, 0xFF, 0x3E, 0xFF, 0x3F,
    0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF,
    0xFF, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E,
    0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0xFF, 0xFF, 0xFF, 0xFF, 0x3F,
    0xFF, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
    0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
];
const INVALID_CODE: u8 = 0xFF;
const EQUALS_CODE: u8 = 0xFE;
const NEWLINE_CODE: u8 = 0xFD;
const SPECIAL_CODES_START: u8 = NEWLINE_CODE;

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

    #[test]
    fn test_encode_basic() {
        assert_eq!(encode(b""), "");
        assert_eq!(encode(b"f"), "Zg==");
        assert_eq!(encode(b"fo"), "Zm8=");
        assert_eq!(encode(b"foo"), "Zm9v");
        assert_eq!(encode(b"foob"), "Zm9vYg==");
        assert_eq!(encode(b"fooba"), "Zm9vYmE=");
        assert_eq!(encode(b"foobar"), "Zm9vYmFy");
    }

    #[test]
    fn test_encode_standard_safe() {
        assert_eq!(encode(&[251, 255]), "+/8=");
    }

    #[test]
    fn test_decode_basic() {
        assert_eq!(decode("").unwrap(), b"");
        assert_eq!(decode("Zg==").unwrap(), b"f");
        assert_eq!(decode("Zm8=").unwrap(), b"fo");
        assert_eq!(decode("Zm9v").unwrap(), b"foo");
        assert_eq!(decode("Zm9vYg==").unwrap(), b"foob");
        assert_eq!(decode("Zm9vYmE=").unwrap(), b"fooba");
        assert_eq!(decode("Zm9vYmFy").unwrap(), b"foobar");
    }

    #[test]
    fn test_decode() {
        assert_eq!(decode("Zm9vYmFy").unwrap(), b"foobar");
    }

    #[test]
    fn test_decode_newlines() {
        assert_eq!(decode("Zm9v\r\nYmFy").unwrap(), b"foobar");
        assert_eq!(decode("Zm9vYg==\r\n").unwrap(), b"foob");
        assert_eq!(decode("Zm9v\nYmFy").unwrap(), b"foobar");
        assert_eq!(decode("Zm9vYg==\n").unwrap(), b"foob");
    }

    #[test]
    fn test_decode_urlsafe() {
        assert_eq!(decode("-_8").unwrap(), decode("+/8=").unwrap());
    }

    #[test]
    fn test_from_base64_invalid_char() {
        assert!(decode("Zm$=").is_err());
        assert!(decode("Zg==$").is_err());
    }

    #[test]
    fn test_decode_invalid_padding() {
        assert!(decode("Z===").is_err());
    }
}
<|endoftext|>


<commit_before>use gl::types::*;
use id_allocator::IdAllocator;
use nalgebra::{normalize, cross};
use nalgebra::{Pnt3, Vec3};
use ncollide::bounding_volume::{AABB, AABB3};
use noise::{Seed, Brownian2, Point2};
use state::EntityId;
use std::cmp::partial_max;
use std::collections::hash_map::HashMap;
use std::num::Float;
use std::ops::Add;
use stopwatch::TimerSet;
use terrain::{AMPLITUDE, TerrainType};

pub const BLOCK_WIDTH: i32 = 4;

#[derive(Show, PartialEq, Eq, Hash, Copy, Clone)]
pub struct BlockPosition(Pnt3<i32>);

impl BlockPosition {
  #[inline(always)]
  pub fn new(x: i32, y: i32, z: i32) -> BlockPosition {
    BlockPosition(Pnt3::new(x, y, z))
  }

  #[inline(always)]
  pub fn as_pnt<'a>(&'a self) -> &'a Pnt3<i32> {
    let BlockPosition(ref pnt) = *self;
    pnt
  }

  #[inline(always)]
  pub fn as_mut_pnt3<'a>(&'a mut self) -> &'a mut Pnt3<i32> {
    let BlockPosition(ref mut pnt) = *self;
    pnt
  }

  pub fn from_world_position(world_position: &Pnt3<f32>) -> BlockPosition {
    macro_rules! convert_coordinate(
      ($x: expr) => ({
        let x = $x.floor() as i32;
        let x =
          if x < 0 {
            x - (BLOCK_WIDTH - 1)
          } else {
            x
          };
        x / BLOCK_WIDTH
      })
    );
    BlockPosition(
      Pnt3::new(
        convert_coordinate!(world_position.x),
        convert_coordinate!(world_position.y),
        convert_coordinate!(world_position.z),
      )
    )
  }

  pub fn to_world_position(&self) -> Pnt3<f32> {
    Pnt3::new(
      (self.as_pnt().x * BLOCK_WIDTH) as f32,
      (self.as_pnt().y * BLOCK_WIDTH) as f32,
      (self.as_pnt().z * BLOCK_WIDTH) as f32,
    )
  }
}

impl Add<Vec3<i32>> for BlockPosition {
  type Output = BlockPosition;

  fn add(mut self, rhs: Vec3<i32>) -> Self {
    self.as_mut_pnt3().x += rhs.x;
    self.as_mut_pnt3().y += rhs.y;
    self.as_mut_pnt3().z += rhs.z;
    self
  }
}

/// Ductyped heightmap methods.
trait HeightMapExt {
  /// The coordinate of the tile at a given x/z.
  fn point_at(&self, seed: &Seed, x: f32, z: f32) -> Pnt3<f32>;
  /// The lighting normal of the tile at a given x/z.
  fn normal_at(&self, seed: &Seed, delta: f32, x: f32, z: f32) -> Vec3<f32>;
}

impl HeightMapExt for Brownian2<f64, fn (&Seed, &Point2<f64>) -> f64> {
  fn point_at(&self, seed: &Seed, x: f32, z: f32) -> Pnt3<f32> {
    let y = AMPLITUDE * (self.call((seed, &[x as f64, z as f64])) + 1.0) / 2.0;
    Pnt3::new(x as f32, y as f32, z as f32)
  }

  fn normal_at(&self, seed: &Seed, delta: f32, x: f32, z: f32) -> Vec3<f32> {
    let dx =
      self.point_at(seed, x + delta, z).to_vec()
      - self.point_at(seed, x - delta, z).to_vec();
    let dz =
      self.point_at(seed, x, z + delta).to_vec()
      - self.point_at(seed, x, z - delta).to_vec();
    normalize(&cross(&dz, &dx))
  }
}

pub struct TerrainBlock {
  // These Vecs must all be ordered the same way.

  // vertex coordinates flattened into separate GLfloats (x, y, z order)
  pub vertex_coordinates: Vec<GLfloat>,
  // per-vertex normal vectors flattened into separate GLfloats (x, y, z order)
  pub normals: Vec<GLfloat>,
  // per-triangle terrain types
  pub typs: Vec<GLuint>,

  // per-triangle entity IDs
  pub ids: Vec<EntityId>,
  // per-triangle bounding boxes
  pub bounds: HashMap<EntityId, AABB3<GLfloat>>,
}

impl TerrainBlock {
  pub fn empty() -> TerrainBlock {
    TerrainBlock {
      vertex_coordinates: Vec::new(),
      normals: Vec::new(),
      typs: Vec::new(),
      ids: Vec::new(),
      bounds: HashMap::new(),
    }
  }

  pub fn generate<HeightMap: HeightMapExt>(
    timers: &TimerSet,
    id_allocator: &mut IdAllocator<EntityId>,
    heightmap: &HeightMap,
    position: &BlockPosition,
    lateral_samples: u32,
    seed: &Seed,
  ) -> TerrainBlock {
    timers.time("update.generate_block", || {
      let mut block = TerrainBlock::empty();

      let x = (position.as_pnt().x * BLOCK_WIDTH) as f32;
      let y = (position.as_pnt().y * BLOCK_WIDTH) as f32;
      let z = (position.as_pnt().z * BLOCK_WIDTH) as f32;

      let sample_width = BLOCK_WIDTH as f32 / lateral_samples as f32;

      for dx in range(0, lateral_samples) {
        let x = x + dx as f32 * sample_width;
        for dz in range(0, lateral_samples) {
          let z = z + dz as f32 * sample_width;
          let position = Pnt3::new(x, y, z);
          TerrainBlock::add_tile(
            timers,
            heightmap,
            id_allocator,
            &mut block,
            sample_width,
            &position,
            seed,
          );
        }
      }

      block
    })
  }

  fn add_tile<'a, HeightMap: HeightMapExt>(
    timers: &TimerSet,
    hm: &HeightMap,
    id_allocator: &mut IdAllocator<EntityId>,
    block: &mut TerrainBlock,
    sample_width: f32,
    position: &Pnt3<f32>,
    seed: &Seed,
  ) {
    let half_width = sample_width / 2.0;
    let center = hm.point_at(seed, position.x + half_width, position.z + half_width);

    if position.y >= center.y || center.y > position.y + BLOCK_WIDTH as f32 {
      return;
    }

    timers.time("update.generate_block.add_tile", || {
      let normal_delta = sample_width / 2.0;
      let center_normal =
        hm.normal_at(seed, normal_delta, position.x + half_width, position.z + half_width);

      let x2 = position.x + sample_width;
      let z2 = position.z + sample_width;

      let ps: [Pnt3<f32>; 4] =
        [ hm.point_at(seed, position.x, position.z)
        , hm.point_at(seed, position.x, z2)
        , hm.point_at(seed, x2, z2)
        , hm.point_at(seed, x2, position.z)
        ];

      let ns: [Vec3<f32>; 4] =
        [ hm.normal_at(seed, normal_delta, position.x, position.z)
        , hm.normal_at(seed, normal_delta, position.x, z2)
        , hm.normal_at(seed, normal_delta, x2, z2)
        , hm.normal_at(seed, normal_delta, x2, position.z)
        ];

      let center_lower_than = ps.iter().filter(|v| center.y < v.y).count();

      let terrain_type =
        if center_lower_than >= 3 {
          TerrainType::Dirt
        } else {
          TerrainType::Grass
        };

      macro_rules! place_terrain(
        ($v1: expr,
         $v2: expr,
         $n1: expr,
         $n2: expr,
         $minx: expr,
         $minz: expr,
         $maxx: expr,
         $maxz: expr
        ) => ({
          let maxy = partial_max($v1.y, $v2.y);
          let maxy = maxy.and_then(|m| partial_max(m, center.y));
          let maxy = maxy.unwrap();

          let id = id_allocator.allocate();
          block.vertex_coordinates.push_all(&[
            $v1.x, $v1.y, $v1.z,
            $v2.x, $v2.y, $v2.z,
            center.x, center.y, center.z,
          ]);
          block.normals.push_all(&[
            $n1.x, $n1.y, $n1.z,
            $n2.x, $n2.y, $n2.z,
            center_normal.x, center_normal.y, center_normal.z,
          ]);
          block.typs.push(terrain_type as GLuint);
          block.ids.push(id);
          block.bounds.insert(
            id,
            AABB::new(
              Pnt3::new($minx, $v1.y, $minz),
              Pnt3::new($maxx, maxy, $maxz),
            ),
          );
        });
      );

      let placements = 4;
      block.vertex_coordinates.reserve(placements*9);
      block.normals.reserve(placements*9);
      block.typs.reserve(placements);
      block.ids.reserve(placements);
      block.bounds.reserve(placements);

      let centr = center; // makes alignment nice
      place_terrain!(&ps[0], &ps[1], &ns[0], &ns[1], ps[0].x, ps[0].z, centr.x, ps[1].z);
      place_terrain!(&ps[1], &ps[2], &ns[1], &ns[2], ps[1].x, centr.z, ps[2].x, ps[2].z);
      place_terrain!(&ps[2], &ps[3], &ns[2], &ns[3], centr.x, centr.z, ps[2].x, ps[2].z);
      place_terrain!(&ps[3], &ps[0], &ns[3], &ns[0], ps[0].x, ps[0].z, ps[3].x, centr.z);
    })
  }
}
<commit_msg>Start using Stone<commit_after>use gl::types::*;
use id_allocator::IdAllocator;
use nalgebra::{normalize, cross};
use nalgebra::{Pnt3, Vec3};
use ncollide::bounding_volume::{AABB, AABB3};
use noise::{Seed, Brownian2, Point2};
use state::EntityId;
use std::cmp::partial_max;
use std::collections::hash_map::HashMap;
use std::num::Float;
use std::ops::Add;
use stopwatch::TimerSet;
use terrain::{AMPLITUDE, TerrainType};

pub const BLOCK_WIDTH: i32 = 4;

#[derive(Show, PartialEq, Eq, Hash, Copy, Clone)]
pub struct BlockPosition(Pnt3<i32>);

impl BlockPosition {
  #[inline(always)]
  pub fn new(x: i32, y: i32, z: i32) -> BlockPosition {
    BlockPosition(Pnt3::new(x, y, z))
  }

  #[inline(always)]
  pub fn as_pnt<'a>(&'a self) -> &'a Pnt3<i32> {
    let BlockPosition(ref pnt) = *self;
    pnt
  }

  #[inline(always)]
  pub fn as_mut_pnt3<'a>(&'a mut self) -> &'a mut Pnt3<i32> {
    let BlockPosition(ref mut pnt) = *self;
    pnt
  }

  pub fn from_world_position(world_position: &Pnt3<f32>) -> BlockPosition {
    macro_rules! convert_coordinate(
      ($x: expr) => ({
        let x = $x.floor() as i32;
        let x =
          if x < 0 {
            x - (BLOCK_WIDTH - 1)
          } else {
            x
          };
        x / BLOCK_WIDTH
      })
    );
    BlockPosition(
      Pnt3::new(
        convert_coordinate!(world_position.x),
        convert_coordinate!(world_position.y),
        convert_coordinate!(world_position.z),
      )
    )
  }

  pub fn to_world_position(&self) -> Pnt3<f32> {
    Pnt3::new(
      (self.as_pnt().x * BLOCK_WIDTH) as f32,
      (self.as_pnt().y * BLOCK_WIDTH) as f32,
      (self.as_pnt().z * BLOCK_WIDTH) as f32,
    )
  }
}

impl Add<Vec3<i32>> for BlockPosition {
  type Output = BlockPosition;

  fn add(mut self, rhs: Vec3<i32>) -> Self {
    self.as_mut_pnt3().x += rhs.x;
    self.as_mut_pnt3().y += rhs.y;
    self.as_mut_pnt3().z += rhs.z;
    self
  }
}

/// Ductyped heightmap methods.
trait HeightMapExt {
  /// The coordinate of the tile at a given x/z.
  fn point_at(&self, seed: &Seed, x: f32, z: f32) -> Pnt3<f32>;
  /// The lighting normal of the tile at a given x/z.
  fn normal_at(&self, seed: &Seed, delta: f32, x: f32, z: f32) -> Vec3<f32>;
}

impl HeightMapExt for Brownian2<f64, fn (&Seed, &Point2<f64>) -> f64> {
  fn point_at(&self, seed: &Seed, x: f32, z: f32) -> Pnt3<f32> {
    let y = AMPLITUDE * (self.call((seed, &[x as f64, z as f64])) + 1.0) / 2.0;
    Pnt3::new(x as f32, y as f32, z as f32)
  }

  fn normal_at(&self, seed: &Seed, delta: f32, x: f32, z: f32) -> Vec3<f32> {
    let dx =
      self.point_at(seed, x + delta, z).to_vec()
      - self.point_at(seed, x - delta, z).to_vec();
    let dz =
      self.point_at(seed, x, z + delta).to_vec()
      - self.point_at(seed, x, z - delta).to_vec();
    normalize(&cross(&dz, &dx))
  }
}

pub struct TerrainBlock {
  // These Vecs must all be ordered the same way.

  // vertex coordinates flattened into separate GLfloats (x, y, z order)
  pub vertex_coordinates: Vec<GLfloat>,
  // per-vertex normal vectors flattened into separate GLfloats (x, y, z order)
  pub normals: Vec<GLfloat>,
  // per-triangle terrain types
  pub typs: Vec<GLuint>,

  // per-triangle entity IDs
  pub ids: Vec<EntityId>,
  // per-triangle bounding boxes
  pub bounds: HashMap<EntityId, AABB3<GLfloat>>,
}

impl TerrainBlock {
  pub fn empty() -> TerrainBlock {
    TerrainBlock {
      vertex_coordinates: Vec::new(),
      normals: Vec::new(),
      typs: Vec::new(),
      ids: Vec::new(),
      bounds: HashMap::new(),
    }
  }

  pub fn generate<HeightMap: HeightMapExt>(
    timers: &TimerSet,
    id_allocator: &mut IdAllocator<EntityId>,
    heightmap: &HeightMap,
    position: &BlockPosition,
    lateral_samples: u32,
    seed: &Seed,
  ) -> TerrainBlock {
    timers.time("update.generate_block", || {
      let mut block = TerrainBlock::empty();

      let x = (position.as_pnt().x * BLOCK_WIDTH) as f32;
      let y = (position.as_pnt().y * BLOCK_WIDTH) as f32;
      let z = (position.as_pnt().z * BLOCK_WIDTH) as f32;

      let sample_width = BLOCK_WIDTH as f32 / lateral_samples as f32;

      for dx in range(0, lateral_samples) {
        let x = x + dx as f32 * sample_width;
        for dz in range(0, lateral_samples) {
          let z = z + dz as f32 * sample_width;
          let position = Pnt3::new(x, y, z);
          TerrainBlock::add_tile(
            timers,
            heightmap,
            id_allocator,
            &mut block,
            sample_width,
            &position,
            seed,
          );
        }
      }

      block
    })
  }

  fn add_tile<'a, HeightMap: HeightMapExt>(
    timers: &TimerSet,
    hm: &HeightMap,
    id_allocator: &mut IdAllocator<EntityId>,
    block: &mut TerrainBlock,
    sample_width: f32,
    position: &Pnt3<f32>,
    seed: &Seed,
  ) {
    let half_width = sample_width / 2.0;
    let center = hm.point_at(seed, position.x + half_width, position.z + half_width);

    if position.y >= center.y || center.y > position.y + BLOCK_WIDTH as f32 {
      return;
    }

    timers.time("update.generate_block.add_tile", || {
      let normal_delta = sample_width / 2.0;
      let center_normal =
        hm.normal_at(seed, normal_delta, position.x + half_width, position.z + half_width);

      let x2 = position.x + sample_width;
      let z2 = position.z + sample_width;

      let ps: [Pnt3<f32>; 4] =
        [ hm.point_at(seed, position.x, position.z)
        , hm.point_at(seed, position.x, z2)
        , hm.point_at(seed, x2, z2)
        , hm.point_at(seed, x2, position.z)
        ];

      let ns: [Vec3<f32>; 4] =
        [ hm.normal_at(seed, normal_delta, position.x, position.z)
        , hm.normal_at(seed, normal_delta, position.x, z2)
        , hm.normal_at(seed, normal_delta, x2, z2)
        , hm.normal_at(seed, normal_delta, x2, position.z)
        ];

      let center_lower_than = ps.iter().filter(|v| center.y < v.y).count();

      let terrain_type =
        if center_lower_than == 4 {
          TerrainType::Stone
        } else if center_lower_than == 3 {
          TerrainType::Dirt
        } else {
          TerrainType::Grass
        };

      macro_rules! place_terrain(
        ($v1: expr,
         $v2: expr,
         $n1: expr,
         $n2: expr,
         $minx: expr,
         $minz: expr,
         $maxx: expr,
         $maxz: expr
        ) => ({
          let maxy = partial_max($v1.y, $v2.y);
          let maxy = maxy.and_then(|m| partial_max(m, center.y));
          let maxy = maxy.unwrap();

          let id = id_allocator.allocate();
          block.vertex_coordinates.push_all(&[
            $v1.x, $v1.y, $v1.z,
            $v2.x, $v2.y, $v2.z,
            center.x, center.y, center.z,
          ]);
          block.normals.push_all(&[
            $n1.x, $n1.y, $n1.z,
            $n2.x, $n2.y, $n2.z,
            center_normal.x, center_normal.y, center_normal.z,
          ]);
          block.typs.push(terrain_type as GLuint);
          block.ids.push(id);
          block.bounds.insert(
            id,
            AABB::new(
              Pnt3::new($minx, $v1.y, $minz),
              Pnt3::new($maxx, maxy, $maxz),
            ),
          );
        });
      );

      let placements = 4;
      block.vertex_coordinates.reserve(placements*9);
      block.normals.reserve(placements*9);
      block.typs.reserve(placements);
      block.ids.reserve(placements);
      block.bounds.reserve(placements);

      let centr = center; // makes alignment nice
      place_terrain!(&ps[0], &ps[1], &ns[0], &ns[1], ps[0].x, ps[0].z, centr.x, ps[1].z);
      place_terrain!(&ps[1], &ps[2], &ns[1], &ns[2], ps[1].x, centr.z, ps[2].x, ps[2].z);
      place_terrain!(&ps[2], &ps[3], &ns[2], &ns[3], centr.x, centr.z, ps[2].x, ps[2].z);
      place_terrain!(&ps[3], &ps[0], &ns[3], &ns[0], ps[0].x, ps[0].z, ps[3].x, centr.z);
    })
  }
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use std::fmt;
use std::str::FromStr;

use intern::string_key::Intern;
use intern::string_key::StringKey;

/// Utility to access an item in a list by its name where name is defined by the
/// `Named` trait.
pub trait NamedItem<'a, T: Named> {
    fn named(self, name: <T as Named>::Name) -> Option<&'a T>;
}

impl<'a, T: Named> NamedItem<'a, T> for &'a [T] {
    fn named(self, name: <T as Named>::Name) -> Option<&'a T> {
        self.iter().find(|x| x.name() == name)
    }
}

/// Represents a node that has a name such as an `Argument` or `Directive`.
/// The Name associated type should be a newtype wrapper around StringKey.
pub trait Named {
    type Name: Eq + PartialEq<<Self as Named>::Name>;
    fn name(&self) -> Self::Name;
}

/// Wrapper struct for clarity rather than having StringKey everywhere.
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
pub struct DirectiveName(pub StringKey);

impl fmt::Display for DirectiveName {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        fmt::Display::fmt(&self.0, f)
    }
}

impl FromStr for DirectiveName {
    type Err = std::convert::Infallible;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        Ok(DirectiveName(s.intern()))
    }
}

#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct ArgumentName(pub StringKey);

impl fmt::Display for ArgumentName {
    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(fmt, "{}", self.0)
    }
}
<commit_msg>Generate the query builder "create" factory methods based on opt in/out directives<commit_after>/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use std::fmt;
use std::str::FromStr;

use intern::string_key::Intern;
use intern::string_key::StringKey;
use serde::Deserialize;

/// Utility to access an item in a list by its name where name is defined by the
/// `Named` trait.
pub trait NamedItem<'a, T: Named> {
    fn named(self, name: <T as Named>::Name) -> Option<&'a T>;
}

impl<'a, T: Named> NamedItem<'a, T> for &'a [T] {
    fn named(self, name: <T as Named>::Name) -> Option<&'a T> {
        self.iter().find(|x| x.name() == name)
    }
}

/// Represents a node that has a name such as an `Argument` or `Directive`.
/// The Name associated type should be a newtype wrapper around StringKey.
pub trait Named {
    type Name: Eq + PartialEq<<Self as Named>::Name>;
    fn name(&self) -> Self::Name;
}

/// Wrapper struct for clarity rather than having StringKey everywhere.
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct DirectiveName(pub StringKey);

impl fmt::Display for DirectiveName {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        fmt::Display::fmt(&self.0, f)
    }
}

impl FromStr for DirectiveName {
    type Err = std::convert::Infallible;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        Ok(DirectiveName(s.intern()))
    }
}

#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct ArgumentName(pub StringKey);

impl fmt::Display for ArgumentName {
    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(fmt, "{}", self.0)
    }
}
<|endoftext|>


<commit_before>// Not in interpret to make sure we do not use private implementation details

use std::convert::TryFrom;

use rustc_hir::Mutability;
use rustc_middle::mir;
use rustc_middle::ty::{self, TyCtxt};
use rustc_span::{source_map::DUMMY_SP, symbol::Symbol};

use crate::interpret::{
    intern_const_alloc_recursive, ConstValue, InternKind, InterpCx, MemPlaceMeta, Scalar,
};

mod error;
mod eval_queries;
mod fn_queries;
mod machine;

pub use error::*;
pub use eval_queries::*;
pub use fn_queries::*;
pub use machine::*;

pub(crate) fn const_caller_location(
    tcx: TyCtxt<'tcx>,
    (file, line, col): (Symbol, u32, u32),
) -> ConstValue<'tcx> {
    trace!("const_caller_location: {}:{}:{}", file, line, col);
    let mut ecx = mk_eval_cx(tcx, DUMMY_SP, ty::ParamEnv::reveal_all(), false);

    let loc_place = ecx.alloc_caller_location(file, line, col);
    intern_const_alloc_recursive(&mut ecx, InternKind::Constant, loc_place, false);
    ConstValue::Scalar(loc_place.ptr)
}

/// This function uses `unwrap` copiously, because an already validated constant
/// must have valid fields and can thus never fail outside of compiler bugs. However, it is
/// invoked from the pretty printer, where it can receive enums with no variants and e.g.
/// `read_discriminant` needs to be able to handle that.
pub(crate) fn destructure_const<'tcx>(
    tcx: TyCtxt<'tcx>,
    param_env: ty::ParamEnv<'tcx>,
    val: &'tcx ty::Const<'tcx>,
) -> mir::DestructuredConst<'tcx> {
    trace!("destructure_const: {:?}", val);
    let ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, false);
    let op = ecx.const_to_op(val, None).unwrap();

    // We go to `usize` as we cannot allocate anything bigger anyway.
    let (field_count, variant, down) = match val.ty.kind() {
        ty::Array(_, len) => (usize::try_from(len.eval_usize(tcx, param_env)).unwrap(), None, op),
        ty::Adt(def, _) if def.variants.is_empty() => {
            return mir::DestructuredConst { variant: None, fields: tcx.arena.alloc_slice(&[]) };
        }
        ty::Adt(def, _) => {
            let variant = ecx.read_discriminant(op).unwrap().1;
            let down = ecx.operand_downcast(op, variant).unwrap();
            (def.variants[variant].fields.len(), Some(variant), down)
        }
        ty::Tuple(substs) => (substs.len(), None, op),
        _ => bug!("cannot destructure constant {:?}", val),
    };

    let fields_iter = (0..field_count).map(|i| {
        let field_op = ecx.operand_field(down, i).unwrap();
        let val = op_to_const(&ecx, field_op);
        ty::Const::from_value(tcx, val, field_op.layout.ty)
    });
    let fields = tcx.arena.alloc_from_iter(fields_iter);

    mir::DestructuredConst { variant, fields }
}

pub(crate) fn deref_const<'tcx>(
    tcx: TyCtxt<'tcx>,
    param_env: ty::ParamEnv<'tcx>,
    val: &'tcx ty::Const<'tcx>,
) -> &'tcx ty::Const<'tcx> {
    trace!("deref_const: {:?}", val);
    let ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, false);
    let op = ecx.const_to_op(val, None).unwrap();
    let mplace = ecx.deref_operand(op).unwrap();
    if let Scalar::Ptr(ptr) = mplace.ptr {
        assert_eq!(
            ecx.memory.get_raw(ptr.alloc_id).unwrap().mutability,
            Mutability::Not,
            "deref_const cannot be used with mutable allocations as \
            that could allow pattern matching to observe mutable statics",
        );
    }

    let ty = match mplace.meta {
        MemPlaceMeta::None => mplace.layout.ty,
        MemPlaceMeta::Poison => bug!("poison metadata in `deref_const`: {:#?}", mplace),
        // In case of unsized types, figure out the real type behind.
        MemPlaceMeta::Meta(scalar) => match mplace.layout.ty.kind() {
            ty::Str => bug!("there's no sized equivalent of a `str`"),
            ty::Slice(elem_ty) => tcx.mk_array(elem_ty, scalar.to_machine_usize(&tcx).unwrap()),
            _ => bug!(
                "type {} should not have metadata, but had {:?}",
                mplace.layout.ty,
                mplace.meta
            ),
        },
    };

    tcx.mk_const(ty::Const { val: ty::ConstKind::Value(op_to_const(&ecx, mplace.into())), ty })
}
<commit_msg>Rollup merge of #77936 - est31:remove_needless_alloc_slice, r=jonas-schievink<commit_after>// Not in interpret to make sure we do not use private implementation details

use std::convert::TryFrom;

use rustc_hir::Mutability;
use rustc_middle::mir;
use rustc_middle::ty::{self, TyCtxt};
use rustc_span::{source_map::DUMMY_SP, symbol::Symbol};

use crate::interpret::{
    intern_const_alloc_recursive, ConstValue, InternKind, InterpCx, MemPlaceMeta, Scalar,
};

mod error;
mod eval_queries;
mod fn_queries;
mod machine;

pub use error::*;
pub use eval_queries::*;
pub use fn_queries::*;
pub use machine::*;

pub(crate) fn const_caller_location(
    tcx: TyCtxt<'tcx>,
    (file, line, col): (Symbol, u32, u32),
) -> ConstValue<'tcx> {
    trace!("const_caller_location: {}:{}:{}", file, line, col);
    let mut ecx = mk_eval_cx(tcx, DUMMY_SP, ty::ParamEnv::reveal_all(), false);

    let loc_place = ecx.alloc_caller_location(file, line, col);
    intern_const_alloc_recursive(&mut ecx, InternKind::Constant, loc_place, false);
    ConstValue::Scalar(loc_place.ptr)
}

/// This function uses `unwrap` copiously, because an already validated constant
/// must have valid fields and can thus never fail outside of compiler bugs. However, it is
/// invoked from the pretty printer, where it can receive enums with no variants and e.g.
/// `read_discriminant` needs to be able to handle that.
pub(crate) fn destructure_const<'tcx>(
    tcx: TyCtxt<'tcx>,
    param_env: ty::ParamEnv<'tcx>,
    val: &'tcx ty::Const<'tcx>,
) -> mir::DestructuredConst<'tcx> {
    trace!("destructure_const: {:?}", val);
    let ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, false);
    let op = ecx.const_to_op(val, None).unwrap();

    // We go to `usize` as we cannot allocate anything bigger anyway.
    let (field_count, variant, down) = match val.ty.kind() {
        ty::Array(_, len) => (usize::try_from(len.eval_usize(tcx, param_env)).unwrap(), None, op),
        ty::Adt(def, _) if def.variants.is_empty() => {
            return mir::DestructuredConst { variant: None, fields: &[] };
        }
        ty::Adt(def, _) => {
            let variant = ecx.read_discriminant(op).unwrap().1;
            let down = ecx.operand_downcast(op, variant).unwrap();
            (def.variants[variant].fields.len(), Some(variant), down)
        }
        ty::Tuple(substs) => (substs.len(), None, op),
        _ => bug!("cannot destructure constant {:?}", val),
    };

    let fields_iter = (0..field_count).map(|i| {
        let field_op = ecx.operand_field(down, i).unwrap();
        let val = op_to_const(&ecx, field_op);
        ty::Const::from_value(tcx, val, field_op.layout.ty)
    });
    let fields = tcx.arena.alloc_from_iter(fields_iter);

    mir::DestructuredConst { variant, fields }
}

pub(crate) fn deref_const<'tcx>(
    tcx: TyCtxt<'tcx>,
    param_env: ty::ParamEnv<'tcx>,
    val: &'tcx ty::Const<'tcx>,
) -> &'tcx ty::Const<'tcx> {
    trace!("deref_const: {:?}", val);
    let ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, false);
    let op = ecx.const_to_op(val, None).unwrap();
    let mplace = ecx.deref_operand(op).unwrap();
    if let Scalar::Ptr(ptr) = mplace.ptr {
        assert_eq!(
            ecx.memory.get_raw(ptr.alloc_id).unwrap().mutability,
            Mutability::Not,
            "deref_const cannot be used with mutable allocations as \
            that could allow pattern matching to observe mutable statics",
        );
    }

    let ty = match mplace.meta {
        MemPlaceMeta::None => mplace.layout.ty,
        MemPlaceMeta::Poison => bug!("poison metadata in `deref_const`: {:#?}", mplace),
        // In case of unsized types, figure out the real type behind.
        MemPlaceMeta::Meta(scalar) => match mplace.layout.ty.kind() {
            ty::Str => bug!("there's no sized equivalent of a `str`"),
            ty::Slice(elem_ty) => tcx.mk_array(elem_ty, scalar.to_machine_usize(&tcx).unwrap()),
            _ => bug!(
                "type {} should not have metadata, but had {:?}",
                mplace.layout.ty,
                mplace.meta
            ),
        },
    };

    tcx.mk_const(ty::Const { val: ty::ConstKind::Value(op_to_const(&ecx, mplace.into())), ty })
}
<|endoftext|>


<commit_before>use pest::Parser;
use pest::iterators::Pair;
use tera::{Map, Context, Value, to_value};

use errors::{Result, ResultExt};
use ::context::RenderContext;

// This include forces recompiling this source file if the grammar file changes.
// Uncomment it when doing changes to the .pest file
const _GRAMMAR: &str = include_str!("content.pest");

#[derive(Parser)]
#[grammar = "content.pest"]
pub struct ContentParser;

fn replace_string_markers(input: &str) -> String {
    match input.chars().next().unwrap() {
        '"' => input.replace('"', "").to_string(),
        '\'' => input.replace('\'', "").to_string(),
        '`' => input.replace('`', "").to_string(),
        _ => unreachable!("How did you even get there"),
    }
}

fn parse_literal(pair: Pair<Rule>) -> Value {
    let mut val = None;
    for p in pair.into_inner() {
        match p.as_rule() {
            Rule::boolean => match p.as_str() {
                "true" => val = Some(Value::Bool(true)),
                "false" => val = Some(Value::Bool(false)),
                _ => unreachable!(),
            },
            Rule::string => val = Some(Value::String(replace_string_markers(p.as_str()))),
            Rule::float => {
                val = Some(to_value(p.as_str().parse::<f64>().unwrap()).unwrap());
            }
            Rule::int => {
                val = Some(to_value(p.as_str().parse::<i64>().unwrap()).unwrap());
            }
            _ => unreachable!("Unknown literal: {:?}", p)
        };
    }

    val.unwrap()
}

/// Returns (shortcode_name, kwargs)
fn parse_shortcode_call(pair: Pair<Rule>) -> (String, Map<String, Value>) {
    let mut name = None;
    let mut args = Map::new();

    for p in pair.into_inner() {
        match p.as_rule() {
            Rule::ident => { name = Some(p.into_span().as_str().to_string()); }
            Rule::kwarg => {
                let mut arg_name = None;
                let mut arg_val = None;
                for p2 in p.into_inner() {
                    match p2.as_rule() {
                        Rule::ident => { arg_name = Some(p2.into_span().as_str().to_string()); }
                        Rule::literal => { arg_val = Some(parse_literal(p2)); }
                        Rule::array => {
                            let mut vals = vec![];
                            for p3 in p2.into_inner() {
                                match p3.as_rule() {
                                    Rule::literal => vals.push(parse_literal(p3)),
                                    _ => unreachable!("Got something other than literal in an array: {:?}", p3),
                                }
                            }
                            arg_val = Some(Value::Array(vals));
                        }
                        _ => unreachable!("Got something unexpected in a kwarg: {:?}", p2),
                    }
                }

                args.insert(arg_name.unwrap(), arg_val.unwrap());
            }
            _ => unreachable!("Got something unexpected in a shortcode: {:?}", p)
        }
    }
    (name.unwrap(), args)
}

fn render_shortcode(name: &str, args: &Map<String, Value>, context: &RenderContext, body: Option<&str>) -> Result<String> {
    let mut tera_context = Context::new();
    for (key, value) in args.iter() {
        tera_context.insert(key, value);
    }
    if let Some(ref b) = body {
        // Trimming right to avoid most shortcodes with bodies ending up with a HTML new line
        tera_context.insert("body", b.trim_right());
    }
    tera_context.extend(context.tera_context.clone());
    let tpl_name = format!("shortcodes/{}.html", name);

    let res = context.tera
        .render(&tpl_name, &tera_context)
        .chain_err(|| format!("Failed to render {} shortcode", name))?;

    // We trim left every single line of a shortcode to avoid the accidental
    // shortcode counted as code block because of 4 spaces left padding
    Ok(res.lines().map(|s| s.trim_left()).collect())
}

pub fn render_shortcodes(content: &str, context: &RenderContext) -> Result<String> {
    let mut res = String::with_capacity(content.len());

    let mut pairs = match ContentParser::parse(Rule::page, content) {
        Ok(p) => p,
        Err(e) => {
            let fancy_e = e.renamed_rules(|rule| {
                match *rule {
                    Rule::int => "an integer".to_string(),
                    Rule::float => "a float".to_string(),
                    Rule::string => "a string".to_string(),
                    Rule::literal => "a literal (int, float, string, bool)".to_string(),
                    Rule::array => "an array".to_string(),
                    Rule::kwarg => "a keyword argument".to_string(),
                    Rule::ident => "an identifier".to_string(),
                    Rule::inline_shortcode => "an inline shortcode".to_string(),
                    Rule::ignored_inline_shortcode => "an ignored inline shortcode".to_string(),
                    Rule::sc_body_start => "the start of a shortcode".to_string(),
                    Rule::ignored_sc_body_start => "the start of an ignored shortcode".to_string(),
                    Rule::text => "some text".to_string(),
                    Rule::EOI => "end of input".to_string(),
                    Rule::double_quoted_string => "double quoted string".to_string(),
                    Rule::single_quoted_string => "single quoted string".to_string(),
                    Rule::backquoted_quoted_string => "backquoted quoted string".to_string(),
                    Rule::boolean => "a boolean (true, false)".to_string(),
                    Rule::all_chars => "a alphanumerical character".to_string(),
                    Rule::kwargs => "a list of keyword arguments".to_string(),
                    Rule::sc_def => "a shortcode definition".to_string(),
                    Rule::shortcode_with_body => "a shortcode with body".to_string(),
                    Rule::ignored_shortcode_with_body => "an ignored shortcode with body".to_string(),
                    Rule::sc_body_end => "{% end %}".to_string(),
                    Rule::ignored_sc_body_end => "{%/* end */%}".to_string(),
                    Rule::text_in_body_sc => "text in a shortcode body".to_string(),
                    Rule::text_in_ignored_body_sc => "text in an ignored shortcode body".to_string(),
                    Rule::content => "some content".to_string(),
                    Rule::page => "a page".to_string(),
                    Rule::WHITESPACE => "whitespace".to_string(),
                }
            });
            bail!("{}", fancy_e);
        }
    };

    // We have at least a `page` pair
    for p in pairs.next().unwrap().into_inner() {
        match p.as_rule() {
            Rule::text | Rule::text_in_ignored_body_sc | Rule::text_in_body_sc => res.push_str(p.into_span().as_str()),
            Rule::inline_shortcode => {
                let (name, args) = parse_shortcode_call(p);
                res.push_str(&render_shortcode(&name, &args, context, None)?);
            }
            Rule::shortcode_with_body => {
                let mut inner = p.into_inner();
                // 3 items in inner: call, body, end
                // we don't care about the closing tag
                let (name, args) = parse_shortcode_call(inner.next().unwrap());
                let body = inner.next().unwrap().into_span().as_str();
                res.push_str(&render_shortcode(&name, &args, context, Some(body))?);
            }
            Rule::ignored_inline_shortcode => {
                res.push_str(
                    &p.into_span().as_str()
                        .replacen("{{/*", "{{", 1)
                        .replacen("*/}}", "}}", 1)
                );
            }
            Rule::ignored_shortcode_with_body => {
                for p2 in p.into_inner() {
                    match p2.as_rule() {
                        Rule::ignored_sc_body_start | Rule::ignored_sc_body_end => {
                            res.push_str(
                                &p2.into_span().as_str()
                                    .replacen("{%/*", "{%", 1)
                                    .replacen("*/%}", "%}", 1)
                            );
                        }
                        Rule::text_in_ignored_body_sc => res.push_str(p2.into_span().as_str()),
                        _ => unreachable!("Got something weird in an ignored shortcode: {:?}", p2),
                    }
                }
            },
            Rule::EOI => (),
            _ => unreachable!("unexpected page rule: {:?}", p.as_rule()),
        }
    }

    Ok(res)
}

#[cfg(test)]
mod tests {
    use std::collections::HashMap;

    use tera::Tera;
    use config::Config;
    use front_matter::InsertAnchor;
    use super::*;

    macro_rules! assert_lex_rule {
        ($rule: expr, $input: expr) => {
            let res = ContentParser::parse($rule, $input);
            println!("{:?}", $input);
            println!("{:#?}", res);
            if res.is_err() {
                println!("{}", res.unwrap_err());
                panic!();
            }
            assert!(res.is_ok());
            assert_eq!(res.unwrap().last().unwrap().into_span().end(), $input.len());
        };
    }

    fn render_shortcodes(code: &str, tera: &Tera) -> String {
        let config = Config::default();
        let permalinks = HashMap::new();
        let context = RenderContext::new(&tera, &config, "", &permalinks, InsertAnchor::None);
        super::render_shortcodes(code, &context).unwrap()
    }

    #[test]
    fn lex_text() {
        let inputs = vec!["Hello world", "HEllo \n world", "Hello 1 2 true false 'hey'"];
        for i in inputs {
            assert_lex_rule!(Rule::text, i);
        }
    }

    #[test]
    fn lex_inline_shortcode() {
        let inputs = vec![
            "{{ youtube() }}",
            "{{ youtube(id=1, autoplay=true, url='hey') }}",
            "{{ youtube(id=1, \nautoplay=true, url='hey') }}",
        ];
        for i in inputs {
            assert_lex_rule!(Rule::inline_shortcode, i);
        }
    }

    #[test]
    fn lex_inline_ignored_shortcode() {
        let inputs = vec![
            "{{/* youtube() */}}",
            "{{/* youtube(id=1, autoplay=true, url='hey') */}}",
            "{{/* youtube(id=1, \nautoplay=true, \nurl='hey') */}}",
        ];
        for i in inputs {
            assert_lex_rule!(Rule::ignored_inline_shortcode, i);
        }
    }

    #[test]
    fn lex_shortcode_with_body() {
        let inputs = vec![
            r#"{% youtube() %}
            Some text
            {% end %}"#,
            r#"{% youtube(id=1,
            autoplay=true, url='hey') %}
            Some text
            {% end %}"#,
        ];
        for i in inputs {
            assert_lex_rule!(Rule::shortcode_with_body, i);
        }
    }

    #[test]
    fn lex_ignored_shortcode_with_body() {
        let inputs = vec![
            r#"{%/* youtube() */%}
            Some text
            {%/* end */%}"#,
            r#"{%/* youtube(id=1,
            autoplay=true, url='hey') */%}
            Some text
            {%/* end */%}"#,
        ];
        for i in inputs {
            assert_lex_rule!(Rule::ignored_shortcode_with_body, i);
        }
    }

    #[test]
    fn lex_page() {
        let inputs = vec![
            "Some text and a shortcode `{{/* youtube() */}}`",
            "{{ youtube(id=1, autoplay=true, url='hey') }}",
            "{{ youtube(id=1, \nautoplay=true, url='hey') }} that's it",
            r#"
            This is a test
            {% hello() %}
            Body {{ var }}
            {% end %}
            "#
        ];
        for i in inputs {
            assert_lex_rule!(Rule::page, i);
        }
    }

    #[test]
    fn does_nothing_with_no_shortcodes() {
        let res = render_shortcodes("Hello World", &Tera::default());
        assert_eq!(res, "Hello World");
    }

    #[test]
    fn can_unignore_inline_shortcode() {
        let res = render_shortcodes("Hello World {{/* youtube() */}}", &Tera::default());
        assert_eq!(res, "Hello World {{ youtube() }}");
    }

    #[test]
    fn can_unignore_shortcode_with_body() {
        let res = render_shortcodes(r#"
Hello World
{%/* youtube() */%}Some body {{ hello() }}{%/* end */%}"#, &Tera::default());
        assert_eq!(res, "\nHello World\n{% youtube() %}Some body {{ hello() }}{% end %}");
    }

    // https://github.com/Keats/gutenberg/issues/383
    #[test]
    fn unignore_shortcode_with_body_does_not_swallow_initial_whitespace() {
        let res = render_shortcodes(r#"
Hello World
{%/* youtube() */%}
Some body {{ hello() }}{%/* end */%}"#, &Tera::default());
        assert_eq!(res, "\nHello World\n{% youtube() %}\nSome body {{ hello() }}{% end %}");
    }

    #[test]
    fn can_parse_shortcode_arguments() {
        let inputs = vec![
            ("{{ youtube() }}", "youtube", Map::new()),
            (
                "{{ youtube(id=1, autoplay=true, hello='salut', float=1.2) }}",
                "youtube",
                {
                    let mut m = Map::new();
                    m.insert("id".to_string(), to_value(1).unwrap());
                    m.insert("autoplay".to_string(), to_value(true).unwrap());
                    m.insert("hello".to_string(), to_value("salut").unwrap());
                    m.insert("float".to_string(), to_value(1.2).unwrap());
                    m
                }
            ),
            (
                "{{ gallery(photos=['something', 'else'], fullscreen=true) }}",
                "gallery",
                {
                    let mut m = Map::new();
                    m.insert("photos".to_string(), to_value(["something", "else"]).unwrap());
                    m.insert("fullscreen".to_string(), to_value(true).unwrap());
                    m
                }
            ),
        ];

        for (i, n, a) in inputs {
            let mut res = ContentParser::parse(Rule::inline_shortcode, i).unwrap();
            let (name, args) = parse_shortcode_call(res.next().unwrap());
            assert_eq!(name, n);
            assert_eq!(args, a);
        }
    }

    #[test]
    fn can_render_inline_shortcodes() {
        let mut tera = Tera::default();
        tera.add_raw_template("shortcodes/youtube.html", "Hello {{id}}").unwrap();
        let res = render_shortcodes("Inline {{ youtube(id=1) }}.", &tera);
        assert_eq!(res, "Inline Hello 1.");
    }

    #[test]
    fn can_render_shortcodes_with_body() {
        let mut tera = Tera::default();
        tera.add_raw_template("shortcodes/youtube.html", "{{body}}").unwrap();
        let res = render_shortcodes("Body\n {% youtube() %}Hey!{% end %}", &tera);
        assert_eq!(res, "Body\n Hey!");
    }
}
<commit_msg>Remove line trimming for shortcode bodies<commit_after>use pest::Parser;
use pest::iterators::Pair;
use tera::{Map, Context, Value, to_value};

use errors::{Result, ResultExt};
use ::context::RenderContext;

// This include forces recompiling this source file if the grammar file changes.
// Uncomment it when doing changes to the .pest file
const _GRAMMAR: &str = include_str!("content.pest");

#[derive(Parser)]
#[grammar = "content.pest"]
pub struct ContentParser;

fn replace_string_markers(input: &str) -> String {
    match input.chars().next().unwrap() {
        '"' => input.replace('"', "").to_string(),
        '\'' => input.replace('\'', "").to_string(),
        '`' => input.replace('`', "").to_string(),
        _ => unreachable!("How did you even get there"),
    }
}

fn parse_literal(pair: Pair<Rule>) -> Value {
    let mut val = None;
    for p in pair.into_inner() {
        match p.as_rule() {
            Rule::boolean => match p.as_str() {
                "true" => val = Some(Value::Bool(true)),
                "false" => val = Some(Value::Bool(false)),
                _ => unreachable!(),
            },
            Rule::string => val = Some(Value::String(replace_string_markers(p.as_str()))),
            Rule::float => {
                val = Some(to_value(p.as_str().parse::<f64>().unwrap()).unwrap());
            }
            Rule::int => {
                val = Some(to_value(p.as_str().parse::<i64>().unwrap()).unwrap());
            }
            _ => unreachable!("Unknown literal: {:?}", p)
        };
    }

    val.unwrap()
}

/// Returns (shortcode_name, kwargs)
fn parse_shortcode_call(pair: Pair<Rule>) -> (String, Map<String, Value>) {
    let mut name = None;
    let mut args = Map::new();

    for p in pair.into_inner() {
        match p.as_rule() {
            Rule::ident => { name = Some(p.into_span().as_str().to_string()); }
            Rule::kwarg => {
                let mut arg_name = None;
                let mut arg_val = None;
                for p2 in p.into_inner() {
                    match p2.as_rule() {
                        Rule::ident => { arg_name = Some(p2.into_span().as_str().to_string()); }
                        Rule::literal => { arg_val = Some(parse_literal(p2)); }
                        Rule::array => {
                            let mut vals = vec![];
                            for p3 in p2.into_inner() {
                                match p3.as_rule() {
                                    Rule::literal => vals.push(parse_literal(p3)),
                                    _ => unreachable!("Got something other than literal in an array: {:?}", p3),
                                }
                            }
                            arg_val = Some(Value::Array(vals));
                        }
                        _ => unreachable!("Got something unexpected in a kwarg: {:?}", p2),
                    }
                }

                args.insert(arg_name.unwrap(), arg_val.unwrap());
            }
            _ => unreachable!("Got something unexpected in a shortcode: {:?}", p)
        }
    }
    (name.unwrap(), args)
}

fn render_shortcode(name: &str, args: &Map<String, Value>, context: &RenderContext, body: Option<&str>) -> Result<String> {
    let mut tera_context = Context::new();
    for (key, value) in args.iter() {
        tera_context.insert(key, value);
    }
    if let Some(ref b) = body {
        // Trimming right to avoid most shortcodes with bodies ending up with a HTML new line
        tera_context.insert("body", b.trim_right());
    }
    tera_context.extend(context.tera_context.clone());
    let tpl_name = format!("shortcodes/{}.html", name);

    let res = context.tera
        .render(&tpl_name, &tera_context)
        .chain_err(|| format!("Failed to render {} shortcode", name))?;

    Ok(res)
}

pub fn render_shortcodes(content: &str, context: &RenderContext) -> Result<String> {
    let mut res = String::with_capacity(content.len());

    let mut pairs = match ContentParser::parse(Rule::page, content) {
        Ok(p) => p,
        Err(e) => {
            let fancy_e = e.renamed_rules(|rule| {
                match *rule {
                    Rule::int => "an integer".to_string(),
                    Rule::float => "a float".to_string(),
                    Rule::string => "a string".to_string(),
                    Rule::literal => "a literal (int, float, string, bool)".to_string(),
                    Rule::array => "an array".to_string(),
                    Rule::kwarg => "a keyword argument".to_string(),
                    Rule::ident => "an identifier".to_string(),
                    Rule::inline_shortcode => "an inline shortcode".to_string(),
                    Rule::ignored_inline_shortcode => "an ignored inline shortcode".to_string(),
                    Rule::sc_body_start => "the start of a shortcode".to_string(),
                    Rule::ignored_sc_body_start => "the start of an ignored shortcode".to_string(),
                    Rule::text => "some text".to_string(),
                    Rule::EOI => "end of input".to_string(),
                    Rule::double_quoted_string => "double quoted string".to_string(),
                    Rule::single_quoted_string => "single quoted string".to_string(),
                    Rule::backquoted_quoted_string => "backquoted quoted string".to_string(),
                    Rule::boolean => "a boolean (true, false)".to_string(),
                    Rule::all_chars => "a alphanumerical character".to_string(),
                    Rule::kwargs => "a list of keyword arguments".to_string(),
                    Rule::sc_def => "a shortcode definition".to_string(),
                    Rule::shortcode_with_body => "a shortcode with body".to_string(),
                    Rule::ignored_shortcode_with_body => "an ignored shortcode with body".to_string(),
                    Rule::sc_body_end => "{% end %}".to_string(),
                    Rule::ignored_sc_body_end => "{%/* end */%}".to_string(),
                    Rule::text_in_body_sc => "text in a shortcode body".to_string(),
                    Rule::text_in_ignored_body_sc => "text in an ignored shortcode body".to_string(),
                    Rule::content => "some content".to_string(),
                    Rule::page => "a page".to_string(),
                    Rule::WHITESPACE => "whitespace".to_string(),
                }
            });
            bail!("{}", fancy_e);
        }
    };

    // We have at least a `page` pair
    for p in pairs.next().unwrap().into_inner() {
        match p.as_rule() {
            Rule::text | Rule::text_in_ignored_body_sc | Rule::text_in_body_sc => res.push_str(p.into_span().as_str()),
            Rule::inline_shortcode => {
                let (name, args) = parse_shortcode_call(p);
                res.push_str(&render_shortcode(&name, &args, context, None)?);
            }
            Rule::shortcode_with_body => {
                let mut inner = p.into_inner();
                // 3 items in inner: call, body, end
                // we don't care about the closing tag
                let (name, args) = parse_shortcode_call(inner.next().unwrap());
                let body = inner.next().unwrap().into_span().as_str();
                res.push_str(&render_shortcode(&name, &args, context, Some(body))?);
            }
            Rule::ignored_inline_shortcode => {
                res.push_str(
                    &p.into_span().as_str()
                        .replacen("{{/*", "{{", 1)
                        .replacen("*/}}", "}}", 1)
                );
            }
            Rule::ignored_shortcode_with_body => {
                for p2 in p.into_inner() {
                    match p2.as_rule() {
                        Rule::ignored_sc_body_start | Rule::ignored_sc_body_end => {
                            res.push_str(
                                &p2.into_span().as_str()
                                    .replacen("{%/*", "{%", 1)
                                    .replacen("*/%}", "%}", 1)
                            );
                        }
                        Rule::text_in_ignored_body_sc => res.push_str(p2.into_span().as_str()),
                        _ => unreachable!("Got something weird in an ignored shortcode: {:?}", p2),
                    }
                }
            },
            Rule::EOI => (),
            _ => unreachable!("unexpected page rule: {:?}", p.as_rule()),
        }
    }

    Ok(res)
}

#[cfg(test)]
mod tests {
    use std::collections::HashMap;

    use tera::Tera;
    use config::Config;
    use front_matter::InsertAnchor;
    use super::*;

    macro_rules! assert_lex_rule {
        ($rule: expr, $input: expr) => {
            let res = ContentParser::parse($rule, $input);
            println!("{:?}", $input);
            println!("{:#?}", res);
            if res.is_err() {
                println!("{}", res.unwrap_err());
                panic!();
            }
            assert!(res.is_ok());
            assert_eq!(res.unwrap().last().unwrap().into_span().end(), $input.len());
        };
    }

    fn render_shortcodes(code: &str, tera: &Tera) -> String {
        let config = Config::default();
        let permalinks = HashMap::new();
        let context = RenderContext::new(&tera, &config, "", &permalinks, InsertAnchor::None);
        super::render_shortcodes(code, &context).unwrap()
    }

    #[test]
    fn lex_text() {
        let inputs = vec!["Hello world", "HEllo \n world", "Hello 1 2 true false 'hey'"];
        for i in inputs {
            assert_lex_rule!(Rule::text, i);
        }
    }

    #[test]
    fn lex_inline_shortcode() {
        let inputs = vec![
            "{{ youtube() }}",
            "{{ youtube(id=1, autoplay=true, url='hey') }}",
            "{{ youtube(id=1, \nautoplay=true, url='hey') }}",
        ];
        for i in inputs {
            assert_lex_rule!(Rule::inline_shortcode, i);
        }
    }

    #[test]
    fn lex_inline_ignored_shortcode() {
        let inputs = vec![
            "{{/* youtube() */}}",
            "{{/* youtube(id=1, autoplay=true, url='hey') */}}",
            "{{/* youtube(id=1, \nautoplay=true, \nurl='hey') */}}",
        ];
        for i in inputs {
            assert_lex_rule!(Rule::ignored_inline_shortcode, i);
        }
    }

    #[test]
    fn lex_shortcode_with_body() {
        let inputs = vec![
            r#"{% youtube() %}
            Some text
            {% end %}"#,
            r#"{% youtube(id=1,
            autoplay=true, url='hey') %}
            Some text
            {% end %}"#,
        ];
        for i in inputs {
            assert_lex_rule!(Rule::shortcode_with_body, i);
        }
    }

    #[test]
    fn lex_ignored_shortcode_with_body() {
        let inputs = vec![
            r#"{%/* youtube() */%}
            Some text
            {%/* end */%}"#,
            r#"{%/* youtube(id=1,
            autoplay=true, url='hey') */%}
            Some text
            {%/* end */%}"#,
        ];
        for i in inputs {
            assert_lex_rule!(Rule::ignored_shortcode_with_body, i);
        }
    }

    #[test]
    fn lex_page() {
        let inputs = vec![
            "Some text and a shortcode `{{/* youtube() */}}`",
            "{{ youtube(id=1, autoplay=true, url='hey') }}",
            "{{ youtube(id=1, \nautoplay=true, url='hey') }} that's it",
            r#"
            This is a test
            {% hello() %}
            Body {{ var }}
            {% end %}
            "#
        ];
        for i in inputs {
            assert_lex_rule!(Rule::page, i);
        }
    }

    #[test]
    fn does_nothing_with_no_shortcodes() {
        let res = render_shortcodes("Hello World", &Tera::default());
        assert_eq!(res, "Hello World");
    }

    #[test]
    fn can_unignore_inline_shortcode() {
        let res = render_shortcodes("Hello World {{/* youtube() */}}", &Tera::default());
        assert_eq!(res, "Hello World {{ youtube() }}");
    }

    #[test]
    fn can_unignore_shortcode_with_body() {
        let res = render_shortcodes(r#"
Hello World
{%/* youtube() */%}Some body {{ hello() }}{%/* end */%}"#, &Tera::default());
        assert_eq!(res, "\nHello World\n{% youtube() %}Some body {{ hello() }}{% end %}");
    }

    // https://github.com/Keats/gutenberg/issues/383
    #[test]
    fn unignore_shortcode_with_body_does_not_swallow_initial_whitespace() {
        let res = render_shortcodes(r#"
Hello World
{%/* youtube() */%}
Some body {{ hello() }}{%/* end */%}"#, &Tera::default());
        assert_eq!(res, "\nHello World\n{% youtube() %}\nSome body {{ hello() }}{% end %}");
    }

    #[test]
    fn can_parse_shortcode_arguments() {
        let inputs = vec![
            ("{{ youtube() }}", "youtube", Map::new()),
            (
                "{{ youtube(id=1, autoplay=true, hello='salut', float=1.2) }}",
                "youtube",
                {
                    let mut m = Map::new();
                    m.insert("id".to_string(), to_value(1).unwrap());
                    m.insert("autoplay".to_string(), to_value(true).unwrap());
                    m.insert("hello".to_string(), to_value("salut").unwrap());
                    m.insert("float".to_string(), to_value(1.2).unwrap());
                    m
                }
            ),
            (
                "{{ gallery(photos=['something', 'else'], fullscreen=true) }}",
                "gallery",
                {
                    let mut m = Map::new();
                    m.insert("photos".to_string(), to_value(["something", "else"]).unwrap());
                    m.insert("fullscreen".to_string(), to_value(true).unwrap());
                    m
                }
            ),
        ];

        for (i, n, a) in inputs {
            let mut res = ContentParser::parse(Rule::inline_shortcode, i).unwrap();
            let (name, args) = parse_shortcode_call(res.next().unwrap());
            assert_eq!(name, n);
            assert_eq!(args, a);
        }
    }

    #[test]
    fn can_render_inline_shortcodes() {
        let mut tera = Tera::default();
        tera.add_raw_template("shortcodes/youtube.html", "Hello {{id}}").unwrap();
        let res = render_shortcodes("Inline {{ youtube(id=1) }}.", &tera);
        assert_eq!(res, "Inline Hello 1.");
    }

    #[test]
    fn can_render_shortcodes_with_body() {
        let mut tera = Tera::default();
        tera.add_raw_template("shortcodes/youtube.html", "{{body}}").unwrap();
        let res = render_shortcodes("Body\n {% youtube() %}Hey!{% end %}", &tera);
        assert_eq!(res, "Body\n Hey!");
    }

    // https://github.com/Keats/gutenberg/issues/462
    #[test]
    fn shortcodes_with_body_do_not_eat_newlines() {
        let mut tera = Tera::default();
        tera.add_raw_template("shortcodes/youtube.html", "{{body | safe}}").unwrap();
        let res = render_shortcodes("Body\n {% youtube() %}\nHello \n World{% end %}", &tera);
        assert_eq!(res, "Body\n Hello \n World");
    }
}
<|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 dom::attr::Attr;
use dom::bindings::codegen::Bindings::DOMTokenListBinding;
use dom::bindings::codegen::Bindings::DOMTokenListBinding::DOMTokenListMethods;
use dom::bindings::error::{Error, ErrorResult, Fallible};
use dom::bindings::global::GlobalRef;
use dom::bindings::js::{JS, Root};
use dom::bindings::utils::{Reflector, reflect_dom_object};
use dom::element::Element;
use dom::node::window_from_node;
use std::borrow::ToOwned;
use string_cache::Atom;
use util::str::{DOMString, HTML_SPACE_CHARACTERS, str_join};

#[dom_struct]
pub struct DOMTokenList {
    reflector_: Reflector,
    element: JS<Element>,
    local_name: Atom,
}

impl DOMTokenList {
    pub fn new_inherited(element: &Element, local_name: Atom) -> DOMTokenList {
        DOMTokenList {
            reflector_: Reflector::new(),
            element: JS::from_ref(element),
            local_name: local_name,
        }
    }

    pub fn new(element: &Element, local_name: &Atom) -> Root<DOMTokenList> {
        let window = window_from_node(element);
        reflect_dom_object(box DOMTokenList::new_inherited(element, local_name.clone()),
                           GlobalRef::Window(window.r()),
                           DOMTokenListBinding::Wrap)
    }

    fn attribute(&self) -> Option<Root<Attr>> {
        self.element.get_attribute(&ns!(""), &self.local_name)
    }

    fn check_token_exceptions(&self, token: &str) -> Fallible<Atom> {
        match token {
            "" => Err(Error::Syntax),
            slice if slice.find(HTML_SPACE_CHARACTERS).is_some() => Err(Error::InvalidCharacter),
            slice => Ok(Atom::from_slice(slice))
        }
    }
}

// https://dom.spec.whatwg.org/#domtokenlist
impl DOMTokenListMethods for DOMTokenList {
    // https://dom.spec.whatwg.org/#dom-domtokenlist-length
    fn Length(&self) -> u32 {
        self.attribute().map(|attr| {
            let attr = attr.r();
            attr.value().as_tokens().len()
        }).unwrap_or(0) as u32
    }

    // https://dom.spec.whatwg.org/#dom-domtokenlist-item
    fn Item(&self, index: u32) -> Option<DOMString> {
        self.attribute().and_then(|attr| {
            let attr = attr.r();
            Some(attr.value().as_tokens()).and_then(|tokens| {
                tokens.get(index as usize).map(|token| (**token).to_owned())
            })
        })
    }

    // https://dom.spec.whatwg.org/#dom-domtokenlist-contains
    fn Contains(&self, token: DOMString) -> Fallible<bool> {
        self.check_token_exceptions(&token).map(|token| {
            self.attribute().map(|attr| {
                let attr = attr.r();
                attr.value()
                    .as_tokens()
                    .iter()
                    .any(|atom: &Atom| *atom == token)
            }).unwrap_or(false)
        })
    }

    // https://dom.spec.whatwg.org/#dom-domtokenlist-add
    fn Add(&self, tokens: Vec<DOMString>) -> ErrorResult {
        let mut atoms = self.element.get_tokenlist_attribute(&self.local_name);
        for token in &tokens {
            let token = try!(self.check_token_exceptions(&token));
            if !atoms.iter().any(|atom| *atom == token) {
                atoms.push(token);
            }
        }
        self.element.set_atomic_tokenlist_attribute(&self.local_name, atoms);
        Ok(())
    }

    // https://dom.spec.whatwg.org/#dom-domtokenlist-remove
    fn Remove(&self, tokens: Vec<DOMString>) -> ErrorResult {
        let mut atoms = self.element.get_tokenlist_attribute(&self.local_name);
        for token in &tokens {
            let token = try!(self.check_token_exceptions(&token));
            atoms.iter().position(|atom| *atom == token).map(|index| {
                atoms.remove(index)
            });
        }
        self.element.set_atomic_tokenlist_attribute(&self.local_name, atoms);
        Ok(())
    }

    // https://dom.spec.whatwg.org/#dom-domtokenlist-toggle
    fn Toggle(&self, token: DOMString, force: Option<bool>) -> Fallible<bool> {
        let mut atoms = self.element.get_tokenlist_attribute(&self.local_name);
        let token = try!(self.check_token_exceptions(&token));
        match atoms.iter().position(|atom| *atom == token) {
            Some(index) => match force {
                Some(true) => Ok(true),
                _ => {
                    atoms.remove(index);
                    self.element.set_atomic_tokenlist_attribute(&self.local_name, atoms);
                    Ok(false)
                }
            },
            None => match force {
                Some(false) => Ok(false),
                _ => {
                    atoms.push(token);
                    self.element.set_atomic_tokenlist_attribute(&self.local_name, atoms);
                    Ok(true)
                }
            }
        }
    }

    // https://dom.spec.whatwg.org/#stringification-behavior
    fn Stringifier(&self) -> DOMString {
        let tokenlist = self.element.get_tokenlist_attribute(&self.local_name);
        str_join(&tokenlist, "\x20")
    }

    // check-tidy: no specs after this line
    fn IndexedGetter(&self, index: u32, found: &mut bool) -> Option<DOMString> {
        let item = self.Item(index);
        *found = item.is_some();
        item
    }
}
<commit_msg>Simplify DOMTokenList::Item.<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 dom::attr::Attr;
use dom::bindings::codegen::Bindings::DOMTokenListBinding;
use dom::bindings::codegen::Bindings::DOMTokenListBinding::DOMTokenListMethods;
use dom::bindings::error::{Error, ErrorResult, Fallible};
use dom::bindings::global::GlobalRef;
use dom::bindings::js::{JS, Root};
use dom::bindings::utils::{Reflector, reflect_dom_object};
use dom::element::Element;
use dom::node::window_from_node;
use std::borrow::ToOwned;
use string_cache::Atom;
use util::str::{DOMString, HTML_SPACE_CHARACTERS, str_join};

#[dom_struct]
pub struct DOMTokenList {
    reflector_: Reflector,
    element: JS<Element>,
    local_name: Atom,
}

impl DOMTokenList {
    pub fn new_inherited(element: &Element, local_name: Atom) -> DOMTokenList {
        DOMTokenList {
            reflector_: Reflector::new(),
            element: JS::from_ref(element),
            local_name: local_name,
        }
    }

    pub fn new(element: &Element, local_name: &Atom) -> Root<DOMTokenList> {
        let window = window_from_node(element);
        reflect_dom_object(box DOMTokenList::new_inherited(element, local_name.clone()),
                           GlobalRef::Window(window.r()),
                           DOMTokenListBinding::Wrap)
    }

    fn attribute(&self) -> Option<Root<Attr>> {
        self.element.get_attribute(&ns!(""), &self.local_name)
    }

    fn check_token_exceptions(&self, token: &str) -> Fallible<Atom> {
        match token {
            "" => Err(Error::Syntax),
            slice if slice.find(HTML_SPACE_CHARACTERS).is_some() => Err(Error::InvalidCharacter),
            slice => Ok(Atom::from_slice(slice))
        }
    }
}

// https://dom.spec.whatwg.org/#domtokenlist
impl DOMTokenListMethods for DOMTokenList {
    // https://dom.spec.whatwg.org/#dom-domtokenlist-length
    fn Length(&self) -> u32 {
        self.attribute().map(|attr| {
            let attr = attr.r();
            attr.value().as_tokens().len()
        }).unwrap_or(0) as u32
    }

    // https://dom.spec.whatwg.org/#dom-domtokenlist-item
    fn Item(&self, index: u32) -> Option<DOMString> {
        self.attribute().and_then(|attr| {
            attr.value().as_tokens().get(index as usize).map(|token| (**token).to_owned())
        })
    }

    // https://dom.spec.whatwg.org/#dom-domtokenlist-contains
    fn Contains(&self, token: DOMString) -> Fallible<bool> {
        self.check_token_exceptions(&token).map(|token| {
            self.attribute().map(|attr| {
                let attr = attr.r();
                attr.value()
                    .as_tokens()
                    .iter()
                    .any(|atom: &Atom| *atom == token)
            }).unwrap_or(false)
        })
    }

    // https://dom.spec.whatwg.org/#dom-domtokenlist-add
    fn Add(&self, tokens: Vec<DOMString>) -> ErrorResult {
        let mut atoms = self.element.get_tokenlist_attribute(&self.local_name);
        for token in &tokens {
            let token = try!(self.check_token_exceptions(&token));
            if !atoms.iter().any(|atom| *atom == token) {
                atoms.push(token);
            }
        }
        self.element.set_atomic_tokenlist_attribute(&self.local_name, atoms);
        Ok(())
    }

    // https://dom.spec.whatwg.org/#dom-domtokenlist-remove
    fn Remove(&self, tokens: Vec<DOMString>) -> ErrorResult {
        let mut atoms = self.element.get_tokenlist_attribute(&self.local_name);
        for token in &tokens {
            let token = try!(self.check_token_exceptions(&token));
            atoms.iter().position(|atom| *atom == token).map(|index| {
                atoms.remove(index)
            });
        }
        self.element.set_atomic_tokenlist_attribute(&self.local_name, atoms);
        Ok(())
    }

    // https://dom.spec.whatwg.org/#dom-domtokenlist-toggle
    fn Toggle(&self, token: DOMString, force: Option<bool>) -> Fallible<bool> {
        let mut atoms = self.element.get_tokenlist_attribute(&self.local_name);
        let token = try!(self.check_token_exceptions(&token));
        match atoms.iter().position(|atom| *atom == token) {
            Some(index) => match force {
                Some(true) => Ok(true),
                _ => {
                    atoms.remove(index);
                    self.element.set_atomic_tokenlist_attribute(&self.local_name, atoms);
                    Ok(false)
                }
            },
            None => match force {
                Some(false) => Ok(false),
                _ => {
                    atoms.push(token);
                    self.element.set_atomic_tokenlist_attribute(&self.local_name, atoms);
                    Ok(true)
                }
            }
        }
    }

    // https://dom.spec.whatwg.org/#stringification-behavior
    fn Stringifier(&self) -> DOMString {
        let tokenlist = self.element.get_tokenlist_attribute(&self.local_name);
        str_join(&tokenlist, "\x20")
    }

    // check-tidy: no specs after this line
    fn IndexedGetter(&self, index: u32, found: &mut bool) -> Option<DOMString> {
        let item = self.Item(index);
        *found = item.is_some();
        item
    }
}
<|endoftext|>