<commit_before>// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// ignore-fast

#[feature(managed_boxes)];

use std::cell::RefCell;
use std::libc::c_void;
use std::ptr;
use std::mem;
use std::unstable::intrinsics::{TyDesc, get_tydesc, visit_tydesc, TyVisitor, Disr, Opaque};
use std::unstable::raw::Vec;

#[doc = "High-level interfaces to `std::unstable::intrinsics::visit_ty` reflection system."]

/// Trait for visitor that wishes to reflect on data.
trait movable_ptr {
    fn move_ptr(&mut self, adjustment: |*c_void| -> *c_void);
}

/// Helper function for alignment calculation.
#[inline(always)]
fn align(size: uint, align: uint) -> uint {
    ((size + align) - 1u) & !(align - 1u)
}

struct ptr_visit_adaptor<V>(Inner<V>);

impl<V:TyVisitor + movable_ptr> ptr_visit_adaptor<V> {
    fn inner<'a>(&'a mut self) -> &'a mut V {
        let ptr_visit_adaptor(ref mut i) = *self;
        &mut i.inner
    }
}

impl<V:TyVisitor + movable_ptr> ptr_visit_adaptor<V> {

    #[inline(always)]
    pub fn bump(&mut self, sz: uint) {
      self.inner().move_ptr(|p| ((p as uint) + sz) as *c_void)
    }

    #[inline(always)]
    pub fn align(&mut self, a: uint) {
      self.inner().move_ptr(|p| align(p as uint, a) as *c_void)
    }

    #[inline(always)]
    pub fn align_to<T>(&mut self) {
        self.align(mem::min_align_of::<T>());
    }

    #[inline(always)]
    pub fn bump_past<T>(&mut self) {
        self.bump(mem::size_of::<T>());
    }

}

impl<V:TyVisitor + movable_ptr> TyVisitor for ptr_visit_adaptor<V> {

    fn visit_bot(&mut self) -> bool {
        self.align_to::<()>();
        if ! self.inner().visit_bot() { return false; }
        self.bump_past::<()>();
        true
    }

    fn visit_nil(&mut self) -> bool {
        self.align_to::<()>();
        if ! self.inner().visit_nil() { return false; }
        self.bump_past::<()>();
        true
    }

    fn visit_bool(&mut self) -> bool {
        self.align_to::<bool>();
        if ! self.inner().visit_bool() { return false; }
        self.bump_past::<bool>();
        true
    }

    fn visit_int(&mut self) -> bool {
        self.align_to::<int>();
        if ! self.inner().visit_int() { return false; }
        self.bump_past::<int>();
        true
    }

    fn visit_i8(&mut self) -> bool {
        self.align_to::<i8>();
        if ! self.inner().visit_i8() { return false; }
        self.bump_past::<i8>();
        true
    }

    fn visit_i16(&mut self) -> bool {
        self.align_to::<i16>();
        if ! self.inner().visit_i16() { return false; }
        self.bump_past::<i16>();
        true
    }

    fn visit_i32(&mut self) -> bool {
        self.align_to::<i32>();
        if ! self.inner().visit_i32() { return false; }
        self.bump_past::<i32>();
        true
    }

    fn visit_i64(&mut self) -> bool {
        self.align_to::<i64>();
        if ! self.inner().visit_i64() { return false; }
        self.bump_past::<i64>();
        true
    }

    fn visit_uint(&mut self) -> bool {
        self.align_to::<uint>();
        if ! self.inner().visit_uint() { return false; }
        self.bump_past::<uint>();
        true
    }

    fn visit_u8(&mut self) -> bool {
        self.align_to::<u8>();
        if ! self.inner().visit_u8() { return false; }
        self.bump_past::<u8>();
        true
    }

    fn visit_u16(&mut self) -> bool {
        self.align_to::<u16>();
        if ! self.inner().visit_u16() { return false; }
        self.bump_past::<u16>();
        true
    }

    fn visit_u32(&mut self) -> bool {
        self.align_to::<u32>();
        if ! self.inner().visit_u32() { return false; }
        self.bump_past::<u32>();
        true
    }

    fn visit_u64(&mut self) -> bool {
        self.align_to::<u64>();
        if ! self.inner().visit_u64() { return false; }
        self.bump_past::<u64>();
        true
    }

    fn visit_f32(&mut self) -> bool {
        self.align_to::<f32>();
        if ! self.inner().visit_f32() { return false; }
        self.bump_past::<f32>();
        true
    }

    fn visit_f64(&mut self) -> bool {
        self.align_to::<f64>();
        if ! self.inner().visit_f64() { return false; }
        self.bump_past::<f64>();
        true
    }

    fn visit_char(&mut self) -> bool {
        self.align_to::<char>();
        if ! self.inner().visit_char() { return false; }
        self.bump_past::<char>();
        true
    }

    fn visit_estr_box(&mut self) -> bool {
        true
    }

    fn visit_estr_uniq(&mut self) -> bool {
        self.align_to::<~str>();
        if ! self.inner().visit_estr_uniq() { return false; }
        self.bump_past::<~str>();
        true
    }

    fn visit_estr_slice(&mut self) -> bool {
        self.align_to::<&'static str>();
        if ! self.inner().visit_estr_slice() { return false; }
        self.bump_past::<&'static str>();
        true
    }

    fn visit_estr_fixed(&mut self, n: uint,
                        sz: uint,
                        align: uint) -> bool {
        self.align(align);
        if ! self.inner().visit_estr_fixed(n, sz, align) { return false; }
        self.bump(sz);
        true
    }

    fn visit_box(&mut self, mtbl: uint, inner: *TyDesc) -> bool {
        self.align_to::<@u8>();
        if ! self.inner().visit_box(mtbl, inner) { return false; }
        self.bump_past::<@u8>();
        true
    }

    fn visit_uniq(&mut self, mtbl: uint, inner: *TyDesc) -> bool {
        self.align_to::<~u8>();
        if ! self.inner().visit_uniq(mtbl, inner) { return false; }
        self.bump_past::<~u8>();
        true
    }

    fn visit_ptr(&mut self, mtbl: uint, inner: *TyDesc) -> bool {
        self.align_to::<*u8>();
        if ! self.inner().visit_ptr(mtbl, inner) { return false; }
        self.bump_past::<*u8>();
        true
    }

    fn visit_rptr(&mut self, mtbl: uint, inner: *TyDesc) -> bool {
        self.align_to::<&'static u8>();
        if ! self.inner().visit_rptr(mtbl, inner) { return false; }
        self.bump_past::<&'static u8>();
        true
    }

    fn visit_unboxed_vec(&mut self, mtbl: uint, inner: *TyDesc) -> bool {
        self.align_to::<Vec<()>>();
        // FIXME (#3732): Inner really has to move its own pointers on this one.
        // or else possibly we could have some weird interface wherein we
        // read-off a word from inner's pointers, but the read-word has to
        // always be the same in all sub-pointers? Dubious.
        if ! self.inner().visit_vec(mtbl, inner) { return false; }
        true
    }

    fn visit_vec(&mut self, mtbl: uint, inner: *TyDesc) -> bool {
        self.align_to::<~[u8]>();
        if ! self.inner().visit_vec(mtbl, inner) { return false; }
        self.bump_past::<~[u8]>();
        true
    }

    fn visit_evec_box(&mut self, mtbl: uint, inner: *TyDesc) -> bool {
        true
    }

    fn visit_evec_uniq(&mut self, mtbl: uint, inner: *TyDesc) -> bool {
        self.align_to::<~[u8]>();
        if ! self.inner().visit_evec_uniq(mtbl, inner) { return false; }
        self.bump_past::<~[u8]>();
        true
    }

    fn visit_evec_slice(&mut self, mtbl: uint, inner: *TyDesc) -> bool {
        self.align_to::<&'static [u8]>();
        if ! self.inner().visit_evec_slice(mtbl, inner) { return false; }
        self.bump_past::<&'static [u8]>();
        true
    }

    fn visit_evec_fixed(&mut self, n: uint, sz: uint, align: uint,
                        mtbl: uint, inner: *TyDesc) -> bool {
        self.align(align);
        if ! self.inner().visit_evec_fixed(n, sz, align, mtbl, inner) {
            return false;
        }
        self.bump(sz);
        true
    }

    fn visit_enter_rec(&mut self, n_fields: uint, sz: uint, align: uint) -> bool {
        self.align(align);
        if ! self.inner().visit_enter_rec(n_fields, sz, align) { return false; }
        true
    }

    fn visit_rec_field(&mut self, i: uint, name: &str,
                       mtbl: uint, inner: *TyDesc) -> bool {
        if ! self.inner().visit_rec_field(i, name, mtbl, inner) { return false; }
        true
    }

    fn visit_leave_rec(&mut self, n_fields: uint, sz: uint, align: uint) -> bool {
        if ! self.inner().visit_leave_rec(n_fields, sz, align) { return false; }
        true
    }

    fn visit_enter_class(&mut self, name: &str, named_fields: bool, n_fields: uint, sz: uint,
                         align: uint) -> bool {
        self.align(align);
        if ! self.inner().visit_enter_class(name, named_fields, n_fields, sz, align) {
            return false;
        }
        true
    }

    fn visit_class_field(&mut self, i: uint, name: &str, named: bool,
                         mtbl: uint, inner: *TyDesc) -> bool {
        if ! self.inner().visit_class_field(i, name, named, mtbl, inner) {
            return false;
        }
        true
    }

    fn visit_leave_class(&mut self, name: &str, named_fields: bool, n_fields: uint, sz: uint,
                         align: uint) -> bool {
        if ! self.inner().visit_leave_class(name, named_fields, n_fields, sz, align) {
            return false;
        }
        true
    }

    fn visit_enter_tup(&mut self, n_fields: uint, sz: uint, align: uint) -> bool {
        self.align(align);
        if ! self.inner().visit_enter_tup(n_fields, sz, align) { return false; }
        true
    }

    fn visit_tup_field(&mut self, i: uint, inner: *TyDesc) -> bool {
        if ! self.inner().visit_tup_field(i, inner) { return false; }
        true
    }

    fn visit_leave_tup(&mut self, n_fields: uint, sz: uint, align: uint) -> bool {
        if ! self.inner().visit_leave_tup(n_fields, sz, align) { return false; }
        true
    }

    fn visit_enter_fn(&mut self, purity: uint, proto: uint,
                      n_inputs: uint, retstyle: uint) -> bool {
        if ! self.inner().visit_enter_fn(purity, proto, n_inputs, retstyle) {
            return false
        }
        true
    }

    fn visit_fn_input(&mut self, i: uint, mode: uint, inner: *TyDesc) -> bool {
        if ! self.inner().visit_fn_input(i, mode, inner) { return false; }
        true
    }

    fn visit_fn_output(&mut self, retstyle: uint, variadic: bool, inner: *TyDesc) -> bool {
        if ! self.inner().visit_fn_output(retstyle, variadic, inner) { return false; }
        true
    }

    fn visit_leave_fn(&mut self, purity: uint, proto: uint,
                      n_inputs: uint, retstyle: uint) -> bool {
        if ! self.inner().visit_leave_fn(purity, proto, n_inputs, retstyle) {
            return false;
        }
        true
    }

    fn visit_enter_enum(&mut self, n_variants: uint,
                        get_disr: extern unsafe fn(ptr: *Opaque) -> Disr,
                        sz: uint, align: uint)
                     -> bool {
        self.align(align);
        if ! self.inner().visit_enter_enum(n_variants, get_disr, sz, align) { return false; }
        true
    }

    fn visit_enter_enum_variant(&mut self, variant: uint,
                                disr_val: Disr,
                                n_fields: uint,
                                name: &str) -> bool {
        if ! self.inner().visit_enter_enum_variant(variant, disr_val,
                                                 n_fields, name) {
            return false;
        }
        true
    }

    fn visit_enum_variant_field(&mut self, i: uint, offset: uint, inner: *TyDesc) -> bool {
        if ! self.inner().visit_enum_variant_field(i, offset, inner) { return false; }
        true
    }

    fn visit_leave_enum_variant(&mut self, variant: uint,
                                disr_val: Disr,
                                n_fields: uint,
                                name: &str) -> bool {
        if ! self.inner().visit_leave_enum_variant(variant, disr_val,
                                                 n_fields, name) {
            return false;
        }
        true
    }

    fn visit_leave_enum(&mut self, n_variants: uint,
                        get_disr: extern unsafe fn(ptr: *Opaque) -> Disr,
                        sz: uint, align: uint)
                     -> bool {
        if ! self.inner().visit_leave_enum(n_variants, get_disr, sz, align) { return false; }
        true
    }

