<commit_before>#[doc(hidden)]
pub const NRBITS: u32 = 8;
#[doc(hidden)]
pub const TYPEBITS: u32 = 8;

#[cfg(any(target_arch = "mips", target_arch = "powerpc"))]
mod consts {
    #[doc(hidden)]
    pub const NONE: u8 = 1;
    #[doc(hidden)]
    pub const READ: u8 = 2;
    #[doc(hidden)]
    pub const WRITE: u8 = 4;
    #[doc(hidden)]
    pub const SIZEBITS: u8 = 13;
    #[doc(hidden)]
    pub const DIRBITS: u8 = 3;
}

#[cfg(not(any(target_arch = "powerpc", target_arch = "mips", target_arch = "x86", target_arch = "arm", target_arch = "x86_64", target_arch = "aarch64")))]
use this_arch_not_supported;

// "Generic" ioctl protocol
#[cfg(any(target_arch = "x86", target_arch = "arm", target_arch = "x86_64", target_arch = "aarch64"))]
mod consts {
    #[doc(hidden)]
    pub const NONE: u8 = 0;
    #[doc(hidden)]
    pub const READ: u8 = 2;
    #[doc(hidden)]
    pub const WRITE: u8 = 1;
    #[doc(hidden)]
    pub const SIZEBITS: u8 = 14;
    #[doc(hidden)]
    pub const DIRBITS: u8 = 2;
}

#[doc(hidden)]
pub use self::consts::*;

#[doc(hidden)]
pub const NRSHIFT: u32 = 0;
#[doc(hidden)]
pub const TYPESHIFT: u32 = NRSHIFT + NRBITS as u32;
#[doc(hidden)]
pub const SIZESHIFT: u32 = TYPESHIFT + TYPEBITS as u32;
#[doc(hidden)]
pub const DIRSHIFT: u32 = SIZESHIFT + SIZEBITS as u32;

#[doc(hidden)]
pub const NRMASK: u32 = (1 << NRBITS) - 1;
#[doc(hidden)]
pub const TYPEMASK: u32 = (1 << TYPEBITS) - 1;
#[doc(hidden)]
pub const SIZEMASK: u32 = (1 << SIZEBITS) - 1;
#[doc(hidden)]
pub const DIRMASK: u32 = (1 << DIRBITS) - 1;

/// Encode an ioctl command.
#[macro_export]
macro_rules! ioc {
    ($dir:expr, $ty:expr, $nr:expr, $sz:expr) => (
        (($dir as u32) << $crate::sys::ioctl::DIRSHIFT) |
        (($ty as u32) << $crate::sys::ioctl::TYPESHIFT) |
        (($nr as u32) << $crate::sys::ioctl::NRSHIFT) |
        (($sz as u32) << $crate::sys::ioctl::SIZESHIFT))
}

/// Encode an ioctl command that has no associated data.
#[macro_export]
macro_rules! io {
    ($ty:expr, $nr:expr) => (ioc!($crate::sys::ioctl::NONE, $ty, $nr, 0))
}

/// Encode an ioctl command that reads.
#[macro_export]
macro_rules! ior {
    ($ty:expr, $nr:expr, $sz:expr) => (ioc!($crate::sys::ioctl::READ, $ty, $nr, $sz))
}

/// Encode an ioctl command that writes.
#[macro_export]
macro_rules! iow {
    ($ty:expr, $nr:expr, $sz:expr) => (ioc!($crate::sys::ioctl::WRITE, $ty, $nr, $sz))
}

/// Encode an ioctl command that both reads and writes.
#[macro_export]
macro_rules! iorw {
    ($ty:expr, $nr:expr, $sz:expr) => (ioc!($crate::sys::ioctl::READ | $crate::sys::ioctl::WRITE, $ty, $nr, $sz))
}

/// Convert raw ioctl return value to a Nix result
#[macro_export]
macro_rules! convert_ioctl_res {
    ($w:expr) => (
        {
            let res = $w;
            if res < 0 {
                return Err($crate::Error::Sys($crate::errno::Errno::last()))
            }
            Ok(res) // res may contain useful information for user
        }
    );
}

/// Declare a wrapper function around an ioctl.
#[macro_export]
macro_rules! ioctl {
    (bad $name:ident with $nr:expr) => (
        pub unsafe fn $name(fd: $crate::sys::ioctl::libc::c_int,
                            data: *mut u8)
                            -> $crate::Result<$crate::sys::ioctl::libc::c_int> {
            convert_ioctl_res!($crate::sys::ioctl::ioctl(fd, $nr as $crate::sys::ioctl::libc::c_ulong, data))
        }
        );
    (none $name:ident with $ioty:expr, $nr:expr) => (
        pub unsafe fn $name(fd: $crate::sys::ioctl::libc::c_int)
                            -> $crate::Result<$crate::sys::ioctl::libc::c_int> {
            convert_ioctl_res!($crate::sys::ioctl::ioctl(fd, io!($ioty, $nr) as $crate::sys::ioctl::libc::c_ulong))
        }
        );
    (read $name:ident with $ioty:expr, $nr:expr; $ty:ty) => (
        pub unsafe fn $name(fd: $crate::sys::ioctl::libc::c_int,
                            val: *mut $ty)
                            -> $crate::Result<$crate::sys::ioctl::libc::c_int> {
            convert_ioctl_res!($crate::sys::ioctl::ioctl(fd, ior!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::libc::c_ulong, val))
        }
        );
    (write $name:ident with $ioty:expr, $nr:expr; $ty:ty) => (
        pub unsafe fn $name(fd: $crate::sys::ioctl::libc::c_int,
                            val: *const $ty)
                            -> $crate::Result<$crate::sys::ioctl::libc::c_int> {
            convert_ioctl_res!($crate::sys::ioctl::ioctl(fd, ior!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::libc::c_ulong, val))
        }
        );
    (readwrite $name:ident with $ioty:expr, $nr:expr; $ty:ty) => (
        pub unsafe fn $name(fd: $crate::sys::ioctl::libc::c_int,
                            val: *mut $ty)
                            -> $crate::Result<$crate::sys::ioctl::libc::c_int> {
            convert_ioctl_res!($crate::sys::ioctl::ioctl(fd, iorw!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::libc::c_ulong, val))
        }
        );
    (read buf $name:ident with $ioty:expr, $nr:expr; $ty:ty) => (
        pub unsafe fn $name(fd: $crate::sys::ioctl::libc::c_int,
                            val: *mut $ty,
                            len: usize)
                            -> $crate::Result<$crate::sys::ioctl::libc::c_int> {
            convert_ioctl_res!($crate::sys::ioctl::ioctl(fd, ior!($ioty, $nr, len) as $crate::sys::ioctl::libc::c_ulong, val))
        }
        );
    (write buf $name:ident with $ioty:expr, $nr:expr; $ty:ty) => (
        pub unsafe fn $name(fd: $crate::sys::ioctl::libc::c_int,
                            val: *const $ty,
                            len: usize) -> $crate::Result<$crate::sys::ioctl::libc::c_int> {
            convert_ioctl_res!($crate::sys::ioctl::ioctl(fd, ior!($ioty, $nr, len) as $crate::sys::ioctl::libc::c_ulong, val))
        }
        );
    (readwrite buf $name:ident with $ioty:expr, $nr:expr; $ty:ty) => (
        pub unsafe fn $name(fd: $crate::sys::ioctl::libc::c_int,
                            val: *const $ty,
                            len: usize)
                            -> $crate::Result<$crate::sys::ioctl::libc::c_int> {
            convert_ioctl_res!($crate::sys::ioctl::ioctl(fd, iorw!($ioty, $nr, len) as $crate::sys::ioctl::libc::c_ulong, val))
        }
        );
}

/// Extracts the "direction" (read/write/none) from an encoded ioctl command.
#[inline(always)]
pub fn ioc_dir(nr: u32) -> u8 {
    ((nr >> DIRSHIFT) & DIRMASK) as u8
}

/// Extracts the type from an encoded ioctl command.
#[inline(always)]
pub fn ioc_type(nr: u32) -> u32 {
    (nr >> TYPESHIFT) & TYPEMASK
}

/// Extracts the ioctl number from an encoded ioctl command.
#[inline(always)]
pub fn ioc_nr(nr: u32) -> u32 {
    (nr >> NRSHIFT) & NRMASK
}

/// Extracts the size from an encoded ioctl command.
#[inline(always)]
pub fn ioc_size(nr: u32) -> u32 {
    ((nr >> SIZESHIFT) as u32) & SIZEMASK
}

#[doc(hidden)]
pub const IN: u32 = (WRITE as u32) << DIRSHIFT;
#[doc(hidden)]
pub const OUT: u32 = (READ as u32) << DIRSHIFT;
#[doc(hidden)]
pub const INOUT: u32 = ((READ|WRITE) as u32) << DIRSHIFT;
#[doc(hidden)]
pub const SIZE_MASK: u32 = SIZEMASK << SIZESHIFT;
<commit_msg>ioctl: do not make macro generated functions public<commit_after>#[doc(hidden)]
pub const NRBITS: u32 = 8;
#[doc(hidden)]
pub const TYPEBITS: u32 = 8;

#[cfg(any(target_arch = "mips", target_arch = "powerpc"))]
mod consts {
    #[doc(hidden)]
    pub const NONE: u8 = 1;
    #[doc(hidden)]
    pub const READ: u8 = 2;
    #[doc(hidden)]
    pub const WRITE: u8 = 4;
    #[doc(hidden)]
    pub const SIZEBITS: u8 = 13;
    #[doc(hidden)]
    pub const DIRBITS: u8 = 3;
}

#[cfg(not(any(target_arch = "powerpc", target_arch = "mips", target_arch = "x86", target_arch = "arm", target_arch = "x86_64", target_arch = "aarch64")))]
use this_arch_not_supported;

// "Generic" ioctl protocol
#[cfg(any(target_arch = "x86", target_arch = "arm", target_arch = "x86_64", target_arch = "aarch64"))]
mod consts {
    #[doc(hidden)]
    pub const NONE: u8 = 0;
    #[doc(hidden)]
    pub const READ: u8 = 2;
    #[doc(hidden)]
    pub const WRITE: u8 = 1;
    #[doc(hidden)]
    pub const SIZEBITS: u8 = 14;
    #[doc(hidden)]
    pub const DIRBITS: u8 = 2;
}

#[doc(hidden)]
pub use self::consts::*;

#[doc(hidden)]
pub const NRSHIFT: u32 = 0;
#[doc(hidden)]
pub const TYPESHIFT: u32 = NRSHIFT + NRBITS as u32;
#[doc(hidden)]
pub const SIZESHIFT: u32 = TYPESHIFT + TYPEBITS as u32;
#[doc(hidden)]
pub const DIRSHIFT: u32 = SIZESHIFT + SIZEBITS as u32;

#[doc(hidden)]
pub const NRMASK: u32 = (1 << NRBITS) - 1;
#[doc(hidden)]
pub const TYPEMASK: u32 = (1 << TYPEBITS) - 1;
#[doc(hidden)]
pub const SIZEMASK: u32 = (1 << SIZEBITS) - 1;
#[doc(hidden)]
pub const DIRMASK: u32 = (1 << DIRBITS) - 1;