    fn visit_trait(&mut self, name: &str) -> bool {
        self.align_to::<~TyVisitor>();
        if ! self.inner().visit_trait(name) { return false; }
        self.bump_past::<~TyVisitor>();
        true
    }

    fn visit_param(&mut self, i: uint) -> bool {
        if ! self.inner().visit_param(i) { return false; }
        true
    }

    fn visit_self(&mut self) -> bool {
        self.align_to::<&'static u8>();
        if ! self.inner().visit_self() { return false; }
        self.align_to::<&'static u8>();
        true
    }

    fn visit_type(&mut self) -> bool {
        if ! self.inner().visit_type() { return false; }
        true
    }
}

struct my_visitor(@RefCell<Stuff>);

#[deriving(Clone)]
struct Stuff {
    ptr1: *c_void,
    ptr2: *c_void,
    vals: ~[~str]
}

impl my_visitor {
    pub fn get<T:Clone>(&mut self, f: |T|) {
        unsafe {
            let my_visitor(s) = *self;
            f((*((*s).get().ptr1 as *T)).clone());
        }
    }

    pub fn visit_inner(&mut self, inner: *TyDesc) -> bool {
        unsafe {
            let my_visitor(s) = *self;
            let u = my_visitor(s);
            let mut v = ptr_visit_adaptor::<my_visitor>(Inner {inner: u});
            visit_tydesc(inner, &mut v as &mut TyVisitor);
            true
        }
    }
}

struct Inner<V> { inner: V }

impl movable_ptr for my_visitor {
    fn move_ptr(&mut self, adjustment: |*c_void| -> *c_void) {
        let my_visitor(s) = *self;
        let mut this = s.borrow_mut();
        this.get().ptr1 = adjustment(this.get().ptr1);
        this.get().ptr2 = adjustment(this.get().ptr2);
    }
}

impl TyVisitor for my_visitor {

    fn visit_bot(&mut self) -> bool { true }
    fn visit_nil(&mut self) -> bool { true }
    fn visit_bool(&mut self) -> bool {
        self.get::<bool>(|b| {
            let my_visitor(s) = *self;
            let mut this = s.borrow_mut();
            this.get().vals.push(b.to_str());
        });
        true
    }
    fn visit_int(&mut self) -> bool {
        self.get::<int>(|i| {
            let my_visitor(s) = *self;
            let mut this = s.borrow_mut();
            this.get().vals.push(i.to_str());
        });
        true
    }
    fn visit_i8(&mut self) -> bool { true }
    fn visit_i16(&mut self) -> bool { true }
    fn visit_i32(&mut self) -> bool { true }
    fn visit_i64(&mut self) -> bool { true }

    fn visit_uint(&mut self) -> bool { true }
    fn visit_u8(&mut self) -> bool { true }
    fn visit_u16(&mut self) -> bool { true }
    fn visit_u32(&mut self) -> bool { true }
    fn visit_u64(&mut self) -> bool { true }

    fn visit_f32(&mut self) -> bool { true }
    fn visit_f64(&mut self) -> bool { true }

    fn visit_char(&mut self) -> bool { true }

    fn visit_estr_box(&mut self) -> bool { true }
    fn visit_estr_uniq(&mut self) -> bool { true }
    fn visit_estr_slice(&mut self) -> bool { true }
    fn visit_estr_fixed(&mut self, _n: uint, _sz: uint,
                        _align: uint) -> bool { true }

    fn visit_box(&mut self, _mtbl: uint, _inner: *TyDesc) -> bool { true }
    fn visit_uniq(&mut self, _mtbl: uint, _inner: *TyDesc) -> bool { true }
    fn visit_ptr(&mut self, _mtbl: uint, _inner: *TyDesc) -> bool { true }
    fn visit_rptr(&mut self, _mtbl: uint, _inner: *TyDesc) -> bool { true }

    fn visit_vec(&mut self, _mtbl: uint, _inner: *TyDesc) -> bool { true }
    fn visit_unboxed_vec(&mut self, _mtbl: uint, _inner: *TyDesc) -> bool { true }
    fn visit_evec_box(&mut self, _mtbl: uint, _inner: *TyDesc) -> bool { true }
    fn visit_evec_uniq(&mut self, _mtbl: uint, _inner: *TyDesc) -> bool { true }
    fn visit_evec_slice(&mut self, _mtbl: uint, _inner: *TyDesc) -> bool { true }
    fn visit_evec_fixed(&mut self, _n: uint, _sz: uint, _align: uint,
                        _mtbl: uint, _inner: *TyDesc) -> bool { true }

    fn visit_enter_rec(&mut self, _n_fields: uint,
                       _sz: uint, _align: uint) -> bool { true }
    fn visit_rec_field(&mut self, _i: uint, _name: &str,
                       _mtbl: uint, inner: *TyDesc) -> bool {
        error!("rec field!");
        self.visit_inner(inner)
    }
    fn visit_leave_rec(&mut self, _n_fields: uint,
                       _sz: uint, _align: uint) -> bool { true }

    fn visit_enter_class(&mut self, _name: &str, _named_fields: bool, _n_fields: uint,
                         _sz: uint, _align: uint) -> bool { true }
    fn visit_class_field(&mut self, _i: uint, _name: &str, _named: bool,
                         _mtbl: uint, inner: *TyDesc) -> bool {
        self.visit_inner(inner)
    }
    fn visit_leave_class(&mut self, _name: &str, _named_fields: bool, _n_fields: uint,
                         _sz: uint, _align: uint) -> bool { true }

    fn visit_enter_tup(&mut self, _n_fields: uint,
                       _sz: uint, _align: uint) -> bool { true }
    fn visit_tup_field(&mut self, _i: uint, inner: *TyDesc) -> bool {
        error!("tup field!");
        self.visit_inner(inner)
    }
    fn visit_leave_tup(&mut self, _n_fields: uint,
                       _sz: uint, _align: uint) -> bool { true }

    fn visit_enter_enum(&mut self, _n_variants: uint,
                        _get_disr: extern unsafe fn(ptr: *Opaque) -> Disr,
                        _sz: uint, _align: uint) -> bool {
        // FIXME (#3732): this needs to rewind between enum variants, or something.
        true
    }
    fn visit_enter_enum_variant(&mut self, _variant: uint,
                                _disr_val: Disr,
                                _n_fields: uint,
                                _name: &str) -> bool { true }
    fn visit_enum_variant_field(&mut self, _i: uint, _offset: uint, inner: *TyDesc) -> bool {
        self.visit_inner(inner)
    }
    fn visit_leave_enum_variant(&mut self, _variant: uint,
                                _disr_val: Disr,
                                _n_fields: uint,
                                _name: &str) -> bool { true }
    fn visit_leave_enum(&mut self, _n_variants: uint,
                        _get_disr: extern unsafe fn(ptr: *Opaque) -> Disr,
                        _sz: uint, _align: uint) -> bool { true }

    fn visit_enter_fn(&mut self, _purity: uint, _proto: uint,
                      _n_inputs: uint, _retstyle: uint) -> bool { true }
    fn visit_fn_input(&mut self, _i: uint, _mode: uint, _inner: *TyDesc) -> bool {
        true
    }
    fn visit_fn_output(&mut self, _retstyle: uint, _variadic: bool, _inner: *TyDesc) -> bool {
        true
    }
    fn visit_leave_fn(&mut self, _purity: uint, _proto: uint,
                      _n_inputs: uint, _retstyle: uint) -> bool { true }

    fn visit_trait(&mut self, _name: &str) -> bool { true }
    fn visit_param(&mut self, _i: uint) -> bool { true }
    fn visit_self(&mut self) -> bool { true }
    fn visit_type(&mut self) -> bool { true }
}

fn get_tydesc_for<T>(_t: T) -> *TyDesc {
    unsafe {
        get_tydesc::<T>()
    }
}

struct Triple { x: int, y: int, z: int }

pub fn main() {
    unsafe {
        let r = (1,2,3,true,false, Triple {x:5,y:4,z:3}, (12,));
        let p = ptr::to_unsafe_ptr(&r) as *c_void;
        let u = my_visitor(@RefCell::new(Stuff {ptr1: p,
                                                ptr2: p,
                                                vals: ~[]}));
        let mut v = ptr_visit_adaptor(Inner {inner: u});
        let td = get_tydesc_for(r);
        error!("tydesc sz: {}, align: {}",
               (*td).size, (*td).align);
        visit_tydesc(td, &mut v as &mut TyVisitor);

        let my_visitor(m) = u;
        let mut ub = m.borrow_mut();
        let r = ub.get().vals.clone();
        for s in r.iter() {
            println!("val: {}", *s);
        }
        error!("{:?}", ub.get().vals.clone());
        assert_eq!(ub.get().vals.clone(),
                   ~[ ~"1", ~"2", ~"3", ~"true", ~"false", ~"5", ~"4", ~"3", ~"12"]);
    }
}
<commit_msg>remove antiquated reflect test rather than bring it up to date<commit_after><|endoftext|>


<commit_before>#![allow(dead_code)]

mod varlink_grammar {
    include!(concat!(env!("OUT_DIR"), "/varlink_grammar.rs"));
}

#[cfg(test)]
use self::varlink_grammar::*;