/// Encode an ioctl command.
#[macro_export]
macro_rules! ioc {
    ($dir:expr, $ty:expr, $nr:expr, $sz:expr) => (
        (($dir as u32) << $crate::sys::ioctl::DIRSHIFT) |
        (($ty as u32) << $crate::sys::ioctl::TYPESHIFT) |
        (($nr as u32) << $crate::sys::ioctl::NRSHIFT) |
        (($sz as u32) << $crate::sys::ioctl::SIZESHIFT))
}

/// Encode an ioctl command that has no associated data.
#[macro_export]
macro_rules! io {
    ($ty:expr, $nr:expr) => (ioc!($crate::sys::ioctl::NONE, $ty, $nr, 0))
}

/// Encode an ioctl command that reads.
#[macro_export]
macro_rules! ior {
    ($ty:expr, $nr:expr, $sz:expr) => (ioc!($crate::sys::ioctl::READ, $ty, $nr, $sz))
}

/// Encode an ioctl command that writes.
#[macro_export]
macro_rules! iow {
    ($ty:expr, $nr:expr, $sz:expr) => (ioc!($crate::sys::ioctl::WRITE, $ty, $nr, $sz))
}

/// Encode an ioctl command that both reads and writes.
#[macro_export]
macro_rules! iorw {
    ($ty:expr, $nr:expr, $sz:expr) => (ioc!($crate::sys::ioctl::READ | $crate::sys::ioctl::WRITE, $ty, $nr, $sz))
}

/// Convert raw ioctl return value to a Nix result
#[macro_export]
macro_rules! convert_ioctl_res {
    ($w:expr) => (
        {
            let res = $w;
            if res < 0 {
                return Err($crate::Error::Sys($crate::errno::Errno::last()))
            }
            Ok(res) // res may contain useful information for user
        }
    );
}

/// Declare a wrapper function around an ioctl.
#[macro_export]
macro_rules! ioctl {
    (bad $name:ident with $nr:expr) => (
        unsafe fn $name(fd: $crate::sys::ioctl::libc::c_int,
                        data: *mut u8)
                        -> $crate::Result<$crate::sys::ioctl::libc::c_int> {
            convert_ioctl_res!($crate::sys::ioctl::ioctl(fd, $nr as $crate::sys::ioctl::libc::c_ulong, data))
        }
        );
    (none $name:ident with $ioty:expr, $nr:expr) => (
        unsafe fn $name(fd: $crate::sys::ioctl::libc::c_int)
                        -> $crate::Result<$crate::sys::ioctl::libc::c_int> {
            convert_ioctl_res!($crate::sys::ioctl::ioctl(fd, io!($ioty, $nr) as $crate::sys::ioctl::libc::c_ulong))
        }
        );
    (read $name:ident with $ioty:expr, $nr:expr; $ty:ty) => (
        unsafe fn $name(fd: $crate::sys::ioctl::libc::c_int,
                        val: *mut $ty)
                        -> $crate::Result<$crate::sys::ioctl::libc::c_int> {
            convert_ioctl_res!($crate::sys::ioctl::ioctl(fd, ior!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::libc::c_ulong, val))
        }
        );
    (write $name:ident with $ioty:expr, $nr:expr; $ty:ty) => (
        unsafe fn $name(fd: $crate::sys::ioctl::libc::c_int,
                        val: *const $ty)
                         -> $crate::Result<$crate::sys::ioctl::libc::c_int> {
            convert_ioctl_res!($crate::sys::ioctl::ioctl(fd, ior!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::libc::c_ulong, val))
        }
        );
    (readwrite $name:ident with $ioty:expr, $nr:expr; $ty:ty) => (
        unsafe fn $name(fd: $crate::sys::ioctl::libc::c_int,
                        val: *mut $ty)
                        -> $crate::Result<$crate::sys::ioctl::libc::c_int> {
            convert_ioctl_res!($crate::sys::ioctl::ioctl(fd, iorw!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::libc::c_ulong, val))
        }
        );
    (read buf $name:ident with $ioty:expr, $nr:expr; $ty:ty) => (
        unsafe fn $name(fd: $crate::sys::ioctl::libc::c_int,
                        val: *mut $ty,
                        len: usize)
                        -> $crate::Result<$crate::sys::ioctl::libc::c_int> {
            convert_ioctl_res!($crate::sys::ioctl::ioctl(fd, ior!($ioty, $nr, len) as $crate::sys::ioctl::libc::c_ulong, val))
        }
        );
    (write buf $name:ident with $ioty:expr, $nr:expr; $ty:ty) => (
        unsafe fn $name(fd: $crate::sys::ioctl::libc::c_int,
                        val: *const $ty,
                        len: usize) -> $crate::Result<$crate::sys::ioctl::libc::c_int> {
            convert_ioctl_res!($crate::sys::ioctl::ioctl(fd, ior!($ioty, $nr, len) as $crate::sys::ioctl::libc::c_ulong, val))
        }
        );
    (readwrite buf $name:ident with $ioty:expr, $nr:expr; $ty:ty) => (
        unsafe fn $name(fd: $crate::sys::ioctl::libc::c_int,
                        val: *const $ty,
                        len: usize)
                        -> $crate::Result<$crate::sys::ioctl::libc::c_int> {
            convert_ioctl_res!($crate::sys::ioctl::ioctl(fd, iorw!($ioty, $nr, len) as $crate::sys::ioctl::libc::c_ulong, val))
        }
        );
}

/// Extracts the "direction" (read/write/none) from an encoded ioctl command.
#[inline(always)]
pub fn ioc_dir(nr: u32) -> u8 {
    ((nr >> DIRSHIFT) & DIRMASK) as u8
}

/// Extracts the type from an encoded ioctl command.
#[inline(always)]
pub fn ioc_type(nr: u32) -> u32 {
    (nr >> TYPESHIFT) & TYPEMASK
}

/// Extracts the ioctl number from an encoded ioctl command.
#[inline(always)]
pub fn ioc_nr(nr: u32) -> u32 {
    (nr >> NRSHIFT) & NRMASK
}

/// Extracts the size from an encoded ioctl command.
#[inline(always)]
pub fn ioc_size(nr: u32) -> u32 {
    ((nr >> SIZESHIFT) as u32) & SIZEMASK
}

#[doc(hidden)]
pub const IN: u32 = (WRITE as u32) << DIRSHIFT;
#[doc(hidden)]
pub const OUT: u32 = (READ as u32) << DIRSHIFT;
#[doc(hidden)]
pub const INOUT: u32 = ((READ|WRITE) as u32) << DIRSHIFT;
#[doc(hidden)]
pub const SIZE_MASK: u32 = SIZEMASK << SIZESHIFT;
<|endoftext|>


<commit_before>// aux-build:issue_2472_b.rs

use issue_2472_b;

import issue_2472_b::{S, T};

fn main() {
    let s = S(());
    s.foo();
    s.bar();
}
<commit_msg>xfail-fast issue-2472<commit_after>// aux-build:issue_2472_b.rs
// xfail-fast

use issue_2472_b;

import issue_2472_b::{S, T};

fn main() {
    let s = S(());
    s.foo();
    s.bar();
}
<|endoftext|>


<commit_before>//#![feature(trace_macros)]
#![allow(dead_code)]
#![cfg_attr(feature = "cargo-clippy", allow(redundant_closure))]

#[macro_use]
extern crate nom;

use nom::{space, Err, IResult, Needed, ErrorKind, le_u64, is_digit};
use nom::types::{CompleteStr, CompleteByteSlice};

#[allow(dead_code)]
struct Range {
  start: char,
  end: char,
}

pub fn take_char(input: &[u8]) -> IResult<&[u8], char> {
  if !input.is_empty() {
    Ok((&input[1..], input[0] as char))
  } else {
    Err(Err::Incomplete(Needed::Size(1)))
  }
}

//trace_macros!(true);

#[allow(dead_code)]
named!(range<&[u8], Range>,
    alt!(
        do_parse!(
            start: take_char >>
            tag!("-")        >>
            end: take_char   >>
            (Range {
                start: start,
                end:   end,
            })
        ) |
        map!(
            take_char,
            |c| {
                Range {
                    start: c,
                    end:   c,
                }
            }
        )
    )
);

#[allow(dead_code)]
named!(literal<&[u8], Vec<char> >,
    map!(
        many1!(take_char),
        |cs| {
          cs
        }
    )
);

#[test]
fn issue_58() {
  let _ = range(&b"abcd"[..]);
  let _ = literal(&b"abcd"[..]);
}

//trace_macros!(false);

#[cfg(feature = "std")]
mod parse_int {
  use nom::HexDisplay;
  use nom::{digit, space, IResult};
  use std::str;

  named!(parse_ints<Vec<i32>>, many0!(spaces_or_int));

  fn spaces_or_int(input: &[u8]) -> IResult<&[u8], i32> {
    println!("{}", input.to_hex(8));
    do_parse!(
      input,
      opt!(complete!(space)) >> res: map!(complete!(digit), |x| {
        println!("x: {:?}", x);
        let result = str::from_utf8(x).unwrap();
        println!("Result: {}", result);
        println!("int is empty?: {}", x.is_empty());
        match result.parse() {
          Ok(i) => i,
          Err(e) => panic!("UH OH! NOT A DIGIT! {:?}", e),
        }
      }) >> (res)
    )
  }

  #[test]
  fn issue_142() {
    let subject = parse_ints(&b"12 34 5689a"[..]);
    let expected = Ok((&b"a"[..], vec![12, 34, 5689]));
    assert_eq!(subject, expected);

    let subject = parse_ints(&b"12 34 5689 "[..]);
    let expected = Ok((&b" "[..], vec![12, 34, 5689]));
    assert_eq!(subject, expected)
  }
}

#[test]
fn usize_length_bytes_issue() {
  use nom::be_u16;
  let _: IResult<&[u8], &[u8], u32> = length_bytes!(b"012346", be_u16);
}

/*
 DOES NOT COMPILE
#[test]
fn issue_152() {
  named!(take4, take!(4));
  named!(xyz, tag!("XYZ"));
  named!(abc, tag!("abc"));

  named!(sw,
    switch!(take4,
      b"abcd" => xyz |
      b"efgh" => abc
    )
  );
}
*/

#[test]
fn take_till_issue() {
  named!(nothing, take_till!(call!(|_| true)));

  assert_eq!(nothing(b""), Err(Err::Incomplete(Needed::Size(1))));
  assert_eq!(nothing(b"abc"), Ok((&b"abc"[..], &b""[..])));
}

named!(
  issue_498<Vec<&[u8]>>,
  separated_nonempty_list!(opt!(space), tag!("abcd"))
);

named!(issue_308(&str) -> bool,
    do_parse! (
        tag_s! ("foo") >>
        b: alt_complete! (
            map! (tag_s! ("1"), |_: &str|->bool {true}) |
            value! (false)
        ) >>
        (b) ));

#[cfg(feature = "alloc")]
fn issue_302(input: &[u8]) -> IResult<&[u8], Option<Vec<u64>>> {
  do_parse!(input, entries: cond!(true, count!(le_u64, 3)) >> (entries))
}

#[test]
fn issue_655() {
  use nom::{line_ending, not_line_ending};
  named!(twolines(&str) -> (&str, &str),
    do_parse!(
      l1 : not_line_ending >>
           line_ending >>
      l2 : not_line_ending >>
           line_ending >>
      ((l1, l2))
    )
  );

  assert_eq!(twolines("foo\nbar\n"), Ok(("", ("foo", "bar"))));
  assert_eq!(twolines("féo\nbar\n"), Ok(("", ("féo", "bar"))));
  assert_eq!(twolines("foé\nbar\n"), Ok(("", ("foé", "bar"))));
  assert_eq!(twolines("foé\r\nbar\n"), Ok(("", ("foé", "bar"))));
}

#[cfg(feature = "std")]
named!(issue_666 <CompleteByteSlice, CompleteByteSlice>, dbg_dmp!(tag!("abc")));

#[test]
fn issue_667() {
  use nom::alpha;

  named!(foo <CompleteByteSlice, Vec<CompleteByteSlice>>,
    many0!(
      alt!(alpha | is_a!("_"))
    )
  );
  assert_eq!(
    foo(CompleteByteSlice(b"")),
    Ok((CompleteByteSlice(b""), vec![]))
  );
  assert_eq!(
    foo(CompleteByteSlice(b"loremipsum")),
    Ok((
      CompleteByteSlice(b""),
      vec![CompleteByteSlice(b"loremipsum")]
    ))
  );
  assert_eq!(
    foo(CompleteByteSlice(b"lorem_ipsum")),
    Ok((
      CompleteByteSlice(b""),
      vec![
        CompleteByteSlice(b"lorem"),
        CompleteByteSlice(b"_"),
        CompleteByteSlice(b"ipsum"),
      ]
    ))
  );
  assert_eq!(
    foo(CompleteByteSlice(b"_lorem_ipsum")),
    Ok((
      CompleteByteSlice(b""),
      vec![
        CompleteByteSlice(b"_"),
        CompleteByteSlice(b"lorem"),
        CompleteByteSlice(b"_"),
        CompleteByteSlice(b"ipsum"),
      ]
    ))
  );
  assert_eq!(
    foo(CompleteByteSlice(b"!@#$")),
    Ok((CompleteByteSlice(b"!@#$"), vec![]))
  );
}

#[test]
fn issue_721() {
  assert_eq!(parse_to!("1234", u16), Ok(("", 1234)));
  assert_eq!(parse_to!("foo", String), Ok(("", "foo".to_string())));
}

#[cfg(feature = "alloc")]
named!(issue_717<&[u8], Vec<&[u8]> >,
  separated_list!(tag!([0x0]), is_not!([0x0u8]))
);

struct NoPartialEq {
  value: i32,
}

named!(issue_724<&str, i32>,
  do_parse!(
    metadata: permutation!(
      map!(tag!("hello"), |_| NoPartialEq { value: 1 }),
      map!(tag!("world"), |_| NoPartialEq { value: 2 })
    ) >>
    (metadata.0.value + metadata.1.value)
  )
);

named!(issue_741_str<CompleteStr, CompleteStr>, re_match!(r"^_?[A-Za-z][0-9A-Z_a-z-]*"));
named!(issue_741_bytes<CompleteByteSlice, CompleteByteSlice>, re_bytes_match!(r"^_?[A-Za-z][0-9A-Z_a-z-]*"));

#[test]
fn issue_752() {
    assert_eq!(
        Err::Error(nom::Context::Code("ab", nom::ErrorKind::ParseTo)),
        parse_to!("ab", usize).unwrap_err()
    )
}

fn atom_specials(c: u8) -> bool {
    c == b'q'
}

named!(
    capability<&str>,
    do_parse!(tag_s!(" ") >> _atom: map_res!(take_till1!(atom_specials), std::str::from_utf8) >> ("a"))
);

#[test]
fn issue_759() {
    assert_eq!(capability(b" abcqd"), Ok((&b"qd"[..], "a")));
}

named_args!(issue_771(count: usize)<Vec<u32>>,
  length_count!(value!(count), call!(nom::be_u32))
);

#[test]
fn issue_768() {
  named!(bit_vec8<CompleteByteSlice, Vec<u16>>, bits!(many0!(take_bits!(u16, 8))));
  named!(bit_vec4<CompleteByteSlice, Vec<u16>>, bits!(many0!(take_bits!(u16, 4))));
  named!(bit_vec3<CompleteByteSlice, Vec<u16>>, bits!(many0!(take_bits!(u16, 3))));
  named!(bit_vec11<CompleteByteSlice, Vec<u16>>, bits!(many0!(take_bits!(u16, 11))));

  let m: Vec<u8> = vec![70, 97, 106, 121, 86, 66, 105, 98, 86, 106, 101];
  assert_eq!(
    bit_vec8(CompleteByteSlice(m.as_slice())),
    Ok((CompleteByteSlice(&[]), vec![70, 97, 106, 121, 86, 66, 105, 98, 86, 106, 101]))
  );
  assert_eq!(
    bit_vec4(CompleteByteSlice(m.as_slice())),
    Ok((CompleteByteSlice(&[]), vec![4, 6, 6, 1, 6, 10, 7, 9, 5, 6, 4, 2, 6, 9, 6, 2, 5, 6, 6, 10, 6, 5]))
  );
  assert_eq!(
    bit_vec3(CompleteByteSlice(m.as_slice())),
    Ok((CompleteByteSlice(&[]), vec![2, 1, 4, 6, 0, 5, 5, 2, 3, 6, 2, 5, 3, 1, 0, 2, 3, 2, 2, 6, 1, 1, 2, 6, 3, 2, 4, 6, 2]))
  );
  assert_eq!(
    bit_vec11(CompleteByteSlice(m.as_slice())),
    Ok((CompleteByteSlice(&[]), vec![563, 90, 1266, 1380, 308, 1417, 717, 613]))
  );
}

/// This test is in a separate module to check that all required symbols are imported in
/// `escaped_transform!()`. Without the module, the `use`-es of the current module would
/// mask the error ('"Use of undeclared type or module `Needed`" in escaped_transform!').
mod issue_780 {
  named!(issue_780<&str, String>,
    escaped_transform!(call!(::nom::alpha), '\\', tag!("n"))
  );
}

// issue 617
named!(digits, take_while1!( is_digit ));
named!(multi_617<&[u8], () >, fold_many0!( digits, (), |_, _| {}));

// Sad :(
named!(multi_617_fails<&[u8], () >, fold_many0!( take_while1!( is_digit ), (), |_, _| {}));

mod issue_647 {
  use nom::{Err,be_f64};
  pub type Input<'a> = &'a [u8];

  #[derive(PartialEq, Debug, Clone)]
  struct Data {
      c: f64,
      v: Vec<f64>
  }

  fn list<'a,'b>(input: Input<'a>, _cs: &'b f64) -> Result<(Input<'a>,Vec<f64>), Err<&'a [u8]>> {
      separated_list_complete!(input, tag!(","),be_f64)
  }

  named!(data<Input,Data>, map!(
      do_parse!(
          c: be_f64 >>
          tag!("\n") >>
          v: call!(list,&c) >>
          (c,v)
      ), |(c,v)| {
          Data {
              c: c,
              v: v
          }
      }
  ));
}

named!(issue_775, take_till1!(|_| true));

#[test]
fn issue_848_overflow_incomplete_bits_to_bytes() {
  named!(parser<&[u8], &[u8]>, bits!(bytes!(take!(0x2000000000000000))));
  assert_eq!(parser(&b""[..]), Err(Err::Failure(error_position!(&b""[..], ErrorKind::TooLarge))));
}
<commit_msg>remove some tag_s uses<commit_after>//#![feature(trace_macros)]
#![allow(dead_code)]
#![cfg_attr(feature = "cargo-clippy", allow(redundant_closure))]

#[macro_use]
extern crate nom;

use nom::{space, Err, IResult, Needed, ErrorKind, le_u64, is_digit};
use nom::types::{CompleteStr, CompleteByteSlice};

#[allow(dead_code)]
struct Range {
  start: char,
  end: char,
}

pub fn take_char(input: &[u8]) -> IResult<&[u8], char> {
  if !input.is_empty() {
    Ok((&input[1..], input[0] as char))
  } else {
    Err(Err::Incomplete(Needed::Size(1)))
  }
}

//trace_macros!(true);

#[allow(dead_code)]
named!(range<&[u8], Range>,
    alt!(
        do_parse!(
            start: take_char >>
            tag!("-")        >>
            end: take_char   >>
            (Range {
                start: start,
                end:   end,
            })
        ) |
        map!(
            take_char,
            |c| {
                Range {
                    start: c,
                    end:   c,
                }
            }
        )
    )
);

#[allow(dead_code)]
named!(literal<&[u8], Vec<char> >,
    map!(
        many1!(take_char),
        |cs| {
          cs
        }
    )
);

#[test]
fn issue_58() {
  let _ = range(&b"abcd"[..]);
  let _ = literal(&b"abcd"[..]);
}

//trace_macros!(false);

#[cfg(feature = "std")]
mod parse_int {
  use nom::HexDisplay;
  use nom::{digit, space, IResult};
  use std::str;

  named!(parse_ints<Vec<i32>>, many0!(spaces_or_int));

  fn spaces_or_int(input: &[u8]) -> IResult<&[u8], i32> {
    println!("{}", input.to_hex(8));
    do_parse!(
      input,
      opt!(complete!(space)) >> res: map!(complete!(digit), |x| {
        println!("x: {:?}", x);
        let result = str::from_utf8(x).unwrap();
        println!("Result: {}", result);
        println!("int is empty?: {}", x.is_empty());
        match result.parse() {
          Ok(i) => i,
          Err(e) => panic!("UH OH! NOT A DIGIT! {:?}", e),
        }
      }) >> (res)
    )
  }