#[test]
fn test_standard() {
    assert!(interfaces("
/**
 * The Varlink Service Interface is added to every varlink service. It provides
 * the Introspect method to be called by a client to retrieve the bootstrap
 * information from a service.
 */
org.varlink.service {
  type Type (name: string, typestring: string)
  type Method (
    name: string,
    monitor: bool,
    type_in: string,
    type_out: string
  )
  type Interface (name: string, types: Type[], methods: Method[])
  type Property (key: string, value: string)
  type InterfaceDescription (
    description: string,
    types: string[],
    methods: string[]
  )

  /**
   * Returns the machine readable information about a service. It contains the service
   * name, all available interfaces with their defined method calls and types.
   */
  Introspect(version: uint64) -> (name: string, interfaces: Interface[])

  /**
   * Returns the human readable description of a service.
   */
  Help() -> (
    description: string,
    properties: Property[],
    interfaces: InterfaceDescription[]
  )
}
")
        .is_ok());
}

#[test]
fn test_one_method() {
    assert!(interfaces("/* comment */ foo.bar{ Foo()->() }").is_ok());
}

#[test]
fn test_one_method_no_type() {
    assert!(interfaces("foo.bar{ Foo()->(b:) }").is_err());
}

#[test]
fn test_domainnames() {
    assert!(interfaces("org.varlink.service {F()->()}").is_ok());
    assert!(interfaces("com.example.0example {F()->()}").is_ok());
    assert!(interfaces("com.example.example-dash {F()->()}").is_ok());
    assert!(interfaces("xn--lgbbat1ad8j.example.algeria {F()->()}").is_ok());
    assert!(interfaces("com.-example.leadinghyphen {F()->()}").is_err());
    assert!(interfaces("com.example-.danglinghyphen- {F()->()}").is_err());
    assert!(interfaces("Com.example.uppercase-toplevel {F()->()}").is_err());
    assert!(interfaces("Co9.exmaple.number-toplevel {F()->()}").is_err());
    assert!(interfaces("com.Example {F()->()}").is_err());
}

#[test]
fn test_no_method() {
    assert!(interfaces("
org.varlink.service {
  type Interface (name: string, types: Type[], methods: Method[])
  type Property (key: string, value: string)
}
")
        .is_err());
}

#[test]
fn test_type_no_args() {
    assert!(interfaces("foo.bar{ type I () F()->() }").is_ok());
}

#[test]
fn test_type_one_arg() {
    assert!(interfaces("foo.bar{ type I (b:bool) F()->() }").is_ok());
}

#[test]
fn test_type_one_array() {
    assert!(interfaces("foo.bar{ type I (b:bool[]) F()->() }").is_ok());
    assert!(interfaces("foo.bar{ type I (b:bool[ ]) F()->() }").is_err());
    assert!(interfaces("foo.bar{ type I (b:bool[1]) F()->() }").is_ok());
    assert!(interfaces("foo.bar{ type I (b:bool[ 1 ]) F()->() }").is_err());
    assert!(interfaces("foo.bar{ type I (b:bool[ 1 1 ]) F()->() }").is_err());
}

// REALLY???
#[test]
fn test_method_struct_optional() {
    assert!(interfaces("foo.bar{ Foo(foo: (i: int64, b: bool)? )->()}").is_ok());
}

// REALLY???
#[test]
fn test_method_struct_array_optional() {
    assert!(interfaces("foo.bar{ Foo(foo: (i: int64, b: bool)[]? )->()}").is_ok());
}

#[test]
fn test_method_struct_array_optional_wrong() {
    assert!(interfaces("foo.bar{ Foo(foo: (i: int64, b: bool)?[]) -> ()}").is_err());
}
<commit_msg>remove comments<commit_after>#![allow(dead_code)]

mod varlink_grammar {
    include!(concat!(env!("OUT_DIR"), "/varlink_grammar.rs"));
}

#[cfg(test)]
use self::varlink_grammar::*;

#[test]
fn test_standard() {
    assert!(interfaces("
/**
 * The Varlink Service Interface is added to every varlink service. It provides
 * the Introspect method to be called by a client to retrieve the bootstrap
 * information from a service.
 */
org.varlink.service {
  type Type (name: string, typestring: string)
  type Method (
    name: string,
    monitor: bool,
    type_in: string,
    type_out: string
  )
  type Interface (name: string, types: Type[], methods: Method[])
  type Property (key: string, value: string)
  type InterfaceDescription (
    description: string,
    types: string[],
    methods: string[]
  )

  /**
   * Returns the machine readable information about a service. It contains the service
   * name, all available interfaces with their defined method calls and types.
   */
  Introspect(version: uint64) -> (name: string, interfaces: Interface[])

  /**
   * Returns the human readable description of a service.
   */
  Help() -> (
    description: string,
    properties: Property[],
    interfaces: InterfaceDescription[]
  )
}
")
        .is_ok());
}

#[test]
fn test_one_method() {
    assert!(interfaces("/* comment */ foo.bar{ Foo()->() }").is_ok());
}

#[test]
fn test_one_method_no_type() {
    assert!(interfaces("foo.bar{ Foo()->(b:) }").is_err());
}

#[test]
fn test_domainnames() {
    assert!(interfaces("org.varlink.service {F()->()}").is_ok());
    assert!(interfaces("com.example.0example {F()->()}").is_ok());
    assert!(interfaces("com.example.example-dash {F()->()}").is_ok());
    assert!(interfaces("xn--lgbbat1ad8j.example.algeria {F()->()}").is_ok());
    assert!(interfaces("com.-example.leadinghyphen {F()->()}").is_err());
    assert!(interfaces("com.example-.danglinghyphen- {F()->()}").is_err());
    assert!(interfaces("Com.example.uppercase-toplevel {F()->()}").is_err());
    assert!(interfaces("Co9.exmaple.number-toplevel {F()->()}").is_err());
    assert!(interfaces("com.Example {F()->()}").is_err());
}

#[test]
fn test_no_method() {
    assert!(interfaces("
org.varlink.service {
  type Interface (name: string, types: Type[], methods: Method[])
  type Property (key: string, value: string)
}
")
        .is_err());
}

#[test]
fn test_type_no_args() {
    assert!(interfaces("foo.bar{ type I () F()->() }").is_ok());
}

#[test]
fn test_type_one_arg() {
    assert!(interfaces("foo.bar{ type I (b:bool) F()->() }").is_ok());
}

#[test]
fn test_type_one_array() {
    assert!(interfaces("foo.bar{ type I (b:bool[]) F()->() }").is_ok());
    assert!(interfaces("foo.bar{ type I (b:bool[ ]) F()->() }").is_err());
    assert!(interfaces("foo.bar{ type I (b:bool[1]) F()->() }").is_ok());
    assert!(interfaces("foo.bar{ type I (b:bool[ 1 ]) F()->() }").is_err());
    assert!(interfaces("foo.bar{ type I (b:bool[ 1 1 ]) F()->() }").is_err());
}

#[test]
fn test_method_struct_optional() {
    assert!(interfaces("foo.bar{ Foo(foo: (i: int64, b: bool)? )->()}").is_ok());
}

#[test]
fn test_method_struct_array_optional() {
    assert!(interfaces("foo.bar{ Foo(foo: (i: int64, b: bool)[]? )->()}").is_ok());
}

#[test]
fn test_method_struct_array_optional_wrong() {
    assert!(interfaces("foo.bar{ Foo(foo: (i: int64, b: bool)?[]) -> ()}").is_err());
}
<|endoftext|>


<commit_before>use std::str;

#[derive(Debug)]
pub enum Message {
    Settings(SettingsValue),
    SetupMap{name: &'static str, value: Vec<u64>},
    PickStartingRegion{time_left: u64, ids: Vec<u64>},
    UpdateMap,
    GoPlaceArmies,
    GoAttackTranser,
}

#[derive(Debug)]
pub enum SettingsValue {
    Timebank(u64),
    TimePerMove(u64),
    MaxRounds(u64),
    YourBot(String),
    OpponentBot(String),
    StartingRegions(Vec<u64>),
    StartingPickAmount(u64),
    StartingArmies(u64)
}

pub fn parse(line: &'static str) -> Result<Message, String> {
    let mut words = line.split(' ');
    match words.next().unwrap() {
        "settings" => parse_settings(words),
        "update_map" => {
            Ok(Message::UpdateMap)
        }
        _ => Err("unknown command".to_string())
    }
}

fn parse_settings(mut parts: str::Split<char>) -> Result<Message, String> {
    let command = try!(parts.next().ok_or("no command given to setting"));

    match command {
        "timebank" => Ok(Message::Settings(SettingsValue::Timebank(try!(parts_to_u64(parts, command.to_owned()))))),
        "time_per_move" => Ok(Message::Settings(SettingsValue::TimePerMove(try!(parts_to_u64(parts, command.to_owned()))))),
        "max_rounds" => Ok(Message::Settings(SettingsValue::MaxRounds(try!(parts_to_u64(parts, command.to_owned()))))),
        "starting_pick_amount" => Ok(Message::Settings(SettingsValue::StartingPickAmount(try!(parts_to_u64(parts, command.to_owned()))))),
        "starting_armies" => Ok(Message::Settings(SettingsValue::StartingArmies(try!(parts_to_u64(parts, command.to_owned()))))),
        "starting_regions" => {
            let mut peeker = parts.peekable();
            try!(peeker.peek().ok_or("got starting_regions setting with no value"));
            let mut value = Vec::new();
            for word in peeker {
                value.push(try!(u64::from_str_radix(word, 10).map_err(|e| e.to_string())));
            }
            Ok(Message::Settings(SettingsValue::StartingRegions(value)))
        }
        "your_bot" => {
            let raw_value = try!(parts.next().ok_or("got your_bot setting with no value"));
            Ok(Message::Settings(SettingsValue::YourBot(raw_value.to_owned())))
        }
        "opponent_bot" => {
            let raw_value = try!(parts.next().ok_or("got opponent_bot setting with no value"));
            Ok(Message::Settings(SettingsValue::OpponentBot(raw_value.to_owned())))
        }
        _ => Err(format!("unknown setting {}", command).to_string())
    }
}

fn parts_to_u64(mut parts: str::Split<char>, command: String) -> Result<u64, String> {
    let raw_value = try!(parts.next().ok_or(format!("got {} setting with no value", command)));
    Ok(try!(u64::from_str_radix(raw_value, 10).map_err(|e| e.to_string())))
}

#[test]
fn blank() {
    match parse("") {
        Ok(_) => panic!("should return error because it is malformed"),
        Err(e) => assert_eq!(e, "unknown command".to_string())
    }
}

#[test]
fn setting_blank() {
    match parse("settings") {
        Ok(_) => panic!("should return error because it is malformed"),
        Err(e) => assert_eq!(e, "no command given to setting".to_string())
    }
}

#[test]
fn setting_timebank_blank() {
    match parse("settings timebank") {
        Ok(_) => panic!("got a setting back when expecting an error"),
        Err(e) => assert_eq!(e, "got timebank setting with no value".to_string())
    }
}

#[test]
fn setting_timebank_bad_value() {
    match parse("settings timebank five") {
        Ok(_) => panic!("got a setting back when expecting an error"),
        Err(e) => assert_eq!(e, "invalid digit found in string".to_string())
    }
}

#[test]
fn setting_timebank_proper() {
    match parse("settings timebank 10000").unwrap() {
        Message::Settings(setting) => match setting {
            SettingsValue::Timebank(value) => assert_eq!(value, 10000),
            _ => panic!("got a setting that wasn't a timebank")
        },
        _ => panic!("didn't get a settings object")
    }
}

#[test]
fn setting_time_per_move_blank() {
    match parse("settings time_per_move") {
        Ok(_) => panic!("got a setting back when expecting an error"),
        Err(e) => assert_eq!(e, "got time_per_move setting with no value".to_string())
    }
}

#[test]
fn setting_time_per_move_bad_value() {
    match parse("settings time_per_move five") {
        Ok(_) => panic!("got a setting back when expecting an error"),
        Err(e) => assert_eq!(e, "invalid digit found in string".to_string())
    }
}

#[test]
fn setting_time_per_move_proper() {
    match parse("settings time_per_move 10000").unwrap() {
        Message::Settings(setting) => match setting {
            SettingsValue::TimePerMove(value) => assert_eq!(value, 10000),
            _ => panic!("got a setting that wasn't a time_per_move")
        },
        _ => panic!("didn't get a settings object")
    }
}

#[test]
fn setting_max_rounds_blank() {
    match parse("settings max_rounds") {
        Ok(_) => panic!("got a setting back when expecting an error"),
        Err(e) => assert_eq!(e, "got max_rounds setting with no value".to_string())
    }
}

#[test]
fn setting_max_rounds_bad_value() {
    match parse("settings max_rounds five") {
        Ok(_) => panic!("got a setting back when expecting an error"),
        Err(e) => assert_eq!(e, "invalid digit found in string".to_string())
    }
}

#[test]
fn setting_max_rounds_proper() {
    match parse("settings max_rounds 10000").unwrap() {
        Message::Settings(setting) => match setting {
            SettingsValue::MaxRounds(value) => assert_eq!(value, 10000),
            _ => panic!("got a setting that wasn't a max_rounds")
        },
        _ => panic!("didn't get a settings object")
    }
}

#[test]
fn setting_starting_pick_amount_blank() {
    match parse("settings starting_pick_amount") {
        Ok(_) => panic!("got a setting back when expecting an error"),
        Err(e) => assert_eq!(e, "got starting_pick_amount setting with no value".to_string())
    }
}

#[test]
fn setting_starting_pick_amount_bad_value() {
    match parse("settings starting_pick_amount five") {
        Ok(_) => panic!("got a setting back when expecting an error"),
        Err(e) => assert_eq!(e, "invalid digit found in string".to_string())
    }
}

#[test]
fn setting_starting_pick_amount_proper() {
    match parse("settings starting_pick_amount 10000").unwrap() {
        Message::Settings(setting) => match setting {
            SettingsValue::StartingPickAmount(value) => assert_eq!(value, 10000),
            _ => panic!("got a setting that wasn't a starting_pick_amount")
        },
        _ => panic!("didn't get a settings object")
    }
}

#[test]
fn setting_starting_armies_blank() {
    match parse("settings starting_armies") {
        Ok(_) => panic!("got a setting back when expecting an error"),
        Err(e) => assert_eq!(e, "got starting_armies setting with no value".to_string())
    }
}

#[test]
fn setting_starting_armies_bad_value() {
    match parse("settings starting_armies five") {
        Ok(_) => panic!("got a setting back when expecting an error"),
        Err(e) => assert_eq!(e, "invalid digit found in string".to_string())
    }
}

#[test]
fn setting_starting_armies_proper() {
    match parse("settings starting_armies 10000").unwrap() {
        Message::Settings(setting) => match setting {
            SettingsValue::StartingArmies(value) => assert_eq!(value, 10000),
            _ => panic!("got a setting that wasn't a starting_armies")
        },
        _ => panic!("didn't get a settings object")
    }
}

#[test]
fn setting_starting_regions_blank() {
    match parse("settings starting_regions") {
        Ok(_) => panic!("got a setting back when expecting an error"),
        Err(e) => assert_eq!(e, "got starting_regions setting with no value".to_string())
    }
}

#[test]
fn setting_starting_regions_bad_value() {
    match parse("settings starting_regions five 50") {
        Ok(a) => panic!("got a setting back when expecting an error, {:?}", a),
        Err(e) => assert_eq!(e, "invalid digit found in string".to_string())
    }
}

#[test]
fn setting_starting_regions_proper() {
    match parse("settings starting_regions 4 7 11").unwrap() {
        Message::Settings(setting) => match setting {
            SettingsValue::StartingRegions(value) => assert_eq!(value, vec![4, 7, 11]),
            _ => panic!("got a setting that wasn't a starting_regions")
        },
        _ => panic!("didn't get a settings object")
    }
}

#[test]
fn setting_your_bot_blank() {
    match parse("settings your_bot") {
        Ok(a) => panic!("got a setting back when expecting an error, {:?}", a),
        Err(e) => assert_eq!(e, "got your_bot setting with no value".to_string())
    }
}

#[test]
fn setting_your_bot_proper() {
    match parse("settings your_bot fig").unwrap() {
        Message::Settings(setting) => match setting {
            SettingsValue::YourBot(value) => assert_eq!(value, "fig".to_string()),
            _ => panic!("got a setting that wasn't a your_bot")
        },
        _ => panic!("didn't get a settings object")
    }
}

#[test]
fn setting_opponent_bot_blank() {
    match parse("settings opponent_bot") {
        Ok(a) => panic!("got a setting back when expecting an error, {:?}", a),
        Err(e) => assert_eq!(e, "got opponent_bot setting with no value".to_string())
    }
}

#[test]
fn setting_opponent_bot_proper() {
    match parse("settings opponent_bot fig").unwrap() {
        Message::Settings(setting) => match setting {
            SettingsValue::OpponentBot(value) => assert_eq!(value, "fig".to_string()),
            _ => panic!("got a setting that wasn't a opponent_bot")
        },
        _ => panic!("didn't get a settings object")
    }
}
<commit_msg>use .to_owned() where I can<commit_after>use std::str;

#[derive(Debug)]
pub enum Message {
    Settings(SettingsValue),
    SetupMap{name: &'static str, value: Vec<u64>},
    PickStartingRegion{time_left: u64, ids: Vec<u64>},
    UpdateMap,
    GoPlaceArmies,
    GoAttackTranser,
}

#[derive(Debug)]
pub enum SettingsValue {
    Timebank(u64),
    TimePerMove(u64),
    MaxRounds(u64),
    YourBot(String),
    OpponentBot(String),
    StartingRegions(Vec<u64>),
    StartingPickAmount(u64),
    StartingArmies(u64)
}

pub fn parse(line: &'static str) -> Result<Message, String> {
    let mut words = line.split(' ');
    match words.next().unwrap() {
        "settings" => parse_settings(words),
        "update_map" => {
            Ok(Message::UpdateMap)
        }
        _ => Err("unknown command".to_owned())
    }
}

fn parse_settings(mut parts: str::Split<char>) -> Result<Message, String> {
    let command = try!(parts.next().ok_or("no command given to setting"));

    match command {
        "timebank" => Ok(Message::Settings(SettingsValue::Timebank(try!(parts_to_u64(parts, command.to_owned()))))),
        "time_per_move" => Ok(Message::Settings(SettingsValue::TimePerMove(try!(parts_to_u64(parts, command.to_owned()))))),
        "max_rounds" => Ok(Message::Settings(SettingsValue::MaxRounds(try!(parts_to_u64(parts, command.to_owned()))))),
        "starting_pick_amount" => Ok(Message::Settings(SettingsValue::StartingPickAmount(try!(parts_to_u64(parts, command.to_owned()))))),
        "starting_armies" => Ok(Message::Settings(SettingsValue::StartingArmies(try!(parts_to_u64(parts, command.to_owned()))))),
        "starting_regions" => {
            let mut peeker = parts.peekable();
            try!(peeker.peek().ok_or("got starting_regions setting with no value"));
            let mut value = Vec::new();
            for word in peeker {
                value.push(try!(u64::from_str_radix(word, 10).map_err(|e| e.to_string())));
            }
            Ok(Message::Settings(SettingsValue::StartingRegions(value)))
        }
        "your_bot" => {
            let raw_value = try!(parts.next().ok_or("got your_bot setting with no value"));
            Ok(Message::Settings(SettingsValue::YourBot(raw_value.to_owned())))
        }
        "opponent_bot" => {
            let raw_value = try!(parts.next().ok_or("got opponent_bot setting with no value"));
            Ok(Message::Settings(SettingsValue::OpponentBot(raw_value.to_owned())))
        }
        _ => Err(format!("unknown setting {}", command).to_owned())
    }
}

fn parts_to_u64(mut parts: str::Split<char>, command: String) -> Result<u64, String> {
    let raw_value = try!(parts.next().ok_or(format!("got {} setting with no value", command)));
    Ok(try!(u64::from_str_radix(raw_value, 10).map_err(|e| e.to_string())))
}

#[test]
fn blank() {
    match parse("") {
        Ok(_) => panic!("should return error because it is malformed"),
        Err(e) => assert_eq!(e, "unknown command".to_owned())
    }
}

#[test]
fn setting_blank() {
    match parse("settings") {
        Ok(_) => panic!("should return error because it is malformed"),
        Err(e) => assert_eq!(e, "no command given to setting".to_owned())
    }
}

#[test]
fn setting_timebank_blank() {
    match parse("settings timebank") {
        Ok(_) => panic!("got a setting back when expecting an error"),
        Err(e) => assert_eq!(e, "got timebank setting with no value".to_owned())
    }
}

#[test]
fn setting_timebank_bad_value() {
    match parse("settings timebank five") {
        Ok(_) => panic!("got a setting back when expecting an error"),
        Err(e) => assert_eq!(e, "invalid digit found in string".to_owned())
    }
}

#[test]
fn setting_timebank_proper() {
    match parse("settings timebank 10000").unwrap() {
        Message::Settings(setting) => match setting {
            SettingsValue::Timebank(value) => assert_eq!(value, 10000),
            _ => panic!("got a setting that wasn't a timebank")
        },
        _ => panic!("didn't get a settings object")
    }
}

#[test]
fn setting_time_per_move_blank() {
    match parse("settings time_per_move") {
        Ok(_) => panic!("got a setting back when expecting an error"),
        Err(e) => assert_eq!(e, "got time_per_move setting with no value".to_owned())
    }
}

#[test]
fn setting_time_per_move_bad_value() {
    match parse("settings time_per_move five") {
        Ok(_) => panic!("got a setting back when expecting an error"),
        Err(e) => assert_eq!(e, "invalid digit found in string".to_owned())
    }
}

#[test]
fn setting_time_per_move_proper() {
    match parse("settings time_per_move 10000").unwrap() {
        Message::Settings(setting) => match setting {
            SettingsValue::TimePerMove(value) => assert_eq!(value, 10000),
            _ => panic!("got a setting that wasn't a time_per_move")
        },
        _ => panic!("didn't get a settings object")
    }
}

#[test]
fn setting_max_rounds_blank() {
    match parse("settings max_rounds") {
        Ok(_) => panic!("got a setting back when expecting an error"),
        Err(e) => assert_eq!(e, "got max_rounds setting with no value".to_owned())
    }
}

#[test]
fn setting_max_rounds_bad_value() {
    match parse("settings max_rounds five") {
        Ok(_) => panic!("got a setting back when expecting an error"),
        Err(e) => assert_eq!(e, "invalid digit found in string".to_owned())
    }
}

#[test]
fn setting_max_rounds_proper() {
    match parse("settings max_rounds 10000").unwrap() {
        Message::Settings(setting) => match setting {
            SettingsValue::MaxRounds(value) => assert_eq!(value, 10000),
            _ => panic!("got a setting that wasn't a max_rounds")
        },
        _ => panic!("didn't get a settings object")
    }
}

#[test]
fn setting_starting_pick_amount_blank() {
    match parse("settings starting_pick_amount") {
        Ok(_) => panic!("got a setting back when expecting an error"),
        Err(e) => assert_eq!(e, "got starting_pick_amount setting with no value".to_owned())
    }
}

#[test]
fn setting_starting_pick_amount_bad_value() {
    match parse("settings starting_pick_amount five") {
        Ok(_) => panic!("got a setting back when expecting an error"),
        Err(e) => assert_eq!(e, "invalid digit found in string".to_owned())
    }
}

#[test]
fn setting_starting_pick_amount_proper() {
    match parse("settings starting_pick_amount 10000").unwrap() {
        Message::Settings(setting) => match setting {
            SettingsValue::StartingPickAmount(value) => assert_eq!(value, 10000),
            _ => panic!("got a setting that wasn't a starting_pick_amount")
        },
        _ => panic!("didn't get a settings object")
    }
}

#[test]
fn setting_starting_armies_blank() {
    match parse("settings starting_armies") {
        Ok(_) => panic!("got a setting back when expecting an error"),
        Err(e) => assert_eq!(e, "got starting_armies setting with no value".to_owned())
    }
}

#[test]
fn setting_starting_armies_bad_value() {
    match parse("settings starting_armies five") {
        Ok(_) => panic!("got a setting back when expecting an error"),
        Err(e) => assert_eq!(e, "invalid digit found in string".to_owned())
    }
}

#[test]
fn setting_starting_armies_proper() {
    match parse("settings starting_armies 10000").unwrap() {
        Message::Settings(setting) => match setting {
            SettingsValue::StartingArmies(value) => assert_eq!(value, 10000),
            _ => panic!("got a setting that wasn't a starting_armies")
        },
        _ => panic!("didn't get a settings object")
    }
}

#[test]
fn setting_starting_regions_blank() {
    match parse("settings starting_regions") {
        Ok(_) => panic!("got a setting back when expecting an error"),
        Err(e) => assert_eq!(e, "got starting_regions setting with no value".to_owned())
    }
}

#[test]
fn setting_starting_regions_bad_value() {
    match parse("settings starting_regions five 50") {
        Ok(a) => panic!("got a setting back when expecting an error, {:?}", a),
        Err(e) => assert_eq!(e, "invalid digit found in string".to_owned())
    }
}

#[test]
fn setting_starting_regions_proper() {
    match parse("settings starting_regions 4 7 11").unwrap() {
        Message::Settings(setting) => match setting {
            SettingsValue::StartingRegions(value) => assert_eq!(value, vec![4, 7, 11]),
            _ => panic!("got a setting that wasn't a starting_regions")
        },
        _ => panic!("didn't get a settings object")
    }
}

#[test]
fn setting_your_bot_blank() {
    match parse("settings your_bot") {
        Ok(a) => panic!("got a setting back when expecting an error, {:?}", a),
        Err(e) => assert_eq!(e, "got your_bot setting with no value".to_owned())
    }
}

#[test]
fn setting_your_bot_proper() {
    match parse("settings your_bot fig").unwrap() {
        Message::Settings(setting) => match setting {
            SettingsValue::YourBot(value) => assert_eq!(value, "fig".to_owned()),
            _ => panic!("got a setting that wasn't a your_bot")
        },
        _ => panic!("didn't get a settings object")
    }
}

#[test]
fn setting_opponent_bot_blank() {
    match parse("settings opponent_bot") {
        Ok(a) => panic!("got a setting back when expecting an error, {:?}", a),
        Err(e) => assert_eq!(e, "got opponent_bot setting with no value".to_owned())
    }
}

#[test]
fn setting_opponent_bot_proper() {
    match parse("settings opponent_bot fig").unwrap() {
        Message::Settings(setting) => match setting {
            SettingsValue::OpponentBot(value) => assert_eq!(value, "fig".to_owned()),
            _ => panic!("got a setting that wasn't a opponent_bot")
        },
        _ => panic!("didn't get a settings object")
    }
}
<|endoftext|>


<commit_before>use nom::IResult::*;
use nom::*;

#[derive(Debug)]
pub struct Address<'a> {
    pub addr_name: &'a [u8],
    pub addr_adl: &'a [u8],
    pub addr_mailbox: &'a [u8],
    pub addr_host: &'a [u8],
}

pub struct Envelope<'a> {
    pub date: &'a [u8],
    pub subject: &'a [u8],
    pub from: Option<Vec<Address<'a>>>,
    pub senter: Option<Vec<Address<'a>>>,
    pub reply_to: Option<Vec<Address<'a>>>,
    pub to: Option<Vec<Address<'a>>>,
    pub cc: Option<Vec<Address<'a>>>,
    pub bcc: Option<Vec<Address<'a>>>,
    pub in_reply_to: &'a [u8],
    pub message_id: &'a [u8],
}

named!(pub nil,
    alt!(
        tag!(b"NIL")
        | tag!(b"nil")
    )
);

named!(pub resp_specials, tag!("]"));

named!(pub quoted_specials,
    alt!(
        tag!("\"")
        | tag!("\\")
    )
);

named!(pub list_wildcards,
    alt!(
        tag!("%")
        | tag!("*")
    )
);

named!(pub atom_specials,
    alt!(
        tag!("(")
        | tag!(")")
        | tag!("{")
        | tag!(" ")
        | tag!("(")
        | list_wildcards
        | resp_specials
        | quoted_specials
    )
);

pub fn atom_char(bytes: &[u8]) -> IResult<&[u8], &[u8]> {
    cond_reduce!(bytes,
                 !atom_specials(bytes).is_done(),
                 chain!(
        alpha: peek!(alphanumeric) ~
        _____: take!(1) ,
        || { &alpha[..1] }
    ))
}

pub fn atom(bytes: &[u8]) -> IResult<&[u8], &[u8]> {
    let mut offset = 0;
    loop {
        if atom_char(&bytes[offset..]).is_done() {
            offset += 1;
        } else {
            break;
        }
    }
    take!(bytes, offset)
}

named!(pub nstring,
       alt!(
           delimited!(
               char!('"'),
               is_not!("\""),
               char!('"')
           )
           | nil
       )
   );

named!(pub astring_char,
   alt!(
       atom_char
       | resp_specials
   )
);

named!(many_addr_or_nil <&[u8], Option<Vec<Address> > >,
    alt!(
        chain!(
            char!('(') ~
            addresses: many1!(address) ~
            char!(')'),
            || {
                Some(addresses)
            }
        )
        | chain!(
            nil,
            || {
                None
            }
        )
    )
);

named!(pub env_bcc <&[u8], Option<Vec<Address> > >,
    chain!(
        maon: many_addr_or_nil,
        || {
            maon
        })
);

named!(pub env_cc <&[u8], Option<Vec<Address> > >,
    chain!(
        maon: many_addr_or_nil,
        || {
            maon
        })
);

named!(pub env_from <&[u8], Option<Vec<Address> > >,
    chain!(
        maon: many_addr_or_nil,
        || {
            maon
        })
);

named!(pub env_date,
    chain!(
        ns: nstring,
        || {
            ns
        })
);

named!(pub env_in_reply_to,
    chain!(
        ns: nstring,
        || {
            ns
        })
);

named!(pub env_message_id,
    chain!(
        ns: nstring,
        || {
            ns
        })
);

named!(pub env_reply_to <&[u8], Option<Vec<Address> > >,
    chain!(
        maon: many_addr_or_nil,
        || {
            maon
        })
);

named!(pub env_sender <&[u8], Option<Vec<Address> > >,
    chain!(
        maon: many_addr_or_nil,
        || {
            maon
        })
);

named!(pub env_subject,
    chain!(
        ns: nstring,
        || {
            ns
        })
);

named!(pub env_to <&[u8], Option<Vec<Address> > >,
    chain!(
        maon: many_addr_or_nil,
        || {
            maon
        })
);

named!(pub address <&[u8], Address>,
    chain!(
        char!('(') ~
        addr_name: nstring ~
        char!(' ') ~
        addr_adl: nstring ~
        char!(' ') ~
        addr_mailbox: nstring ~
        char!(' ') ~
        addr_host: nstring ~
        char!(')'),
        || {
            Address {
                addr_name: addr_name,
                addr_adl: addr_adl,
                addr_mailbox: addr_mailbox,
                addr_host: addr_host,
            }
        }
    )
);

named!(pub envelope <&[u8], Envelope>,
    chain!(
        char!('(') ~
        date: env_date ~
        char!(' ') ~
        subject: env_subject ~
        char!(' ') ~
        from: env_from ~
        char!(' ') ~
        senter: env_sender ~
        char!(' ') ~
        reply_to: env_reply_to ~
        char!(' ') ~
        to: env_to ~
        char!(' ') ~
        cc: env_cc ~
        char!(' ') ~
        bcc: env_bcc ~
        char!(' ') ~
        in_reply_to: env_in_reply_to ~
        char!(' ') ~
        message_id: env_message_id ~

        char!(')'),
        || {
            Envelope {

                date: date,
                subject: subject,
                from: from,
                senter: senter,
                reply_to: reply_to,
                to: to,
                cc: cc,
                bcc: bcc,
                in_reply_to: in_reply_to,
                message_id: message_id,

            }
        }
    )
);

#[cfg(test)]
mod tests {
    use nom::IResult::*;
    use nom::*;
    use super::*;
    use std::str;

    #[test]
    fn test_address_parse() {
        let addr = b"(\"name\" \"adl\" \"mailbox\" \"host\")";
        match address(&addr[..]) {
            Done(remaining, addr) => {
                assert_eq!(remaining.len(), 0);
                assert_eq!(addr.addr_name, b"name");
                assert_eq!(addr.addr_adl, b"adl");
                assert_eq!(addr.addr_mailbox, b"mailbox");
                assert_eq!(addr.addr_host, b"host");
            }
            _ => panic!(),
        }
    }

    #[test]
    fn test_atom() {
        let s = b"abcd";

        match atom(&s[..]) {
            Done(remaining, atom) => {
                assert_eq!(atom, s);
            }
            _ => panic!("NOT DONE"),
        }
    }
}
<commit_msg>Make Envelope impl Debug<commit_after>use nom::IResult::*;
use nom::*;

#[derive(Debug)]
pub struct Address<'a> {
    pub addr_name: &'a [u8],
    pub addr_adl: &'a [u8],
    pub addr_mailbox: &'a [u8],
    pub addr_host: &'a [u8],
}

#[derive(Debug)]
pub struct Envelope<'a> {
    pub date: &'a [u8],
    pub subject: &'a [u8],
    pub from: Option<Vec<Address<'a>>>,
    pub senter: Option<Vec<Address<'a>>>,
    pub reply_to: Option<Vec<Address<'a>>>,
    pub to: Option<Vec<Address<'a>>>,
    pub cc: Option<Vec<Address<'a>>>,
    pub bcc: Option<Vec<Address<'a>>>,
    pub in_reply_to: &'a [u8],
    pub message_id: &'a [u8],
}

named!(pub nil,
    alt!(
        tag!(b"NIL")
        | tag!(b"nil")
    )
);

named!(pub resp_specials, tag!("]"));

named!(pub quoted_specials,
    alt!(
        tag!("\"")
        | tag!("\\")
    )
);

named!(pub list_wildcards,
    alt!(
        tag!("%")
        | tag!("*")
    )
);

named!(pub atom_specials,
    alt!(
        tag!("(")
        | tag!(")")
        | tag!("{")
        | tag!(" ")
        | tag!("(")
        | list_wildcards
        | resp_specials
        | quoted_specials
    )
);

pub fn atom_char(bytes: &[u8]) -> IResult<&[u8], &[u8]> {
    cond_reduce!(bytes,
                 !atom_specials(bytes).is_done(),
                 chain!(
        alpha: peek!(alphanumeric) ~
        _____: take!(1) ,
        || { &alpha[..1] }
    ))
}

pub fn atom(bytes: &[u8]) -> IResult<&[u8], &[u8]> {
    let mut offset = 0;
    loop {
        if atom_char(&bytes[offset..]).is_done() {
            offset += 1;
        } else {
            break;
        }
    }
    take!(bytes, offset)
}

named!(pub nstring,
       alt!(
           delimited!(
               char!('"'),
               is_not!("\""),
               char!('"')
           )
           | nil
       )
   );

named!(pub astring_char,
   alt!(
       atom_char
       | resp_specials
   )
);

#[inline_always]
named!(many_addr_or_nil <&[u8], Option<Vec<Address> > >,
    alt!(
        chain!(
            char!('(') ~
            addresses: many1!(address) ~
            char!(')'),
            || {
                Some(addresses)
            }
        )
        | chain!(
            nil,
            || {
                None
            }
        )
    )
);

named!(pub env_bcc <&[u8], Option<Vec<Address> > >,
    chain!(
        maon: many_addr_or_nil,
        || {
            maon
        })
);

named!(pub env_cc <&[u8], Option<Vec<Address> > >,
    chain!(
        maon: many_addr_or_nil,
        || {
            maon
        })
);

named!(pub env_from <&[u8], Option<Vec<Address> > >,
    chain!(
        maon: many_addr_or_nil,
        || {
            maon
        })
);

named!(pub env_date,
    chain!(
        ns: nstring,
        || {
            ns
        })
);

named!(pub env_in_reply_to,
    chain!(
        ns: nstring,
        || {
            ns
        })
);

named!(pub env_message_id,
    chain!(
        ns: nstring,
        || {
            ns
        })
);

named!(pub env_reply_to <&[u8], Option<Vec<Address> > >,
    chain!(
        maon: many_addr_or_nil,
        || {
            maon
        })
);

named!(pub env_sender <&[u8], Option<Vec<Address> > >,
    chain!(
        maon: many_addr_or_nil,
        || {
            maon
        })
);

named!(pub env_subject,
    chain!(
        ns: nstring,
        || {
            ns
        })
);

named!(pub env_to <&[u8], Option<Vec<Address> > >,
    chain!(
        maon: many_addr_or_nil,
        || {
            maon
        })
);

named!(pub address <&[u8], Address>,
    chain!(
        char!('(') ~
        addr_name: nstring ~
        char!(' ') ~
        addr_adl: nstring ~
        char!(' ') ~
        addr_mailbox: nstring ~
        char!(' ') ~
        addr_host: nstring ~
        char!(')'),
        || {
            Address {
                addr_name: addr_name,
                addr_adl: addr_adl,
                addr_mailbox: addr_mailbox,
                addr_host: addr_host,
            }
        }
    )
);

named!(pub envelope <&[u8], Envelope>,
    chain!(
        char!('(') ~
        date: env_date ~
        char!(' ') ~
        subject: env_subject ~
        char!(' ') ~
        from: env_from ~
        char!(' ') ~
        senter: env_sender ~
        char!(' ') ~
        reply_to: env_reply_to ~
        char!(' ') ~
        to: env_to ~
        char!(' ') ~
        cc: env_cc ~
        char!(' ') ~
        bcc: env_bcc ~
        char!(' ') ~
        in_reply_to: env_in_reply_to ~
        char!(' ') ~
        message_id: env_message_id ~

        char!(')'),
        || {
            Envelope {
                date: date,
                subject: subject,
                from: from,
                senter: senter,
                reply_to: reply_to,
                to: to,
                cc: cc,
                bcc: bcc,
                in_reply_to: in_reply_to,
                message_id: message_id,
            }
        }
    )
);

#[cfg(test)]
mod tests {
    use nom::IResult::*;
    use nom::*;
    use super::*;
    use std::str;

    #[test]
    fn test_address_parse() {
        let addr = b"(\"name\" \"adl\" \"mailbox\" \"host\")";
        match address(&addr[..]) {
            Done(remaining, addr) => {
                assert_eq!(remaining.len(), 0);
                assert_eq!(addr.addr_name, b"name");
                assert_eq!(addr.addr_adl, b"adl");
                assert_eq!(addr.addr_mailbox, b"mailbox");
                assert_eq!(addr.addr_host, b"host");
            }
            _ => panic!(),
        }
    }

    #[test]
    fn test_atom() {
        let s = b"abcd";

        match atom(&s[..]) {
            Done(remaining, atom) => {
                assert_eq!(atom, s);
            }
            _ => panic!("NOT DONE"),
        }
    }
}
<|endoftext|>


<commit_before>use nalgebra::na;
use nalgebra::na::{Cast, Indexable, FloatVecExt, Vec3, Vec2};
use procedural::{TriMesh, SplitIndexBuffer, Polyline};

/**
 * Generates a cuboid geometry with a split index buffer.
 *
 * # Arguments:
 * * `extents` - the extents of the cuboid.
 */
pub fn cuboid<N: Float + Clone + Cast<f64>>(extents: &Vec3<N>) -> TriMesh<N, Vec3<N>> {
    let mut cuboid = unit_cuboid();

    cuboid.scale_by(extents);

    cuboid
}

/**
 * Generates a cuboid geometry with a split index buffer.
 *
 * The cuboid is centered at the origin, and has its half extents set to 0.5.
 */
pub fn unit_cuboid<N: Float + Clone + Cast<f64>>() -> TriMesh<N, Vec3<N>> {
    let mut coords  = Vec::with_capacity(8);
    let mut uvs     = Vec::with_capacity(4);
    let mut normals = Vec::with_capacity(6);
    let mut faces   = Vec::with_capacity(12);

    let _0_5: N = na::cast(0.5);
    let m0_5: N = -_0_5;
    let _1:   N = na::one();
    let m1:   N = -_1;
    let _0:   N = na::zero();

    coords.push(Vec3::new(m0_5.clone(), m0_5.clone(), _0_5.clone()));
    coords.push(Vec3::new(m0_5.clone(), m0_5.clone(), m0_5.clone()));
    coords.push(Vec3::new(_0_5.clone(), m0_5.clone(), m0_5.clone()));
    coords.push(Vec3::new(_0_5.clone(), m0_5.clone(), _0_5.clone()));
    coords.push(Vec3::new(m0_5.clone(), _0_5.clone(), _0_5.clone()));
    coords.push(Vec3::new(m0_5.clone(), _0_5.clone(), m0_5.clone()));
    coords.push(Vec3::new(_0_5.clone(), _0_5.clone(), m0_5.clone()));
    coords.push(Vec3::new(_0_5.clone(), _0_5.clone(), _0_5.clone()));

    uvs.push(Vec2::new(_0.clone(), _0.clone()));
    uvs.push(Vec2::new(_1.clone(), _0.clone()));
    uvs.push(Vec2::new(_0.clone(), _1.clone()));
    uvs.push(Vec2::new(_1.clone(), _1.clone()));

    normals.push(Vec3::new(m1.clone(), _0.clone(), _0.clone()));
    normals.push(Vec3::new(_0.clone(), _0.clone(), m1.clone()));
    normals.push(Vec3::new(_1.clone(), _0.clone(), _0.clone()));
    normals.push(Vec3::new(_0.clone(), _0.clone(), _1.clone()));
    normals.push(Vec3::new(_0.clone(), m1.clone(), _0.clone()));
    normals.push(Vec3::new(_0.clone(), _1.clone(), _0.clone()));

    faces.push(Vec3::new(Vec3::new(4, 0, 0), Vec3::new(5, 1, 0), Vec3::new(0, 2, 0)));
    faces.push(Vec3::new(Vec3::new(5, 0, 1), Vec3::new(6, 1, 1), Vec3::new(1, 2, 1)));
    faces.push(Vec3::new(Vec3::new(6, 1, 2), Vec3::new(7, 0, 2), Vec3::new(3, 2, 2)));
    faces.push(Vec3::new(Vec3::new(7, 1, 3), Vec3::new(4, 0, 3), Vec3::new(0, 2, 3)));
    faces.push(Vec3::new(Vec3::new(0, 2, 4), Vec3::new(1, 0, 4), Vec3::new(2, 1, 4)));
    faces.push(Vec3::new(Vec3::new(7, 3, 5), Vec3::new(6, 1, 5), Vec3::new(5, 0, 5)));
    faces.push(Vec3::new(Vec3::new(5, 1, 0), Vec3::new(1, 3, 0), Vec3::new(0, 2, 0)));
    faces.push(Vec3::new(Vec3::new(6, 1, 1), Vec3::new(2, 3, 1), Vec3::new(1, 2, 1)));
    faces.push(Vec3::new(Vec3::new(2, 3, 2), Vec3::new(6, 1, 2), Vec3::new(3, 2, 2)));
    faces.push(Vec3::new(Vec3::new(3, 3, 3), Vec3::new(7, 1, 3), Vec3::new(0, 2, 3)));
    faces.push(Vec3::new(Vec3::new(3, 3, 4), Vec3::new(0, 2, 4), Vec3::new(2, 1, 4)));
    faces.push(Vec3::new(Vec3::new(4, 2, 5), Vec3::new(7, 3, 5), Vec3::new(5, 0, 5)));

    TriMesh::new(coords, Some(normals), Some(uvs), Some(SplitIndexBuffer(faces)))
}

/// The contour of a cuboid lying on the x-y plane.
pub fn rectangle<N: Float + Clone + Cast<f64>, V: FloatVecExt<N>>(extents: &V) -> Polyline<N, V> {
    let mut rectangle = unit_rectangle();

    rectangle.scale_by(extents);

    rectangle
}

/// The contour of a unit cuboid lying on the x-y plane.
pub fn unit_rectangle<N: Float + Clone + Cast<f64>, V: FloatVecExt<N>>() -> Polyline<N, V> {
    let _0_5: N = na::cast(0.5);
    let m0_5: N = -_0_5;

    let mut p_ul = na::zero::<V>();
    let mut p_ur = na::zero::<V>();
    let mut p_dl = na::zero::<V>();
    let mut p_dr = na::zero::<V>();

    p_ul.set(0, m0_5.clone()); p_ul.set(1, _0_5.clone());
    p_ur.set(0, _0_5.clone()); p_ur.set(1, _0_5.clone());
    p_dl.set(0, _0_5.clone()); p_dl.set(1, m0_5.clone());
    p_dr.set(0, m0_5.clone()); p_dr.set(1, m0_5.clone());

    Polyline::new(vec!(p_ur, p_ul, p_dl, p_dr), None)
}
<commit_msg>Flip the procedurally-generated-cube uvs.<commit_after>use nalgebra::na;
use nalgebra::na::{Cast, Indexable, FloatVecExt, Vec3, Vec2};
use procedural::{TriMesh, SplitIndexBuffer, Polyline};

/**
 * Generates a cuboid geometry with a split index buffer.
 *
 * # Arguments:
 * * `extents` - the extents of the cuboid.
 */
pub fn cuboid<N: Float + Clone + Cast<f64>>(extents: &Vec3<N>) -> TriMesh<N, Vec3<N>> {
    let mut cuboid = unit_cuboid();

    cuboid.scale_by(extents);

    cuboid
}

/**
 * Generates a cuboid geometry with a split index buffer.
 *
 * The cuboid is centered at the origin, and has its half extents set to 0.5.
 */
pub fn unit_cuboid<N: Float + Clone + Cast<f64>>() -> TriMesh<N, Vec3<N>> {
    let mut coords  = Vec::with_capacity(8);
    let mut uvs     = Vec::with_capacity(4);
    let mut normals = Vec::with_capacity(6);
    let mut faces   = Vec::with_capacity(12);

    let _0_5: N = na::cast(0.5);
    let m0_5: N = -_0_5;
    let _1:   N = na::one();
    let m1:   N = -_1;
    let _0:   N = na::zero();

    coords.push(Vec3::new(m0_5.clone(), m0_5.clone(), _0_5.clone()));
    coords.push(Vec3::new(m0_5.clone(), m0_5.clone(), m0_5.clone()));
    coords.push(Vec3::new(_0_5.clone(), m0_5.clone(), m0_5.clone()));
    coords.push(Vec3::new(_0_5.clone(), m0_5.clone(), _0_5.clone()));
    coords.push(Vec3::new(m0_5.clone(), _0_5.clone(), _0_5.clone()));
    coords.push(Vec3::new(m0_5.clone(), _0_5.clone(), m0_5.clone()));
    coords.push(Vec3::new(_0_5.clone(), _0_5.clone(), m0_5.clone()));
    coords.push(Vec3::new(_0_5.clone(), _0_5.clone(), _0_5.clone()));

    uvs.push(Vec2::new(_0.clone(), _1.clone()));
    uvs.push(Vec2::new(_1.clone(), _1.clone()));
    uvs.push(Vec2::new(_0.clone(), _0.clone()));
    uvs.push(Vec2::new(_1.clone(), _0.clone()));

    normals.push(Vec3::new(m1.clone(), _0.clone(), _0.clone()));
    normals.push(Vec3::new(_0.clone(), _0.clone(), m1.clone()));
    normals.push(Vec3::new(_1.clone(), _0.clone(), _0.clone()));
    normals.push(Vec3::new(_0.clone(), _0.clone(), _1.clone()));
    normals.push(Vec3::new(_0.clone(), m1.clone(), _0.clone()));
    normals.push(Vec3::new(_0.clone(), _1.clone(), _0.clone()));

    faces.push(Vec3::new(Vec3::new(4, 0, 0), Vec3::new(5, 1, 0), Vec3::new(0, 2, 0)));
    faces.push(Vec3::new(Vec3::new(5, 0, 1), Vec3::new(6, 1, 1), Vec3::new(1, 2, 1)));
    faces.push(Vec3::new(Vec3::new(6, 1, 2), Vec3::new(7, 0, 2), Vec3::new(3, 2, 2)));
    faces.push(Vec3::new(Vec3::new(7, 1, 3), Vec3::new(4, 0, 3), Vec3::new(0, 2, 3)));
    faces.push(Vec3::new(Vec3::new(0, 2, 4), Vec3::new(1, 0, 4), Vec3::new(2, 1, 4)));
    faces.push(Vec3::new(Vec3::new(7, 3, 5), Vec3::new(6, 1, 5), Vec3::new(5, 0, 5)));
    faces.push(Vec3::new(Vec3::new(5, 1, 0), Vec3::new(1, 3, 0), Vec3::new(0, 2, 0)));
    faces.push(Vec3::new(Vec3::new(6, 1, 1), Vec3::new(2, 3, 1), Vec3::new(1, 2, 1)));
    faces.push(Vec3::new(Vec3::new(2, 3, 2), Vec3::new(6, 1, 2), Vec3::new(3, 2, 2)));
    faces.push(Vec3::new(Vec3::new(3, 3, 3), Vec3::new(7, 1, 3), Vec3::new(0, 2, 3)));
    faces.push(Vec3::new(Vec3::new(3, 3, 4), Vec3::new(0, 2, 4), Vec3::new(2, 1, 4)));
    faces.push(Vec3::new(Vec3::new(4, 2, 5), Vec3::new(7, 3, 5), Vec3::new(5, 0, 5)));

    TriMesh::new(coords, Some(normals), Some(uvs), Some(SplitIndexBuffer(faces)))
}

/// The contour of a cuboid lying on the x-y plane.
pub fn rectangle<N: Float + Clone + Cast<f64>, V: FloatVecExt<N>>(extents: &V) -> Polyline<N, V> {
    let mut rectangle = unit_rectangle();

    rectangle.scale_by(extents);

    rectangle
}

/// The contour of a unit cuboid lying on the x-y plane.
pub fn unit_rectangle<N: Float + Clone + Cast<f64>, V: FloatVecExt<N>>() -> Polyline<N, V> {
    let _0_5: N = na::cast(0.5);
    let m0_5: N = -_0_5;

    let mut p_ul = na::zero::<V>();
    let mut p_ur = na::zero::<V>();
    let mut p_dl = na::zero::<V>();
    let mut p_dr = na::zero::<V>();

    p_ul.set(0, m0_5.clone()); p_ul.set(1, _0_5.clone());
    p_ur.set(0, _0_5.clone()); p_ur.set(1, _0_5.clone());
    p_dl.set(0, _0_5.clone()); p_dl.set(1, m0_5.clone());
    p_dr.set(0, m0_5.clone()); p_dr.set(1, m0_5.clone());

    Polyline::new(vec!(p_ur, p_ul, p_dl, p_dr), None)
}
<|endoftext|>


<commit_before>/// Helper macro to produce a Janus plugin instance. Should be called with
/// a `LibraryMetadata` instance and a series of exported plugin event handlers.
#[macro_export]
macro_rules! build_plugin {
    ($md:expr, $($cb:ident),*) => {{
        extern "C" fn get_api_compatibility() -> c_int { $md.api_version }
        extern "C" fn get_version() -> c_int { $md.version }
        extern "C" fn get_version_string() -> *const c_char { $md.version_str.as_ptr() }
        extern "C" fn get_description() -> *const c_char { $md.description.as_ptr() }
        extern "C" fn get_name() -> *const c_char { $md.name.as_ptr() }
        extern "C" fn get_author() -> *const c_char { $md.author.as_ptr() }
        extern "C" fn get_package() -> *const c_char { $md.package.as_ptr() }
        $crate::Plugin {
            get_api_compatibility,
            get_version,
            get_version_string,
            get_description,
            get_name,
            get_author,
            get_package,
            $($cb,)*
        }
    }}
}

/// Macro to export a Janus plugin instance from this module.
#[macro_export]
macro_rules! export_plugin {
    ($pl:expr) => {
        /// Called by Janus to create an instance of this plugin, using the provided callbacks to dispatch events.
        #[no_mangle]
        pub extern "C" fn create() -> *const $crate::Plugin { $pl }
    }
}

/// A Janus plugin result; what a plugin returns to the gateway as a direct response to a signalling message.
#[derive(Debug)]
pub struct PluginResult {
    ptr: *mut RawPluginResult,
}

impl PluginResult {
    /// Creates a new plugin result.
    pub unsafe fn new(type_: PluginResultType, text: *const c_char, content: *mut RawJanssonValue) -> Self {
        Self { ptr: ffi::plugin::janus_plugin_result_new(type_, text, content) }
    }

    /// Creates a plugin result indicating a synchronously successful request. The provided response
    /// JSON will be passed back to the client.
    pub fn ok(response: JanssonValue) -> Self {
        unsafe { Self::new(PluginResultType::JANUS_PLUGIN_OK, ptr::null(), response.into_raw()) }
    }

    /// Creates a plugin result indicating an asynchronous request in progress. If provided, the hint text
    /// will be synchronously passed back to the client in the acknowledgement.
    pub fn ok_wait(hint: Option<&'static CStr>) -> Self {
        let hint_ptr = hint.map(|x| x.as_ptr()).unwrap_or_else(ptr::null);
        unsafe { Self::new(PluginResultType::JANUS_PLUGIN_OK_WAIT, hint_ptr, ptr::null_mut()) }
    }

    /// Creates a plugin result indicating an error. The provided error text will be synchronously passed
    /// back to the client.
    pub fn error(msg: &'static CStr) -> Self {
        unsafe { Self::new(PluginResultType::JANUS_PLUGIN_ERROR, msg.as_ptr(), ptr::null_mut()) }
    }

    /// Transfers ownership of this result to the wrapped raw pointer. The consumer is responsible for calling
    /// `janus_plugin_result_destroy` on the pointer when finished.
    pub fn into_raw(self) -> *mut RawPluginResult {
        let ptr = self.ptr;
        mem::forget(self);
        ptr
    }
}

impl Deref for PluginResult {
    type Target = RawPluginResult;

    fn deref(&self) -> &RawPluginResult {
        unsafe { &*self.ptr }
    }
}

impl Drop for PluginResult {
    fn drop(&mut self) {
        unsafe { ffi::plugin::janus_plugin_result_destroy(self.ptr) }
    }
}

unsafe impl Send for PluginResult {}
<commit_msg>Remove accidentally duplicated code<commit_after><|endoftext|>


<commit_before>//! Render README files to HTML.

use ammonia::{Builder, UrlRelative, UrlRelativeEvaluate};
use htmlescape::encode_minimal;
use std::borrow::Cow;
use std::path::Path;
use swirl::errors::PerformError;
use url::Url;

use crate::background_jobs::Environment;
use crate::models::Version;

/// Context for markdown to HTML rendering.
#[allow(missing_debug_implementations)]
struct MarkdownRenderer<'a> {
    html_sanitizer: Builder<'a>,
}

impl<'a> MarkdownRenderer<'a> {
    /// Creates a new renderer instance.
    ///
    /// Per `readme_to_html`, `base_url` is the base URL prepended to any
    /// relative links in the input document.  See that function for more detail.
    fn new(base_url: Option<&'a str>) -> MarkdownRenderer<'a> {
        let allowed_classes = hashmap(&[(
            "code",
            hashset(&[
                "language-bash",
                "language-clike",
                "language-glsl",
                "language-go",
                "language-ini",
                "language-javascript",
                "language-json",
                "language-markup",
                "language-protobuf",
                "language-ruby",
                "language-rust",
                "language-scss",
                "language-sql",
                "yaml",
            ]),
        )]);
        let sanitize_url = UrlRelative::Custom(Box::new(SanitizeUrl::new(base_url)));

        let mut html_sanitizer = Builder::default();
        html_sanitizer
            .add_tags(&["input"])
            .link_rel(Some("nofollow noopener noreferrer"))
            .add_tag_attributes("a", &["id", "target"])
            .add_tag_attributes("input", &["checked", "disabled", "type"])
            .allowed_classes(allowed_classes)
            .url_relative(sanitize_url)
            .id_prefix(Some("user-content-"));
        MarkdownRenderer { html_sanitizer }
    }

    /// Renders the given markdown to HTML using the current settings.
    fn to_html(&self, text: &str) -> String {
        let options = comrak::ComrakOptions {
            unsafe_: true, // The output will be sanitized with `ammonia`
            ext_autolink: true,
            ext_strikethrough: true,
            ext_table: true,
            ext_tagfilter: true,
            ext_tasklist: true,
            ext_header_ids: Some("user-content-".to_string()),
            ..comrak::ComrakOptions::default()
        };
        let rendered = comrak::markdown_to_html(text, &options);
        self.html_sanitizer.clean(&rendered).to_string()
    }
}

/// Add trailing slash and remove `.git` suffix of base URL.
fn canon_base_url(mut base_url: String) -> String {
    if !base_url.ends_with('/') {
        base_url.push('/');
    }
    if base_url.ends_with(".git/") {
        let offset = base_url.len() - 5;
        base_url.drain(offset..offset + 4);
    }
    base_url
}

/// Sanitize relative URLs in README files.
struct SanitizeUrl {
    base_url: Option<String>,
}

impl SanitizeUrl {
    fn new(base_url: Option<&str>) -> Self {
        let base_url = base_url
            .and_then(|base_url| Url::parse(base_url).ok())
            .and_then(|url| match url.host_str() {
                Some("github.com") | Some("gitlab.com") | Some("bitbucket.org") => {
                    Some(canon_base_url(url.into_string()))
                }
                _ => None,
            });
        Self { base_url }
    }
}

/// Determine whether the given URL has a media file externsion.
fn is_media_url(url: &str) -> bool {
    Path::new(url)
        .extension()
        .and_then(std::ffi::OsStr::to_str)
        .map_or(false, |e| match e {
            "png" | "svg" | "jpg" | "jpeg" | "gif" | "mp4" | "webm" | "ogg" => true,
            _ => false,
        })
}

impl UrlRelativeEvaluate for SanitizeUrl {
    fn evaluate<'a>(&self, url: &'a str) -> Option<Cow<'a, str>> {
        if url.starts_with('#') {
            // Always allow fragment URLs.
            return Some(Cow::Borrowed(url));
        }
        self.base_url.as_ref().map(|base_url| {
            let mut new_url = base_url.clone();
            // Assumes GitHub’s URL scheme. GitHub renders text and markdown
            // better in the "blob" view, but images need to be served raw.
            new_url += if is_media_url(url) {
                "raw/master"
            } else {
                "blob/master"
            };
            if !url.starts_with('/') {
                new_url.push('/');
            }
            new_url += url;
            Cow::Owned(new_url)
        })
    }
}