  #[test]
  fn issue_142() {
    let subject = parse_ints(&b"12 34 5689a"[..]);
    let expected = Ok((&b"a"[..], vec![12, 34, 5689]));
    assert_eq!(subject, expected);

    let subject = parse_ints(&b"12 34 5689 "[..]);
    let expected = Ok((&b" "[..], vec![12, 34, 5689]));
    assert_eq!(subject, expected)
  }
}

#[test]
fn usize_length_bytes_issue() {
  use nom::be_u16;
  let _: IResult<&[u8], &[u8], u32> = length_bytes!(b"012346", be_u16);
}

/*
 DOES NOT COMPILE
#[test]
fn issue_152() {
  named!(take4, take!(4));
  named!(xyz, tag!("XYZ"));
  named!(abc, tag!("abc"));

  named!(sw,
    switch!(take4,
      b"abcd" => xyz |
      b"efgh" => abc
    )
  );
}
*/

#[test]
fn take_till_issue() {
  named!(nothing, take_till!(call!(|_| true)));

  assert_eq!(nothing(b""), Err(Err::Incomplete(Needed::Size(1))));
  assert_eq!(nothing(b"abc"), Ok((&b"abc"[..], &b""[..])));
}

named!(
  issue_498<Vec<&[u8]>>,
  separated_nonempty_list!(opt!(space), tag!("abcd"))
);

named!(issue_308(&str) -> bool,
    do_parse! (
        tag! ("foo") >>
        b: alt_complete! (
            map! (tag! ("1"), |_: &str|->bool {true}) |
            value! (false)
        ) >>
        (b) ));

#[cfg(feature = "alloc")]
fn issue_302(input: &[u8]) -> IResult<&[u8], Option<Vec<u64>>> {
  do_parse!(input, entries: cond!(true, count!(le_u64, 3)) >> (entries))
}

#[test]
fn issue_655() {
  use nom::{line_ending, not_line_ending};
  named!(twolines(&str) -> (&str, &str),
    do_parse!(
      l1 : not_line_ending >>
           line_ending >>
      l2 : not_line_ending >>
           line_ending >>
      ((l1, l2))
    )
  );

  assert_eq!(twolines("foo\nbar\n"), Ok(("", ("foo", "bar"))));
  assert_eq!(twolines("féo\nbar\n"), Ok(("", ("féo", "bar"))));
  assert_eq!(twolines("foé\nbar\n"), Ok(("", ("foé", "bar"))));
  assert_eq!(twolines("foé\r\nbar\n"), Ok(("", ("foé", "bar"))));
}

#[cfg(feature = "std")]
named!(issue_666 <CompleteByteSlice, CompleteByteSlice>, dbg_dmp!(tag!("abc")));

#[test]
fn issue_667() {
  use nom::alpha;

  named!(foo <CompleteByteSlice, Vec<CompleteByteSlice>>,
    many0!(
      alt!(alpha | is_a!("_"))
    )
  );
  assert_eq!(
    foo(CompleteByteSlice(b"")),
    Ok((CompleteByteSlice(b""), vec![]))
  );
  assert_eq!(
    foo(CompleteByteSlice(b"loremipsum")),
    Ok((
      CompleteByteSlice(b""),
      vec![CompleteByteSlice(b"loremipsum")]
    ))
  );
  assert_eq!(
    foo(CompleteByteSlice(b"lorem_ipsum")),
    Ok((
      CompleteByteSlice(b""),
      vec![
        CompleteByteSlice(b"lorem"),
        CompleteByteSlice(b"_"),
        CompleteByteSlice(b"ipsum"),
      ]
    ))
  );
  assert_eq!(
    foo(CompleteByteSlice(b"_lorem_ipsum")),
    Ok((
      CompleteByteSlice(b""),
      vec![
        CompleteByteSlice(b"_"),
        CompleteByteSlice(b"lorem"),
        CompleteByteSlice(b"_"),
        CompleteByteSlice(b"ipsum"),
      ]
    ))
  );
  assert_eq!(
    foo(CompleteByteSlice(b"!@#$")),
    Ok((CompleteByteSlice(b"!@#$"), vec![]))
  );
}

#[test]
fn issue_721() {
  assert_eq!(parse_to!("1234", u16), Ok(("", 1234)));
  assert_eq!(parse_to!("foo", String), Ok(("", "foo".to_string())));
}

#[cfg(feature = "alloc")]
named!(issue_717<&[u8], Vec<&[u8]> >,
  separated_list!(tag!([0x0]), is_not!([0x0u8]))
);

struct NoPartialEq {
  value: i32,
}

named!(issue_724<&str, i32>,
  do_parse!(
    metadata: permutation!(
      map!(tag!("hello"), |_| NoPartialEq { value: 1 }),
      map!(tag!("world"), |_| NoPartialEq { value: 2 })
    ) >>
    (metadata.0.value + metadata.1.value)
  )
);

named!(issue_741_str<CompleteStr, CompleteStr>, re_match!(r"^_?[A-Za-z][0-9A-Z_a-z-]*"));
named!(issue_741_bytes<CompleteByteSlice, CompleteByteSlice>, re_bytes_match!(r"^_?[A-Za-z][0-9A-Z_a-z-]*"));

#[test]
fn issue_752() {
    assert_eq!(
        Err::Error(nom::Context::Code("ab", nom::ErrorKind::ParseTo)),
        parse_to!("ab", usize).unwrap_err()
    )
}

fn atom_specials(c: u8) -> bool {
    c == b'q'
}

named!(
    capability<&str>,
    do_parse!(tag!(" ") >> _atom: map_res!(take_till1!(atom_specials), std::str::from_utf8) >> ("a"))
);

#[test]
fn issue_759() {
    assert_eq!(capability(b" abcqd"), Ok((&b"qd"[..], "a")));
}

named_args!(issue_771(count: usize)<Vec<u32>>,
  length_count!(value!(count), call!(nom::be_u32))
);

#[test]
fn issue_768() {
  named!(bit_vec8<CompleteByteSlice, Vec<u16>>, bits!(many0!(take_bits!(u16, 8))));
  named!(bit_vec4<CompleteByteSlice, Vec<u16>>, bits!(many0!(take_bits!(u16, 4))));
  named!(bit_vec3<CompleteByteSlice, Vec<u16>>, bits!(many0!(take_bits!(u16, 3))));
  named!(bit_vec11<CompleteByteSlice, Vec<u16>>, bits!(many0!(take_bits!(u16, 11))));

  let m: Vec<u8> = vec![70, 97, 106, 121, 86, 66, 105, 98, 86, 106, 101];
  assert_eq!(
    bit_vec8(CompleteByteSlice(m.as_slice())),
    Ok((CompleteByteSlice(&[]), vec![70, 97, 106, 121, 86, 66, 105, 98, 86, 106, 101]))
  );
  assert_eq!(
    bit_vec4(CompleteByteSlice(m.as_slice())),
    Ok((CompleteByteSlice(&[]), vec![4, 6, 6, 1, 6, 10, 7, 9, 5, 6, 4, 2, 6, 9, 6, 2, 5, 6, 6, 10, 6, 5]))
  );
  assert_eq!(
    bit_vec3(CompleteByteSlice(m.as_slice())),
    Ok((CompleteByteSlice(&[]), vec![2, 1, 4, 6, 0, 5, 5, 2, 3, 6, 2, 5, 3, 1, 0, 2, 3, 2, 2, 6, 1, 1, 2, 6, 3, 2, 4, 6, 2]))
  );
  assert_eq!(
    bit_vec11(CompleteByteSlice(m.as_slice())),
    Ok((CompleteByteSlice(&[]), vec![563, 90, 1266, 1380, 308, 1417, 717, 613]))
  );
}

/// This test is in a separate module to check that all required symbols are imported in
/// `escaped_transform!()`. Without the module, the `use`-es of the current module would
/// mask the error ('"Use of undeclared type or module `Needed`" in escaped_transform!').
mod issue_780 {
  named!(issue_780<&str, String>,
    escaped_transform!(call!(::nom::alpha), '\\', tag!("n"))
  );
}

// issue 617
named!(digits, take_while1!( is_digit ));
named!(multi_617<&[u8], () >, fold_many0!( digits, (), |_, _| {}));

// Sad :(
named!(multi_617_fails<&[u8], () >, fold_many0!( take_while1!( is_digit ), (), |_, _| {}));

mod issue_647 {
  use nom::{Err,be_f64};
  pub type Input<'a> = &'a [u8];

  #[derive(PartialEq, Debug, Clone)]
  struct Data {
      c: f64,
      v: Vec<f64>
  }

  fn list<'a,'b>(input: Input<'a>, _cs: &'b f64) -> Result<(Input<'a>,Vec<f64>), Err<&'a [u8]>> {
      separated_list_complete!(input, tag!(","),be_f64)
  }

  named!(data<Input,Data>, map!(
      do_parse!(
          c: be_f64 >>
          tag!("\n") >>
          v: call!(list,&c) >>
          (c,v)
      ), |(c,v)| {
          Data {
              c: c,
              v: v
          }
      }
  ));
}

named!(issue_775, take_till1!(|_| true));

#[test]
fn issue_848_overflow_incomplete_bits_to_bytes() {
  named!(parser<&[u8], &[u8]>, bits!(bytes!(take!(0x2000000000000000))));
  assert_eq!(parser(&b""[..]), Err(Err::Failure(error_position!(&b""[..], ErrorKind::TooLarge))));
}
<|endoftext|>


<commit_before>#![cfg(test)]

extern crate easter;
extern crate esprit;
extern crate estree;
extern crate glob;
extern crate joker;
extern crate serde_json;
extern crate test;
extern crate unjson;

use easter::expr::Expr;
use easter::patt::{AssignTarget, Patt};
use easter::stmt::{Stmt, StmtListItem};
use esprit::script;
use estree::IntoScript;
use glob::glob;
use joker::track::Untrack;
use serde_json::value::Value;
use std::ffi::OsStr;
use std::fs::{File, read_dir};
use std::io::Read;
use std::path::Path;
use std::{thread, env};
use test::{TestDesc, TestDescAndFn, TestName, TestFn, test_main};
use test::ShouldPanic::No;
use unjson::Unjson;

fn add_test<F: FnOnce() + Send + 'static>(tests: &mut Vec<TestDescAndFn>, name: String, ignore: bool, f: F) {
    tests.push(TestDescAndFn {
        desc: TestDesc {
            name: TestName::DynTestName(name),
            ignore: ignore,
            should_panic: No
        },
        testfn: TestFn::dyn_test_fn(f)
    });
}

fn as_ref_test(tests: &mut Vec<TestDescAndFn>) {
    add_test(tests, "reference test".to_string(), false, || {
        let mut ast = script("foobar = 17;").unwrap();
        ast.untrack();
        match ast.items.first().unwrap() {
            &StmtListItem::Stmt(Stmt::Expr(_, Expr::Assign(_, Patt::Simple(AssignTarget::Id(ref id)), _), _)) => {
                assert_eq!(id.name.as_ref(), "foobar");
            }
            _ => { panic!("unexpected AST structure"); }
        }
    });
}

const DEFAULT_MB: usize = 4;

fn read_envvar() -> Option<usize> {
    match env::var("ESTREE_STACK_SIZE_MB") {
        Ok(s) => {
            match s.parse() {
                Ok(x) => Some(x),
                Err(_) => None
            }
        }
        Err(env::VarError::NotPresent) => Some(DEFAULT_MB),
        Err(_) => None
    }
}

fn stack_size() -> usize {
    let mb = match read_envvar() {
        Some(x) => x,
        None => {
            println!("warning: invalid ESTREE_STACK_SIZE_MB value; defaulting to 4MB");
            DEFAULT_MB
        }
    };
    mb * 1024 * 1024
}

fn integration_tests(target: &mut Vec<TestDescAndFn>, ignore: bool) {
    let root = Path::new(env!("CARGO_MANIFEST_DIR"));

    let fixtures = {
        let mut fixtures = root.to_path_buf();
        fixtures.push("tests");
        fixtures.push("esprima");
        fixtures.push("test");
        fixtures.push("3rdparty");
        fixtures
    };

    let syntax = fixtures.join("syntax");

    let files =
        read_dir(syntax).unwrap()
        .map(|dir| dir.unwrap().path())
        .filter(|path| path.extension() == Some(OsStr::new("json")))
        .map(|tree_path| {
            let mut source_path_buf = fixtures.join(tree_path.file_name().unwrap());
            source_path_buf.set_extension("js");
            (tree_path, source_path_buf)
        });

    for (tree_path, source_path) in files {
        add_test(target, source_path.strip_prefix(&root).unwrap().to_str().unwrap().to_string(), ignore, move || {
            let expected_ast = thread::Builder::new().stack_size(stack_size()).spawn(|| {
                let v: Value = serde_json::de::from_reader(File::open(tree_path).unwrap()).unwrap();
                v.into_object().unwrap().into_script().map_err(|err| {
                    format!("failed to deserialize script: {}", err)
                }).unwrap()
            }).unwrap().join().unwrap();
            let mut source = String::new();
            File::open(source_path).unwrap().read_to_string(&mut source).unwrap();
            match script(&source[..]) {
                Ok(mut actual_ast) => {
                    actual_ast.untrack();
                    assert!(actual_ast == expected_ast, "integration test got wrong result");
                }
                Err(actual_err) => {
                    panic!("integration test failed to parse:\n{:#?}", actual_err);
                }
            }
        });
    }
}

fn unit_tests(target: &mut Vec<TestDescAndFn>) {
    let root = Path::new(env!("CARGO_MANIFEST_DIR"));

    let fixtures = {
        let mut fixtures = root.to_path_buf();
        fixtures.push("tests");
        fixtures.push("esprima");
        fixtures.push("test");
        fixtures.push("fixtures");
        fixtures
    };

    let testignore: Vec<_> =
        include_str!(".testignore")
        .lines()
        .filter(|s| !s.is_empty() && !s.starts_with("#"))
        .map(|s| glob::Pattern::new(s).unwrap())
        .collect();

    let files =
        read_dir(fixtures.as_path()).unwrap()
        .flat_map(|dir| glob(&format!("{}/**/*.tree.json", dir.unwrap().path().to_str().unwrap())).unwrap())
        .filter_map(|entry| {
            let tree_path = entry.unwrap();
            let source_path = {
                let tree_file_name = tree_path.file_name().unwrap().to_str().unwrap();
                let source_file_name = tree_file_name[..tree_file_name.len() - 9].to_string() + "js";
                tree_path.with_file_name(source_file_name)
            };
            if source_path.exists() {
                let ignore = {
                    let local_tree_path = tree_path.strip_prefix(&fixtures).unwrap();
                    testignore.iter().any(|ignore| ignore.matches_path(&local_tree_path))
                };
                Some((tree_path, source_path, ignore))
            } else {
                None
            }
        });

    for (tree_path, source_path, ignore) in files {
        add_test(target, source_path.strip_prefix(&root).unwrap().to_str().unwrap().to_string(), ignore, move || {
            let v: Value = serde_json::de::from_reader(File::open(tree_path).unwrap()).unwrap();
            let expected_ast = v.into_object().unwrap().into_script().map_err(|err| {
                format!("failed to deserialize script: {}", err)
            }).unwrap();
            let mut source = String::new();
            File::open(source_path).unwrap().read_to_string(&mut source).unwrap();
            match script(&source[..]) {
                Ok(mut actual_ast) => {
                    actual_ast.untrack();
                    assert!(actual_ast == expected_ast, "unit test got wrong result\n\
                    expected AST: {:#?}\n\
                    actual AST: {:#?}", expected_ast, actual_ast);
                }
                Err(actual_err) => {
                    panic!("unit test failed to parse:\n{:#?}", actual_err);
                }
            }
        });
    }
}

fn main() {
    let args: Vec<_> = env::args().collect();
    let mut tests = Vec::new();
    as_ref_test(&mut tests);
    unit_tests(&mut tests);
    let ignore_integration_tests = env::var_os("ESTREE_INTEGRATION_TESTS") == None;
    if ignore_integration_tests {
        println!("note: Run with `ESTREE_INTEGRATION_TESTS=1` to run with integration tests (much slower).");
    }
    integration_tests(&mut tests, ignore_integration_tests);
    test_main(&args, tests);
}
<commit_msg>Respect "errors" field in tests<commit_after>#![cfg(test)]

extern crate easter;
extern crate esprit;
extern crate estree;
extern crate glob;
extern crate joker;
extern crate serde_json;
extern crate test;
extern crate unjson;

use easter::expr::Expr;
use easter::patt::{AssignTarget, Patt};
use easter::stmt::{Stmt, StmtListItem};
use esprit::script;
use estree::IntoScript;
use glob::glob;
use joker::track::Untrack;
use serde_json::value::Value;
use std::ffi::OsStr;
use std::fs::{File, read_dir};
use std::io::Read;
use std::path::Path;
use std::{thread, env};
use test::{TestDesc, TestDescAndFn, TestName, TestFn, test_main};
use test::ShouldPanic::No;
use unjson::{ExtractField, Unjson};

fn add_test<F: FnOnce() + Send + 'static>(tests: &mut Vec<TestDescAndFn>, name: String, ignore: bool, f: F) {
    tests.push(TestDescAndFn {
        desc: TestDesc {
            name: TestName::DynTestName(name),
            ignore: ignore,
            should_panic: No
        },
        testfn: TestFn::dyn_test_fn(f)
    });
}

fn as_ref_test(tests: &mut Vec<TestDescAndFn>) {
    add_test(tests, "reference test".to_string(), false, || {
        let mut ast = script("foobar = 17;").unwrap();
        ast.untrack();
        match ast.items.first().unwrap() {
            &StmtListItem::Stmt(Stmt::Expr(_, Expr::Assign(_, Patt::Simple(AssignTarget::Id(ref id)), _), _)) => {
                assert_eq!(id.name.as_ref(), "foobar");
            }
            _ => { panic!("unexpected AST structure"); }
        }
    });
}

const DEFAULT_MB: usize = 4;

fn read_envvar() -> Option<usize> {
    match env::var("ESTREE_STACK_SIZE_MB") {
        Ok(s) => {
            match s.parse() {
                Ok(x) => Some(x),
                Err(_) => None
            }
        }
        Err(env::VarError::NotPresent) => Some(DEFAULT_MB),
        Err(_) => None
    }
}

fn stack_size() -> usize {
    let mb = match read_envvar() {
        Some(x) => x,
        None => {
            println!("warning: invalid ESTREE_STACK_SIZE_MB value; defaulting to 4MB");
            DEFAULT_MB
        }
    };
    mb * 1024 * 1024
}

fn integration_tests(target: &mut Vec<TestDescAndFn>, ignore: bool) {
    let root = Path::new(env!("CARGO_MANIFEST_DIR"));

    let fixtures = {
        let mut fixtures = root.to_path_buf();
        fixtures.push("tests");
        fixtures.push("esprima");
        fixtures.push("test");
        fixtures.push("3rdparty");
        fixtures
    };

    let syntax = fixtures.join("syntax");

    let files =
        read_dir(syntax).unwrap()
        .map(|dir| dir.unwrap().path())
        .filter(|path| path.extension() == Some(OsStr::new("json")))
        .map(|tree_path| {
            let mut source_path_buf = fixtures.join(tree_path.file_name().unwrap());
            source_path_buf.set_extension("js");
            (tree_path, source_path_buf)
        });

    for (tree_path, source_path) in files {
        add_test(target, source_path.strip_prefix(&root).unwrap().to_str().unwrap().to_string(), ignore, move || {
            let expected_ast = thread::Builder::new().stack_size(stack_size()).spawn(|| {
                let v: Value = serde_json::de::from_reader(File::open(tree_path).unwrap()).unwrap();
                v.into_object().unwrap().into_script().map_err(|err| {
                    format!("failed to deserialize script: {}", err)
                }).unwrap()
            }).unwrap().join().unwrap();
            let mut source = String::new();
            File::open(source_path).unwrap().read_to_string(&mut source).unwrap();
            match script(&source[..]) {
                Ok(mut actual_ast) => {
                    actual_ast.untrack();
                    assert!(actual_ast == expected_ast, "integration test got wrong result");
                }
                Err(actual_err) => {
                    panic!("integration test failed to parse:\n{:#?}", actual_err);
                }
            }
        });
    }
}

fn unit_tests(target: &mut Vec<TestDescAndFn>) {
    let root = Path::new(env!("CARGO_MANIFEST_DIR"));

    let fixtures = {
        let mut fixtures = root.to_path_buf();
        fixtures.push("tests");
        fixtures.push("esprima");
        fixtures.push("test");
        fixtures.push("fixtures");
        fixtures
    };

    let testignore: Vec<_> =
        include_str!(".testignore")
        .lines()
        .filter(|s| !s.is_empty() && !s.starts_with("#"))
        .map(|s| glob::Pattern::new(s).unwrap())
        .collect();

    let files =
        read_dir(fixtures.as_path()).unwrap()
        .flat_map(|dir| glob(&format!("{}/**/*.tree.json", dir.unwrap().path().to_str().unwrap())).unwrap())
        .filter_map(|entry| {
            let tree_path = entry.unwrap();
            let source_path = {
                let tree_file_name = tree_path.file_name().unwrap().to_str().unwrap();
                let source_file_name = tree_file_name[..tree_file_name.len() - 9].to_string() + "js";
                tree_path.with_file_name(source_file_name)
            };
            if source_path.exists() {
                let ignore = {
                    let local_tree_path = tree_path.strip_prefix(&fixtures).unwrap();
                    testignore.iter().any(|ignore| ignore.matches_path(&local_tree_path))
                };
                Some((tree_path, source_path, ignore))
            } else {
                None
            }
        });

    for (tree_path, source_path, ignore) in files {
        add_test(target, source_path.strip_prefix(&root).unwrap().to_str().unwrap().to_string(), ignore, move || {
            let v: Value = serde_json::de::from_reader(File::open(tree_path).unwrap()).unwrap();
            let mut obj = v.into_object().unwrap();
            let expected = match obj.extract_array("errors") {
                Ok(errors) => {
                    Err(errors[0].as_object().unwrap()["message"].clone())
                }
                Err(unjson::error::Error::MissingField(_)) => {
                    Ok(obj.into_script().map_err(|err| {
                        format!("failed to deserialize script: {}", err)
                    }).unwrap())
                }
                Err(err) => panic!(err)
            };
            let mut source = String::new();
            File::open(source_path).unwrap().read_to_string(&mut source).unwrap();
            match (script(&source[..]), expected) {
                (Ok(mut actual_ast), expected) => {
                    actual_ast.untrack();
                    assert!(Ok(&actual_ast) == expected.as_ref(), "unit test got wrong result\n\
                    expected: {:#?}\n\
                    actual AST: {:#?}", expected, actual_ast);
                }
                (Err(actual_err), Ok(_)) => {
                    panic!("unit test failed to parse:\n{:#?}", actual_err);
                }
                (Err(_), Err(_)) => {}
            }
        });
    }
}

fn main() {
    let args: Vec<_> = env::args().collect();
    let mut tests = Vec::new();
    as_ref_test(&mut tests);
    unit_tests(&mut tests);
    let ignore_integration_tests = env::var_os("ESTREE_INTEGRATION_TESTS") == None;
    if ignore_integration_tests {
        println!("note: Run with `ESTREE_INTEGRATION_TESTS=1` to run with integration tests (much slower).");
    }
    integration_tests(&mut tests, ignore_integration_tests);
    test_main(&args, tests);
}
<|endoftext|>


<commit_before>use std::collections::{BTreeMap, HashMap};
use std::fs::File;
use std::io::prelude::*;
use std::iter::FromIterator;

use toml;

use euclid::{TypedPoint2D, TypedVector2D};

use polyomino::*;
use shape::*;

// This is all very ad-hoc, but maybe that's fine.

// TODO: don't just panic on errors.

fn interpret_ivec<U>(val: &toml::Value) -> TypedPoint2D<i32, U> {
    let array = val.as_array().expect("vec was not an array");
    TypedPoint2D::new(
        array[0].as_integer().expect("vec entry was not integer") as i32,
        array[1].as_integer().expect("vec entry was not integer") as i32,
    )
}