/// Renders Markdown text to sanitized HTML with a given `base_url`.
/// See `readme_to_html` for the interpretation of `base_url`.
fn markdown_to_html(text: &str, base_url: Option<&str>) -> String {
    let renderer = MarkdownRenderer::new(base_url);
    renderer.to_html(text)
}

/// Any readme with a filename ending in one of these extensions will be rendered as Markdown.
/// Note we also render a readme as Markdown if _no_ extension is on the filename.
static MARKDOWN_EXTENSIONS: [&str; 7] = [
    ".md",
    ".markdown",
    ".mdown",
    ".mdwn",
    ".mkd",
    ".mkdn",
    ".mkdown",
];

/// Renders a readme to sanitized HTML.  An appropriate rendering method is chosen depending
/// on the extension of the supplied `filename`.
///
/// The returned text will not contain any harmful HTML tag or attribute (such as iframe,
/// onclick, onmouseover, etc.).
///
/// The `base_url` parameter will be used as the base for any relative links found in the
/// Markdown, as long as its host part is github.com, gitlab.com, or bitbucket.org.  The
/// supplied URL will be used as a directory base whether or not the relative link is
/// prefixed with '/'.  If `None` is passed, relative links will be omitted.
///
/// # Examples
///
/// ```
/// use render::render_to_html;
///
/// let text = "[Rust](https://rust-lang.org/) is an awesome *systems programming* language!";
/// let rendered = readme_to_html(text, "README.md", None)?;
/// ```
pub fn readme_to_html(text: &str, filename: &str, base_url: Option<&str>) -> String {
    let filename = filename.to_lowercase();

    if !filename.contains('.') || MARKDOWN_EXTENSIONS.iter().any(|e| filename.ends_with(e)) {
        return markdown_to_html(text, base_url);
    }

    encode_minimal(text).replace("\n", "
\n")
}

#[swirl::background_job]
pub fn render_and_upload_readme(
    env: &Environment,
    version_id: i32,
    text: String,
    file_name: String,
    base_url: Option<String>,
) -> Result<(), PerformError> {
    use crate::schema::*;
    use crate::util::errors::std_error_no_send;
    use diesel::prelude::*;

    let rendered = readme_to_html(&text, &file_name, base_url.as_ref().map(String::as_str));
    let conn = env.connection()?;

    conn.transaction(|| {
        Version::record_readme_rendering(version_id, &conn)?;
        let (crate_name, vers) = versions::table
            .find(version_id)
            .inner_join(crates::table)
            .select((crates::name, versions::num))
            .first::<(String, String)>(&*conn)?;
        env.uploader
            .upload_readme(env.http_client(), &crate_name, &vers, rendered)
            .map_err(std_error_no_send)?;
        Ok(())
    })
}

/// Helper function to build a new `HashSet` from the items slice.
fn hashset<T>(items: &[T]) -> std::collections::HashSet<T>
where
    T: Clone + Eq + std::hash::Hash,
{
    items.iter().cloned().collect()
}

/// Helper function to build a new `HashMap` from a slice of key-value pairs.
fn hashmap<K, V>(items: &[(K, V)]) -> std::collections::HashMap<K, V>
where
    K: Clone + Eq + std::hash::Hash,
    V: Clone,
{
    items.iter().cloned().collect()
}

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

    #[test]
    fn empty_text() {
        let text = "";
        let result = markdown_to_html(text, None);
        assert_eq!(result, "");
    }

    #[test]
    fn text_with_script_tag() {
        let text = "foo_readme\n\n";
        let result = markdown_to_html(text, None);
        assert_eq!(
            result,
            "
foo_readme
\n<script>alert(\'Hello World\')</script>\n"
        );
    }

    #[test]
    fn text_with_iframe_tag() {
        let text = "foo_readme\n\nalert('Hello World')";
        let result = markdown_to_html(text, None);
        assert_eq!(
            result,
            "
foo_readme
\n<iframe>alert(\'Hello World\')</iframe>\n"
        );
    }

    #[test]
    fn text_with_unknown_tag() {
        let text = "foo_readme\n\n<unknown>alert('Hello World')</unknown>";
        let result = markdown_to_html(text, None);
        assert_eq!(result, "
foo_readme
\n
alert(\'Hello World\')
\n");
    }

    #[test]
    fn text_with_inline_javascript() {
        let text =
            r#"foo_readme\n\nCrate page"#;
        let result = markdown_to_html(text, None);
        assert_eq!(
            result,
            "
foo_readme\\n\\nCrate page
\n"
        );
    }

    // See https://github.com/kivikakk/comrak/issues/37. This panic happened
    // in comrak 0.1.8 but was fixed in 0.1.9.
    #[test]
    fn text_with_fancy_single_quotes() {
        let text = r#"wb’"#;
        let result = markdown_to_html(text, None);
        assert_eq!(result, "
wb’
\n");
    }

    #[test]
    fn code_block_with_syntax_highlighting() {
        let code_block = r#"```rust \
                            println!("Hello World"); \
                           ```"#;
        let result = markdown_to_html(code_block, None);
        assert!(result.contains(""));
    }

    #[test]
    fn text_with_forbidden_class_attribute() {
        let text = "
Hello World!
";
        let result = markdown_to_html(text, None);
        assert_eq!(result, "
Hello World!
\n");
    }

    #[test]
    fn relative_links() {
        let absolute = "[hi](/hi)";
        let relative = "[there](there)";
        let image = "![alt](img.png)";

        for host in &["github.com", "gitlab.com", "bitbucket.org"] {
            for (&extra_slash, &dot_git) in [true, false].iter().zip(&[true, false]) {
                let url = format!(
                    "https://{}/rust-lang/test{}{}",
                    host,
                    if dot_git { ".git" } else { "" },
                    if extra_slash { "/" } else { "" },
                );

                let result = markdown_to_html(absolute, Some(&url));
                assert_eq!(
                    result,
                    format!(
                        "
hi
\n",
                        host
                    )
                );

                let result = markdown_to_html(relative, Some(&url));
                assert_eq!(
                    result,
                    format!(
                        "
there
\n",
                        host
                    )
                );

                let result = markdown_to_html(image, Some(&url));
                assert_eq!(
                    result,
                    format!(
                 "

\n",
                        host
                    )
                );
            }
        }

        let result = markdown_to_html(absolute, Some("https://google.com/"));
        assert_eq!(
            result,
            "
hi
\n"
        );
    }

    #[test]
    fn absolute_links_dont_get_resolved() {
        let readme_text =
            "[![Crates.io](https://img.shields.io/crates/v/clap.svg)](https://crates.io/crates/clap)";
        let repository = "https://github.com/kbknapp/clap-rs/";
        let result = markdown_to_html(readme_text, Some(repository));

        assert_eq!(
            result,
            "

\n"
        );
    }

    #[test]
    fn readme_to_html_renders_markdown() {
        for f in &["README", "readme.md", "README.MARKDOWN", "whatever.mkd"] {
            assert_eq!(
                readme_to_html("*lobster*", f, None),
                "
lobster
\n"
            );
        }
    }

    #[test]
    fn readme_to_html_renders_other_things() {
        for f in &["readme.exe", "readem.org", "blah.adoc"] {
            assert_eq!(
                readme_to_html("\n\nis my friend\n", f, None),
                "<script>lobster</script>
\n
\nis my friend
\n"
            );
        }
    }

    #[test]
    fn header_has_tags() {
        let text = "# My crate\n\nHello, world!\n";
        let result = markdown_to_html(text, None);
        assert_eq!(
            result,
            "
My crate
\n
Hello, world!
\n"
        );
    }

    #[test]
    fn manual_anchor_is_sanitized() {
        let text =
            "
My crate
\n
Hello, world!
\n";
        let result = markdown_to_html(text, None);
        assert_eq!(
            result,
            "
My crate
\n
Hello, world!
\n"
        );
    }

    #[test]
    fn tables_with_rowspan_and_colspan() {
        let text = "

Target

\n";
        let result = markdown_to_html(text, None);
        assert_eq!(
            result,
            "

Target

\n"
        );
    }
}
<commit_msg>Reformat with beta rustfmt<commit_after>//! Render README files to HTML.

use ammonia::{Builder, UrlRelative, UrlRelativeEvaluate};
use htmlescape::encode_minimal;
use std::borrow::Cow;
use std::path::Path;
use swirl::errors::PerformError;
use url::Url;

use crate::background_jobs::Environment;
use crate::models::Version;

/// Context for markdown to HTML rendering.
#[allow(missing_debug_implementations)]
struct MarkdownRenderer<'a> {
    html_sanitizer: Builder<'a>,
}

impl<'a> MarkdownRenderer<'a> {
    /// Creates a new renderer instance.
    ///
    /// Per `readme_to_html`, `base_url` is the base URL prepended to any
    /// relative links in the input document.  See that function for more detail.
    fn new(base_url: Option<&'a str>) -> MarkdownRenderer<'a> {
        let allowed_classes = hashmap(&[(
            "code",
            hashset(&[
                "language-bash",
                "language-clike",
                "language-glsl",
                "language-go",
                "language-ini",
                "language-javascript",
                "language-json",
                "language-markup",
                "language-protobuf",
                "language-ruby",
                "language-rust",
                "language-scss",
                "language-sql",
                "yaml",
            ]),
        )]);
        let sanitize_url = UrlRelative::Custom(Box::new(SanitizeUrl::new(base_url)));

        let mut html_sanitizer = Builder::default();
        html_sanitizer
            .add_tags(&["input"])
            .link_rel(Some("nofollow noopener noreferrer"))
            .add_tag_attributes("a", &["id", "target"])
            .add_tag_attributes("input", &["checked", "disabled", "type"])
            .allowed_classes(allowed_classes)
            .url_relative(sanitize_url)
            .id_prefix(Some("user-content-"));
        MarkdownRenderer { html_sanitizer }
    }

    /// Renders the given markdown to HTML using the current settings.
    fn to_html(&self, text: &str) -> String {
        let options = comrak::ComrakOptions {
            unsafe_: true, // The output will be sanitized with `ammonia`
            ext_autolink: true,
            ext_strikethrough: true,
            ext_table: true,
            ext_tagfilter: true,
            ext_tasklist: true,
            ext_header_ids: Some("user-content-".to_string()),
            ..comrak::ComrakOptions::default()
        };
        let rendered = comrak::markdown_to_html(text, &options);
        self.html_sanitizer.clean(&rendered).to_string()
    }
}

/// Add trailing slash and remove `.git` suffix of base URL.
fn canon_base_url(mut base_url: String) -> String {
    if !base_url.ends_with('/') {
        base_url.push('/');
    }
    if base_url.ends_with(".git/") {
        let offset = base_url.len() - 5;
        base_url.drain(offset..offset + 4);
    }
    base_url
}

/// Sanitize relative URLs in README files.
struct SanitizeUrl {
    base_url: Option<String>,
}

impl SanitizeUrl {
    fn new(base_url: Option<&str>) -> Self {
        let base_url = base_url
            .and_then(|base_url| Url::parse(base_url).ok())
            .and_then(|url| match url.host_str() {
                Some("github.com") | Some("gitlab.com") | Some("bitbucket.org") => {
                    Some(canon_base_url(url.into_string()))
                }
                _ => None,
            });
        Self { base_url }
    }
}

/// Determine whether the given URL has a media file externsion.
fn is_media_url(url: &str) -> bool {
    Path::new(url)
        .extension()
        .and_then(std::ffi::OsStr::to_str)
        .map_or(false, |e| match e {
            "png" | "svg" | "jpg" | "jpeg" | "gif" | "mp4" | "webm" | "ogg" => true,
            _ => false,
        })
}

impl UrlRelativeEvaluate for SanitizeUrl {
    fn evaluate<'a>(&self, url: &'a str) -> Option<Cow<'a, str>> {
        if url.starts_with('#') {
            // Always allow fragment URLs.
            return Some(Cow::Borrowed(url));
        }
        self.base_url.as_ref().map(|base_url| {
            let mut new_url = base_url.clone();
            // Assumes GitHub’s URL scheme. GitHub renders text and markdown
            // better in the "blob" view, but images need to be served raw.
            new_url += if is_media_url(url) {
                "raw/master"
            } else {
                "blob/master"
            };
            if !url.starts_with('/') {
                new_url.push('/');
            }
            new_url += url;
            Cow::Owned(new_url)
        })
    }
}

/// Renders Markdown text to sanitized HTML with a given `base_url`.
/// See `readme_to_html` for the interpretation of `base_url`.
fn markdown_to_html(text: &str, base_url: Option<&str>) -> String {
    let renderer = MarkdownRenderer::new(base_url);
    renderer.to_html(text)
}

/// Any readme with a filename ending in one of these extensions will be rendered as Markdown.
/// Note we also render a readme as Markdown if _no_ extension is on the filename.
static MARKDOWN_EXTENSIONS: [&str; 7] = [
    ".md",
    ".markdown",
    ".mdown",
    ".mdwn",
    ".mkd",
    ".mkdn",
    ".mkdown",
];

/// Renders a readme to sanitized HTML.  An appropriate rendering method is chosen depending
/// on the extension of the supplied `filename`.
///
/// The returned text will not contain any harmful HTML tag or attribute (such as iframe,
/// onclick, onmouseover, etc.).
///
/// The `base_url` parameter will be used as the base for any relative links found in the
/// Markdown, as long as its host part is github.com, gitlab.com, or bitbucket.org.  The
/// supplied URL will be used as a directory base whether or not the relative link is
/// prefixed with '/'.  If `None` is passed, relative links will be omitted.
///
/// # Examples
///
/// ```
/// use render::render_to_html;
///
/// let text = "[Rust](https://rust-lang.org/) is an awesome *systems programming* language!";
/// let rendered = readme_to_html(text, "README.md", None)?;
/// ```
pub fn readme_to_html(text: &str, filename: &str, base_url: Option<&str>) -> String {
    let filename = filename.to_lowercase();

    if !filename.contains('.') || MARKDOWN_EXTENSIONS.iter().any(|e| filename.ends_with(e)) {
        return markdown_to_html(text, base_url);
    }

    encode_minimal(text).replace("\n", "
\n")
}

#[swirl::background_job]
pub fn render_and_upload_readme(
    env: &Environment,
    version_id: i32,
    text: String,
    file_name: String,
    base_url: Option<String>,
) -> Result<(), PerformError> {
    use crate::schema::*;
    use crate::util::errors::std_error_no_send;
    use diesel::prelude::*;

    let rendered = readme_to_html(&text, &file_name, base_url.as_ref().map(String::as_str));
    let conn = env.connection()?;

    conn.transaction(|| {
        Version::record_readme_rendering(version_id, &conn)?;
        let (crate_name, vers) = versions::table
            .find(version_id)
            .inner_join(crates::table)
            .select((crates::name, versions::num))
            .first::<(String, String)>(&*conn)?;
        env.uploader
            .upload_readme(env.http_client(), &crate_name, &vers, rendered)
            .map_err(std_error_no_send)?;
        Ok(())
    })
}

/// Helper function to build a new `HashSet` from the items slice.
fn hashset<T>(items: &[T]) -> std::collections::HashSet<T>
where
    T: Clone + Eq + std::hash::Hash,
{
    items.iter().cloned().collect()
}

/// Helper function to build a new `HashMap` from a slice of key-value pairs.
fn hashmap<K, V>(items: &[(K, V)]) -> std::collections::HashMap<K, V>
where
    K: Clone + Eq + std::hash::Hash,
    V: Clone,
{
    items.iter().cloned().collect()
}

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

    #[test]
    fn empty_text() {
        let text = "";
        let result = markdown_to_html(text, None);
        assert_eq!(result, "");
    }

    #[test]
    fn text_with_script_tag() {
        let text = "foo_readme\n\n";
        let result = markdown_to_html(text, None);
        assert_eq!(
            result,
            "
foo_readme
\n<script>alert(\'Hello World\')</script>\n"
        );
    }

    #[test]
    fn text_with_iframe_tag() {
        let text = "foo_readme\n\nalert('Hello World')";
        let result = markdown_to_html(text, None);
        assert_eq!(
            result,
            "
foo_readme
\n<iframe>alert(\'Hello World\')</iframe>\n"
        );
    }

    #[test]
    fn text_with_unknown_tag() {
        let text = "foo_readme\n\n<unknown>alert('Hello World')</unknown>";
        let result = markdown_to_html(text, None);
        assert_eq!(result, "
foo_readme
\n
alert(\'Hello World\')
\n");
    }

    #[test]
    fn text_with_inline_javascript() {
        let text = r#"foo_readme\n\nCrate page"#;
        let result = markdown_to_html(text, None);
        assert_eq!(
            result,
            "
foo_readme\\n\\nCrate page
\n"
        );
    }

    // See https://github.com/kivikakk/comrak/issues/37. This panic happened
    // in comrak 0.1.8 but was fixed in 0.1.9.
    #[test]
    fn text_with_fancy_single_quotes() {
        let text = r#"wb’"#;
        let result = markdown_to_html(text, None);
        assert_eq!(result, "
wb’
\n");
    }

    #[test]
    fn code_block_with_syntax_highlighting() {
        let code_block = r#"```rust \
                            println!("Hello World"); \
                           ```"#;
        let result = markdown_to_html(code_block, None);
        assert!(result.contains(""));
    }

    #[test]
    fn text_with_forbidden_class_attribute() {
        let text = "
Hello World!
";
        let result = markdown_to_html(text, None);
        assert_eq!(result, "
Hello World!
\n");
    }

    #[test]
    fn relative_links() {
        let absolute = "[hi](/hi)";
        let relative = "[there](there)";
        let image = "![alt](img.png)";

        for host in &["github.com", "gitlab.com", "bitbucket.org"] {
            for (&extra_slash, &dot_git) in [true, false].iter().zip(&[true, false]) {
                let url = format!(
                    "https://{}/rust-lang/test{}{}",
                    host,
                    if dot_git { ".git" } else { "" },
                    if extra_slash { "/" } else { "" },
                );

                let result = markdown_to_html(absolute, Some(&url));
                assert_eq!(
                    result,
                    format!(
                        "
hi
\n",
                        host
                    )
                );

                let result = markdown_to_html(relative, Some(&url));
                assert_eq!(
                    result,
                    format!(
                        "
there
\n",
                        host
                    )
                );

                let result = markdown_to_html(image, Some(&url));
                assert_eq!(
                    result,
                    format!(
                 "

\n",
                        host
                    )
                );
            }
        }

        let result = markdown_to_html(absolute, Some("https://google.com/"));
        assert_eq!(
            result,
            "
hi
\n"
        );
    }

    #[test]
    fn absolute_links_dont_get_resolved() {
        let readme_text =
            "[![Crates.io](https://img.shields.io/crates/v/clap.svg)](https://crates.io/crates/clap)";
        let repository = "https://github.com/kbknapp/clap-rs/";
        let result = markdown_to_html(readme_text, Some(repository));

        assert_eq!(
            result,
            "

\n"
        );
    }

    #[test]
    fn readme_to_html_renders_markdown() {
        for f in &["README", "readme.md", "README.MARKDOWN", "whatever.mkd"] {
            assert_eq!(
                readme_to_html("*lobster*", f, None),
                "
lobster
\n"
            );
        }
    }

    #[test]
    fn readme_to_html_renders_other_things() {
        for f in &["readme.exe", "readem.org", "blah.adoc"] {
            assert_eq!(
                readme_to_html("\n\nis my friend\n", f, None),
                "<script>lobster</script>
\n
\nis my friend
\n"
            );
        }
    }

    #[test]
    fn header_has_tags() {
        let text = "# My crate\n\nHello, world!\n";
        let result = markdown_to_html(text, None);
        assert_eq!(
            result,
            "
My crate
\n
Hello, world!
\n"
        );
    }

    #[test]
    fn manual_anchor_is_sanitized() {
        let text =
            "
My crate
\n
Hello, world!
\n";
        let result = markdown_to_html(text, None);
        assert_eq!(
            result,
            "
My crate
\n
Hello, world!
\n"
        );
    }

    #[test]
    fn tables_with_rowspan_and_colspan() {
        let text = "

Target

\n";
        let result = markdown_to_html(text, None);
        assert_eq!(
            result,
            "

Target

\n"
        );
    }
}
<|endoftext|>