fn interpret_color(val: &toml::Value) -> [f32; 3] {
    let array = val.as_array().expect("color was not an array");
    [
        array[0].as_float().expect("color entry was not float") as f32,
        array[1].as_float().expect("color entry was not float") as f32,
        array[2].as_float().expect("color entry was not float") as f32,
    ]
}

fn interpret_shape(
    table: &toml::value::Table,
    new_id: ShapeId,
    name_map: &BTreeMap<String, ShapeId>,
) -> Shape {
    let parent_ids = HashMap::from_iter(
        table["parents"]
            .as_table()
            .expect("parents value was not a table")
            .iter()
            .map(|(parent_name, value)| (name_map[parent_name], interpret_ivec(value))),
    );

    let polyomino_squares = table["polyomino"]
        .as_array()
        .expect("polyomino value was not an array")
        .iter()
        .map(|v| interpret_ivec(v))
        .collect();

    Shape {
        id: new_id,
        parent_ids,
        polyomino: Polyomino {
            squares: polyomino_squares,
        },

        // Drawing:
        fill_color: interpret_color(&table["fill_color"]),
        outline_color: interpret_color(&table["outline_color"]),
    }
}

pub fn parse_universe(string: String) -> LogicalState {
    let toml = string.parse::<toml::Value>().expect("Couldn't parse TOML");
    let toml_table = toml.as_table().expect("TOML wasn't a table");

    let mut name_map: BTreeMap<String, ShapeId> = btreemap![];
    let mut player_name = "";

    let mut current_id = 1;
    for (name, val) in toml_table {
        if name == "player" {
            player_name = val.as_str().expect("player name was not a string");
            continue;
        }

        name_map.insert(name.clone(), ShapeId(current_id));
        current_id += 1;
    }

    let mut shapes: BTreeMap<ShapeId, Shape> = btreemap![];

    for (name, val) in toml_table {
        if name == "player" {
            continue;
        }

        let this_id = name_map[name];
        shapes.insert(
            this_id,
            interpret_shape(
                val.as_table().expect("Shape wasn't a table"),
                this_id,
                &name_map,
            ),
        );
    }
    let universe = Universe { shapes };
    let player_chunk = universe.top_chunk_of_id(name_map[player_name]);
    LogicalState {
        universe,
        player_chunk,
    }
}

pub fn load_universe(filename: String) -> LogicalState {
    let mut f = File::open(filename).expect("Couldn't open file.");

    let mut contents = String::new();
    f.read_to_string(&mut contents).expect("Couldn't read file");

    parse_universe(contents)
}
<commit_msg>Use default colours if none provided<commit_after>use std::collections::{BTreeMap, HashMap};
use std::fs::File;
use std::io::prelude::*;
use std::iter::FromIterator;

use toml;

use euclid::{TypedPoint2D, TypedVector2D};

use polyomino::*;
use shape::*;

// This is all very ad-hoc, but maybe that's fine.

// TODO: don't just panic on errors.

fn interpret_ivec<U>(val: &toml::Value) -> TypedPoint2D<i32, U> {
    let array = val.as_array().expect("vec was not an array");
    TypedPoint2D::new(
        array[0].as_integer().expect("vec entry was not integer") as i32,
        array[1].as_integer().expect("vec entry was not integer") as i32,
    )
}

fn interpret_color(val: &toml::Value) -> [f32; 3] {
    let array = val.as_array().expect("color was not an array");
    [
        array[0].as_float().expect("color entry was not float") as f32,
        array[1].as_float().expect("color entry was not float") as f32,
        array[2].as_float().expect("color entry was not float") as f32,
    ]
}

fn interpret_shape(
    table: &toml::value::Table,
    new_id: ShapeId,
    name_map: &BTreeMap<String, ShapeId>,
) -> Shape {
    let parent_ids = HashMap::from_iter(
        table["parents"]
            .as_table()
            .expect("parents value was not a table")
            .iter()
            .map(|(parent_name, value)| (name_map[parent_name], interpret_ivec(value))),
    );

    let polyomino_squares = table["polyomino"]
        .as_array()
        .expect("polyomino value was not an array")
        .iter()
        .map(|v| interpret_ivec(v))
        .collect();

    let fill_color = match table.get("fill_color") {
        Some(entry) => interpret_color(entry),
        None => [0.5, 0.5, 1.0], // Default
    };

    let outline_color = match table.get("outline_color") {
        Some(entry) => interpret_color(entry),
        None => [0.0, 0.0, 0.0], // Default
    };

    Shape {
        id: new_id,
        parent_ids,
        polyomino: Polyomino {
            squares: polyomino_squares,
        },

        // Drawing:
        fill_color,
        outline_color,
    }
}

pub fn parse_universe(string: String) -> LogicalState {
    let toml = string.parse::<toml::Value>().expect("Couldn't parse TOML");
    let toml_table = toml.as_table().expect("TOML wasn't a table");

    let mut name_map: BTreeMap<String, ShapeId> = btreemap![];
    let mut player_name = "";

    let mut current_id = 1;
    for (name, val) in toml_table {
        if name == "player" {
            player_name = val.as_str().expect("player name was not a string");
            continue;
        }

        name_map.insert(name.clone(), ShapeId(current_id));
        current_id += 1;
    }

    let mut shapes: BTreeMap<ShapeId, Shape> = btreemap![];

    for (name, val) in toml_table {
        if name == "player" {
            continue;
        }

        let this_id = name_map[name];
        shapes.insert(
            this_id,
            interpret_shape(
                val.as_table().expect("Shape wasn't a table"),
                this_id,
                &name_map,
            ),
        );
    }
    let universe = Universe { shapes };
    let player_chunk = universe.top_chunk_of_id(name_map[player_name]);
    LogicalState {
        universe,
        player_chunk,
    }
}

pub fn load_universe(filename: String) -> LogicalState {
    let mut f = File::open(filename).expect("Couldn't open file.");

    let mut contents = String::new();
    f.read_to_string(&mut contents).expect("Couldn't read file");

    parse_universe(contents)
}
<|endoftext|>


<commit_before>/*
 * Copyright 2022 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

use k8s_openapi::{
    apiextensions_apiserver::pkg::apis::apiextensions::v1::{
        CustomResourceDefinition, CustomResourceDefinitionNames, CustomResourceDefinitionSpec,
        CustomResourceDefinitionVersion, CustomResourceValidation,
    },
    apimachinery::pkg::apis::meta::v1::ObjectMeta,
};
use kube::core::Resource;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

use crate::endpoint::Endpoint;

const QUILKIN_TOKEN_LABEL: &str = "quilkin.dev/tokens";

/// Auto-generated derived type for GameServerSpec via `CustomResource`
#[derive(Clone, Debug, schemars::JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct GameServer {
    #[schemars(skip)]
    metadata: ObjectMeta,
    spec: GameServerSpec,
    status: Option<GameServerStatus>,
}

#[derive(Clone, Debug, Deserialize, schemars::JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct Inner {
    #[schemars(skip)]
    metadata: ObjectMeta,
    spec: GameServerSpec,
    status: Option<GameServerStatus>,
}

impl<'de> serde::Deserialize<'de> for GameServer {
    fn deserialize<D: serde::Deserializer<'de>>(de: D) -> Result<Self, D::Error> {
        use serde::de::Error;
        let value = serde_json::Value::deserialize(de).unwrap();

        serde_json::from_value::<Inner>(value.clone())
            .map_err(|error| {
                tracing::trace!(%error, %value, "gameserver failed");
                Error::custom(error)
            })
            .map(
                |Inner {
                     metadata,
                     spec,
                     status,
                 }| Self {
                    metadata,
                    spec,
                    status,
                },
            )
    }
}

impl GameServer {
    pub fn new(name: &str, spec: GameServerSpec) -> Self {
        Self {
            metadata: ObjectMeta {
                name: Some(name.to_string()),
                ..Default::default()
            },
            spec,
            status: None,
        }
    }

    pub fn is_allocated(&self) -> bool {
        self.status.as_ref().map_or(false, |status| {
            tracing::trace!(%status.address, ?status.state, "checking gameserver");
            matches!(status.state, GameServerState::Allocated)
        })
    }
}

impl serde::Serialize for GameServer {
    fn serialize<S: serde::Serializer>(&self, ser: S) -> Result<S::Ok, S::Error> {
        use serde::ser::SerializeStruct;
        let mut obj = ser.serialize_struct("GameServer", 5)?;
        obj.serialize_field("apiVersion", &GameServer::api_version(&()))?;
        obj.serialize_field("kind", &GameServer::kind(&()))?;
        obj.serialize_field("metadata", &self.metadata)?;
        obj.serialize_field("spec", &self.spec)?;
        obj.serialize_field("status", &self.status)?;
        obj.end()
    }
}

impl Resource for GameServer {
    type DynamicType = ();

    fn group(_: &()) -> std::borrow::Cow<'_, str> {
        "agones.dev".into()
    }

    fn kind(_: &()) -> std::borrow::Cow<'_, str> {
        "GameServer".into()
    }

    fn version(_: &()) -> std::borrow::Cow<'_, str> {
        "v1".into()
    }

    fn api_version(_: &()) -> std::borrow::Cow<'_, str> {
        "agones.dev/v1".into()
    }

    fn plural(_: &()) -> std::borrow::Cow<'_, str> {
        "gameservers".into()
    }

    fn meta(&self) -> &ObjectMeta {
        &self.metadata
    }

    fn meta_mut(&mut self) -> &mut ObjectMeta {
        &mut self.metadata
    }
}

impl kube::core::crd::v1::CustomResourceExt for GameServer {
    fn crd() -> CustomResourceDefinition {
        let open_api_v3_schema = Some(
            schemars::gen::SchemaSettings::openapi3()
                .with(|s| {
                    s.inline_subschemas = true;
                    s.meta_schema = None;
                })
                .with_visitor(kube::core::schema::StructuralSchemaRewriter)
                .into_generator()
                .into_root_schema_for::<Self>(),
        );

        CustomResourceDefinition {
            metadata: ObjectMeta {
                name: Some("gameservers.agones.dev".into()),
                ..<_>::default()
            },
            spec: CustomResourceDefinitionSpec {
                group: "agones.dev".into(),
                scope: "Namespaced".into(),
                names: CustomResourceDefinitionNames {
                    plural: "gameservers".into(),
                    singular: Some("gameserver".into()),
                    kind: "GameServer".into(),
                    ..<_>::default()
                },
                versions: vec![CustomResourceDefinitionVersion {
                    name: "v1".into(),
                    served: true,
                    storage: true,
                    schema: Some(CustomResourceValidation {
                        // Hack to get around k8s and schemars having different
                        // root JSON schema types. Replace this with a From impl
                        open_api_v3_schema: serde_json::from_value(
                            serde_json::to_value(&open_api_v3_schema).unwrap(),
                        )
                        .unwrap(),
                    }),
                    ..<_>::default()
                }],
                ..<_>::default()
            },
            status: None,
        }
    }

    fn crd_name() -> &'static str {
        "gameservers.agones.dev"
    }

    fn api_resource() -> kube::core::dynamic::ApiResource {
        kube::core::dynamic::ApiResource::erase::<Self>(&())
    }

    fn shortnames() -> &'static [&'static str] {
        &[]
    }
}

impl kube::core::object::HasSpec for GameServer {
    type Spec = GameServerSpec;

    fn spec(&self) -> &GameServerSpec {
        &self.spec
    }
    fn spec_mut(&mut self) -> &mut GameServerSpec {
        &mut self.spec
    }
}

#[derive(Clone, Debug, Deserialize, Serialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct GameServerSpec {
    /// Container specifies which Pod container is the game server. Only
    /// required if there is more than one container defined.
    container: String,
    /// Ports are the array of ports that can be exposed via the game server
    #[serde(default)]
    ports: Vec<GameServerPort>,
    /// Configures health checking
    health: Health,
    /// Scheduling strategy. Defaults to "Packed"
    scheduling: SchedulingStrategy,
    /// Specifies parameters for the Agones SDK Server sidecar container.
    sdk_server: SdkServer,
    /// Describes the Pod that will be created for the [`GameServer`].
    template: k8s_openapi::api::core::v1::PodTemplateSpec,
}

impl TryFrom<GameServer> for Endpoint {
    type Error = tonic::Status;

    fn try_from(server: GameServer) -> Result<Self, Self::Error> {
        let status = server
            .status
            .as_ref()
            .ok_or_else(|| tonic::Status::internal("No status found for game server"))?;

        let tokens = match server.metadata.annotations.as_ref() {
            Some(annotations) => annotations
                .get(QUILKIN_TOKEN_LABEL)
                .map(|value| {
                    value
                        .split(',')
                        .map(String::from)
                        .map(base64::decode)
                        .filter_map(Result::ok)
                        .collect::<std::collections::BTreeSet<_>>()
                })
                .unwrap_or_default(),
            None => <_>::default(),
        };

        let address = status.address.clone();
        let port = server
            .spec
            .ports
            .first()
            .ok_or_else(|| tonic::Status::internal("No port found for game server"))?
            .container_port;
        let filter_metadata = crate::endpoint::Metadata { tokens };
        Ok(Self::with_metadata((address, port).into(), filter_metadata))
    }
}

impl TryFrom<Vec<GameServer>> for crate::endpoint::LocalityEndpoints {
    type Error = tonic::Status;

    fn try_from(servers: Vec<GameServer>) -> Result<Self, Self::Error> {
        Ok(servers
            .into_iter()
            .map(Endpoint::try_from)
            .collect::<Result<std::collections::BTreeSet<_>, _>>()?
            .into())
    }
}

#[derive(Clone, Debug, Deserialize, Serialize, JsonSchema)]
struct Health {
    /// Whether health checking is disabled or not
    #[serde(default)]
    disabled: bool,
    /// The number of seconds each health ping has to occur in
    #[serde(rename = "periodSeconds", default = "default_period_seconds")]
    period_seconds: i32,
    /// How many failures in a row constitutes unhealthy
    #[serde(rename = "failureThreshold", default = "default_failure_threshold")]
    failure_threshold: i32,
    /// Initial delay before checking health
    #[serde(
        rename = "initialDelaySeconds",
        default = "default_initial_delay_seconds"
    )]
    initial_delay_seconds: i32,
}

fn default_period_seconds() -> i32 {
    5
}
fn default_initial_delay_seconds() -> i32 {
    5
}
fn default_failure_threshold() -> i32 {
    5
}

/// Defines a set of Ports that are to be exposed via the [`GameServer`].
#[derive(Clone, Debug, Deserialize, Serialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
struct GameServerPort {
    /// Name is the descriptive name of the port
    name: String,
    /// PortPolicy defines the policy for how the HostPort is populated.
    /// Dynamic port will allocate a HostPort within the selected MIN_PORT and MAX_PORT range passed to the controller
    /// at installation time.
    /// When `Static` portPolicy is specified, `HostPort` is required, to specify the port that game clients will
    /// connect to
    #[serde(default)]
    port_policy: PortPolicy,
    /// The name of the container on which to open the port. Defaults to the
    /// game server container.
    container: Option<String>,
    /// The port that is being opened on the specified container's process
    container_port: u16,
    /// The port exposed on the host for clients to connect to
    host_port: Option<u16>,
    /// Protocol is the network protocol being used. Defaults to UDP. TCP and TCPUDP are other options.
    #[serde(default)]
    protocol: Protocol,
}

/// The status for a [`GameServer`] resource.
#[derive(Clone, Debug, Deserialize, Serialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
struct GameServerStatus {
    /// The current state of a [`GameServer`].
    state: GameServerState,
    ports: Option<Vec<GameServerStatusPort>>,
    address: String,
    node_name: String,
    reserved_until: Option<k8s_openapi::apimachinery::pkg::apis::meta::v1::Time>,
}

#[derive(Clone, Debug, Deserialize, Serialize, JsonSchema)]
pub enum GameServerState {
    /// A dynamically allocating GameServer is being created, an open port needs
    /// to be allocated
    PortAllocation,
    /// The Pod for the GameServer is being created.
    Creating,
    /// The Pods for the GameServer are being created but are not yet Scheduled
    Starting,
    /// We have determined that the Pod has been scheduled in the cluster --
    /// basically, we have a NodeName
    Scheduled,
    /// The GameServer has declared that it is ready
    RequestReady,
    /// The GameServer is ready to take connections from game clients.
    Ready,
    /// The GameServer has shutdown and everything needs to be deleted from the cluster
    Shutdown,
    /// Something has gone wrong with the Gameserver and it cannot be resolved
    Error,
    /// The GameServer has failed its health checks
    Unhealthy,
    /// The GameServer is reserved and therefore can be allocated but not removed
    Reserved,
    /// The GameServer has been allocated to a session
    Allocated,
}

/// The port that was allocated to a GameServer.
#[derive(Clone, Debug, Deserialize, Serialize, JsonSchema)]
pub struct GameServerStatusPort {
    pub name: String,
    pub port: u16,
}

/// Parameters for the Agones SDK Server sidecar container
#[derive(Clone, Debug, Deserialize, Serialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct SdkServer {
    /// LogLevel for SDK server (sidecar) logs. Defaults to "Info"
    #[serde(default)]
    log_level: SdkServerLogLevel,
    /// GRPCPort is the port on which the SDK Server binds the gRPC server to accept incoming connections
    #[serde(default = "default_sdk_grpc_port")]
    grpc_port: u16,
    /// HTTPPort is the port on which the SDK Server binds the HTTP gRPC gateway server to accept incoming connections
    #[serde(default = "default_sdk_http_port")]
    http_port: u16,
}

fn default_sdk_grpc_port() -> u16 {
    9357
}
fn default_sdk_http_port() -> u16 {
    9358
}

#[derive(Clone, Debug, Deserialize, Serialize, JsonSchema)]
pub enum SdkServerLogLevel {
    /// Output all messages except for debug messages.
    Info,
    /// Output all messages including debug messages.
    Debug,
    /// Only output error messages.
    Error,
}

impl Default for SdkServerLogLevel {
    fn default() -> Self {
        Self::Info
    }
}

#[derive(Clone, Debug, Deserialize, Serialize, JsonSchema)]
pub enum PortPolicy {
    /// The user defines the host port to be used in the configuration.
    Static,
    /// The system will choose an open port for the [`GameServer`] in question.
    Dynamic,
    /// Dynamically sets the container port to the same value as the dynamically
    /// selected host port. This will mean that users will need to lookup what
    /// port has been opened through the server side SDK.
    Passthrough,
}

impl Default for PortPolicy {
    fn default() -> Self {
        Self::Dynamic
    }
}

/// the strategy that a Fleet & GameServers will use when scheduling
/// GameServers' Pods across a cluster. In future versions, this will also
/// impact Fleet scale down, and Pod Scheduling.
#[derive(Clone, Debug, Deserialize, Serialize, JsonSchema)]
pub enum SchedulingStrategy {
    /// Prioritise allocating GameServers on Nodes with the most Allocated, and
    /// then Ready GameServers to bin pack as many Allocated GameServers on a
    /// single node.  This is most useful for dynamic Kubernetes clusters - such
    /// as on Cloud Providers.
    Packed,
    /// prioritise allocating GameServers on Nodes with the least Allocated, and
    /// then Ready GameServers to distribute Allocated GameServers across many
    /// nodes. This is most useful for statically sized Kubernetes clusters -
    /// such as on physical hardware.
    Distributed,
}

#[derive(Clone, Debug, Deserialize, Serialize, JsonSchema)]
pub enum Protocol {
    #[serde(rename = "UDP")]
    Udp,
    #[serde(rename = "TCP")]
    Tcp,
    #[serde(rename = "TCPUDP")]
    UdpTcp,
}

impl Default for Protocol {
    fn default() -> Self {
        Self::Udp
    }
}
<commit_msg>Use status.ports rather spec.ports with Agones (#562)<commit_after>/*
 * Copyright 2022 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

use k8s_openapi::{
    apiextensions_apiserver::pkg::apis::apiextensions::v1::{
        CustomResourceDefinition, CustomResourceDefinitionNames, CustomResourceDefinitionSpec,
        CustomResourceDefinitionVersion, CustomResourceValidation,
    },
    apimachinery::pkg::apis::meta::v1::ObjectMeta,
};
use kube::core::Resource;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

use crate::endpoint::Endpoint;

const QUILKIN_TOKEN_LABEL: &str = "quilkin.dev/tokens";

/// Auto-generated derived type for GameServerSpec via `CustomResource`
#[derive(Clone, Debug, schemars::JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct GameServer {
    #[schemars(skip)]
    metadata: ObjectMeta,
    spec: GameServerSpec,
    status: Option<GameServerStatus>,
}

#[derive(Clone, Debug, Deserialize, schemars::JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct Inner {
    #[schemars(skip)]
    metadata: ObjectMeta,
    spec: GameServerSpec,
    status: Option<GameServerStatus>,
}

impl<'de> serde::Deserialize<'de> for GameServer {
    fn deserialize<D: serde::Deserializer<'de>>(de: D) -> Result<Self, D::Error> {
        use serde::de::Error;
        let value = serde_json::Value::deserialize(de).unwrap();

        serde_json::from_value::<Inner>(value.clone())
            .map_err(|error| {
                tracing::trace!(%error, %value, "gameserver failed");
                Error::custom(error)
            })
            .map(
                |Inner {
                     metadata,
                     spec,
                     status,
                 }| Self {
                    metadata,
                    spec,
                    status,
                },
            )
    }
}

impl GameServer {
    pub fn new(name: &str, spec: GameServerSpec) -> Self {
        Self {
            metadata: ObjectMeta {
                name: Some(name.to_string()),
                ..Default::default()
            },
            spec,
            status: None,
        }
    }

    pub fn is_allocated(&self) -> bool {
        self.status.as_ref().map_or(false, |status| {
            tracing::trace!(%status.address, ?status.state, "checking gameserver");
            matches!(status.state, GameServerState::Allocated)
        })
    }
}

impl serde::Serialize for GameServer {
    fn serialize<S: serde::Serializer>(&self, ser: S) -> Result<S::Ok, S::Error> {
        use serde::ser::SerializeStruct;
        let mut obj = ser.serialize_struct("GameServer", 5)?;
        obj.serialize_field("apiVersion", &GameServer::api_version(&()))?;
        obj.serialize_field("kind", &GameServer::kind(&()))?;
        obj.serialize_field("metadata", &self.metadata)?;
        obj.serialize_field("spec", &self.spec)?;
        obj.serialize_field("status", &self.status)?;
        obj.end()
    }
}

impl Resource for GameServer {
    type DynamicType = ();

    fn group(_: &()) -> std::borrow::Cow<'_, str> {
        "agones.dev".into()
    }

    fn kind(_: &()) -> std::borrow::Cow<'_, str> {
        "GameServer".into()
    }

    fn version(_: &()) -> std::borrow::Cow<'_, str> {
        "v1".into()
    }

    fn api_version(_: &()) -> std::borrow::Cow<'_, str> {
        "agones.dev/v1".into()
    }

    fn plural(_: &()) -> std::borrow::Cow<'_, str> {
        "gameservers".into()
    }

    fn meta(&self) -> &ObjectMeta {
        &self.metadata
    }

    fn meta_mut(&mut self) -> &mut ObjectMeta {
        &mut self.metadata
    }
}

impl kube::core::crd::v1::CustomResourceExt for GameServer {
    fn crd() -> CustomResourceDefinition {
        let open_api_v3_schema = Some(
            schemars::gen::SchemaSettings::openapi3()
                .with(|s| {
                    s.inline_subschemas = true;
                    s.meta_schema = None;
                })
                .with_visitor(kube::core::schema::StructuralSchemaRewriter)
                .into_generator()
                .into_root_schema_for::<Self>(),
        );

        CustomResourceDefinition {
            metadata: ObjectMeta {
                name: Some("gameservers.agones.dev".into()),
                ..<_>::default()
            },
            spec: CustomResourceDefinitionSpec {
                group: "agones.dev".into(),
                scope: "Namespaced".into(),
                names: CustomResourceDefinitionNames {
                    plural: "gameservers".into(),
                    singular: Some("gameserver".into()),
                    kind: "GameServer".into(),
                    ..<_>::default()
                },
                versions: vec![CustomResourceDefinitionVersion {
                    name: "v1".into(),
                    served: true,
                    storage: true,
                    schema: Some(CustomResourceValidation {
                        // Hack to get around k8s and schemars having different
                        // root JSON schema types. Replace this with a From impl
                        open_api_v3_schema: serde_json::from_value(
                            serde_json::to_value(&open_api_v3_schema).unwrap(),
                        )
                        .unwrap(),
                    }),
                    ..<_>::default()
                }],
                ..<_>::default()
            },
            status: None,
        }
    }

    fn crd_name() -> &'static str {
        "gameservers.agones.dev"
    }

    fn api_resource() -> kube::core::dynamic::ApiResource {
        kube::core::dynamic::ApiResource::erase::<Self>(&())
    }

    fn shortnames() -> &'static [&'static str] {
        &[]
    }
}

impl kube::core::object::HasSpec for GameServer {
    type Spec = GameServerSpec;

    fn spec(&self) -> &GameServerSpec {
        &self.spec
    }
    fn spec_mut(&mut self) -> &mut GameServerSpec {
        &mut self.spec
    }
}

#[derive(Clone, Debug, Deserialize, Serialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct GameServerSpec {
    /// Container specifies which Pod container is the game server. Only
    /// required if there is more than one container defined.
    container: String,
    /// Ports are the array of ports that can be exposed via the game server
    #[serde(default)]
    ports: Vec<GameServerPort>,
    /// Configures health checking
    health: Health,
    /// Scheduling strategy. Defaults to "Packed"
    scheduling: SchedulingStrategy,
    /// Specifies parameters for the Agones SDK Server sidecar container.
    sdk_server: SdkServer,
    /// Describes the Pod that will be created for the [`GameServer`].
    template: k8s_openapi::api::core::v1::PodTemplateSpec,
}

impl TryFrom<GameServer> for Endpoint {
    type Error = tonic::Status;

    fn try_from(server: GameServer) -> Result<Self, Self::Error> {
        let status = server
            .status
            .as_ref()
            .ok_or_else(|| tonic::Status::internal("No status found for game server"))?;

        let tokens = match server.metadata.annotations.as_ref() {
            Some(annotations) => annotations
                .get(QUILKIN_TOKEN_LABEL)
                .map(|value| {
                    value
                        .split(',')
                        .map(String::from)
                        .map(base64::decode)
                        .filter_map(Result::ok)
                        .collect::<std::collections::BTreeSet<_>>()
                })
                .unwrap_or_default(),
            None => <_>::default(),
        };

        let address = status.address.clone();
        let port = status
            .ports
            .as_ref()
            .and_then(|ports| ports.first().map(|status| status.port))
            .ok_or_else(|| tonic::Status::internal("No port found for game server"))?;
        let filter_metadata = crate::endpoint::Metadata { tokens };
        Ok(Self::with_metadata((address, port).into(), filter_metadata))
    }
}

impl TryFrom<Vec<GameServer>> for crate::endpoint::LocalityEndpoints {
    type Error = tonic::Status;

    fn try_from(servers: Vec<GameServer>) -> Result<Self, Self::Error> {
        Ok(servers
            .into_iter()
            .map(Endpoint::try_from)
            .collect::<Result<std::collections::BTreeSet<_>, _>>()?
            .into())
    }
}

#[derive(Clone, Debug, Deserialize, Serialize, JsonSchema)]
struct Health {
    /// Whether health checking is disabled or not
    #[serde(default)]
    disabled: bool,
    /// The number of seconds each health ping has to occur in
    #[serde(rename = "periodSeconds", default = "default_period_seconds")]
    period_seconds: i32,
    /// How many failures in a row constitutes unhealthy
    #[serde(rename = "failureThreshold", default = "default_failure_threshold")]
    failure_threshold: i32,
    /// Initial delay before checking health
    #[serde(
        rename = "initialDelaySeconds",
        default = "default_initial_delay_seconds"
    )]
    initial_delay_seconds: i32,
}

fn default_period_seconds() -> i32 {
    5
}
fn default_initial_delay_seconds() -> i32 {
    5
}
fn default_failure_threshold() -> i32 {
    5
}

/// Defines a set of Ports that are to be exposed via the [`GameServer`].
#[derive(Clone, Debug, Deserialize, Serialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
struct GameServerPort {
    /// Name is the descriptive name of the port
    name: String,
    /// PortPolicy defines the policy for how the HostPort is populated.
    /// Dynamic port will allocate a HostPort within the selected MIN_PORT and MAX_PORT range passed to the controller
    /// at installation time.
    /// When `Static` portPolicy is specified, `HostPort` is required, to specify the port that game clients will
    /// connect to
    #[serde(default)]
    port_policy: PortPolicy,
    /// The name of the container on which to open the port. Defaults to the
    /// game server container.
    container: Option<String>,
    /// The port that is being opened on the specified container's process
    container_port: u16,
    /// The port exposed on the host for clients to connect to
    host_port: Option<u16>,
    /// Protocol is the network protocol being used. Defaults to UDP. TCP and TCPUDP are other options.
    #[serde(default)]
    protocol: Protocol,
}

/// The status for a [`GameServer`] resource.
#[derive(Clone, Debug, Deserialize, Serialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
struct GameServerStatus {
    /// The current state of a [`GameServer`].
    state: GameServerState,
    ports: Option<Vec<GameServerStatusPort>>,
    address: String,
    node_name: String,
    reserved_until: Option<k8s_openapi::apimachinery::pkg::apis::meta::v1::Time>,
}

#[derive(Clone, Debug, Deserialize, Serialize, JsonSchema)]
pub enum GameServerState {
    /// A dynamically allocating GameServer is being created, an open port needs
    /// to be allocated
    PortAllocation,
    /// The Pod for the GameServer is being created.
    Creating,
    /// The Pods for the GameServer are being created but are not yet Scheduled
    Starting,
    /// We have determined that the Pod has been scheduled in the cluster --
    /// basically, we have a NodeName
    Scheduled,
    /// The GameServer has declared that it is ready
    RequestReady,
    /// The GameServer is ready to take connections from game clients.
    Ready,
    /// The GameServer has shutdown and everything needs to be deleted from the cluster
    Shutdown,
    /// Something has gone wrong with the Gameserver and it cannot be resolved
    Error,
    /// The GameServer has failed its health checks
    Unhealthy,
    /// The GameServer is reserved and therefore can be allocated but not removed
    Reserved,
    /// The GameServer has been allocated to a session
    Allocated,
}

/// The port that was allocated to a GameServer.
#[derive(Clone, Debug, Deserialize, Serialize, JsonSchema)]
pub struct GameServerStatusPort {
    pub name: String,
    pub port: u16,
}

/// Parameters for the Agones SDK Server sidecar container
#[derive(Clone, Debug, Deserialize, Serialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub struct SdkServer {
    /// LogLevel for SDK server (sidecar) logs. Defaults to "Info"
    #[serde(default)]
    log_level: SdkServerLogLevel,
    /// GRPCPort is the port on which the SDK Server binds the gRPC server to accept incoming connections
    #[serde(default = "default_sdk_grpc_port")]
    grpc_port: u16,
    /// HTTPPort is the port on which the SDK Server binds the HTTP gRPC gateway server to accept incoming connections
    #[serde(default = "default_sdk_http_port")]
    http_port: u16,
}

fn default_sdk_grpc_port() -> u16 {
    9357
}
fn default_sdk_http_port() -> u16 {
    9358
}

#[derive(Clone, Debug, Deserialize, Serialize, JsonSchema)]
pub enum SdkServerLogLevel {
    /// Output all messages except for debug messages.
    Info,
    /// Output all messages including debug messages.
    Debug,
    /// Only output error messages.
    Error,
}

impl Default for SdkServerLogLevel {
    fn default() -> Self {
        Self::Info
    }
}

#[derive(Clone, Debug, Deserialize, Serialize, JsonSchema)]
pub enum PortPolicy {
    /// The user defines the host port to be used in the configuration.
    Static,
    /// The system will choose an open port for the [`GameServer`] in question.
    Dynamic,
    /// Dynamically sets the container port to the same value as the dynamically
    /// selected host port. This will mean that users will need to lookup what
    /// port has been opened through the server side SDK.
    Passthrough,
}

impl Default for PortPolicy {
    fn default() -> Self {
        Self::Dynamic
    }
}

/// the strategy that a Fleet & GameServers will use when scheduling
/// GameServers' Pods across a cluster. In future versions, this will also
/// impact Fleet scale down, and Pod Scheduling.
#[derive(Clone, Debug, Deserialize, Serialize, JsonSchema)]
pub enum SchedulingStrategy {
    /// Prioritise allocating GameServers on Nodes with the most Allocated, and
    /// then Ready GameServers to bin pack as many Allocated GameServers on a
    /// single node.  This is most useful for dynamic Kubernetes clusters - such
    /// as on Cloud Providers.
    Packed,
    /// prioritise allocating GameServers on Nodes with the least Allocated, and
    /// then Ready GameServers to distribute Allocated GameServers across many
    /// nodes. This is most useful for statically sized Kubernetes clusters -
    /// such as on physical hardware.
    Distributed,
}

#[derive(Clone, Debug, Deserialize, Serialize, JsonSchema)]
pub enum Protocol {
    #[serde(rename = "UDP")]
    Udp,
    #[serde(rename = "TCP")]
    Tcp,
    #[serde(rename = "TCPUDP")]
    UdpTcp,
}

impl Default for Protocol {
    fn default() -> Self {
        Self::Udp
    }
}
<|endoftext|>