<commit_before>/* diosix capsule-provided service management

 * (c) Chris Williams, 2019-2020.
 *
 * See LICENSE for usage and copying.
 */

use super::lock::Mutex;
use hashbrown::hash_map::{HashMap, Entry};
use alloc::collections::vec_deque::VecDeque;
use alloc::vec::Vec;
use super::message;
use super::error::Cause;
use super::capsule::{self, CapsuleID};

/* available type of services that can be offered by a capsule */
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub enum ServiceType
{
    ConsoleInterface = 0 /* act as the console interface manager */
}

/* select either a particular service or all services */
pub enum SelectService
{
    AllServices,
    SingleService(ServiceType)
}

/* todo: a fixed list of known system services,
such as video, sound, serial, network, etc
that privileged / trusted capsules can register.
then other capsules can message those services
to access those underlying resources. */

/* maintain a table of registered services */
lazy_static!
{
    static ref SERVICES: Mutex<HashMap<ServiceType, Service>> = Mutex::new("system service table", HashMap::new());
}

/* return true if the given service type is registered */
pub fn is_registered(stype: ServiceType) -> bool
{
    let tbl = SERVICES.lock();
    tbl.contains_key(&stype)
}

/* describe an individual service */
struct Service
{
    capsuleid: CapsuleID,       /* capsule that's registered this service */
    msgs: VecDeque<message::Message>  /* queue of messages to deliver to service */
}

impl Service
{
    pub fn queue(&mut self, msg: message::Message)
    {
        self.msgs.push_front(msg);
    }

    pub fn get_capsule_id(&self) -> CapsuleID { self.capsuleid }
}

/* register a service for a capsule. this will fail if the
   capsule has no right to run the service, or if the capsule doesn't exist,
   or if another capsule has already claimed the service type.
   be aware if the capsule has already claimed the service, it will
   return Err(Cause::ServiceAlreadyOwner). this will be the result if a
   restarted capsule registers its service(s) again. services aren't released
   during a restart to provide a non-stop continuation of services.
    => stype = type of service to register
       cid = ID of capsule to handle this service
    <= return Ok for success, or a failure code */
pub fn register(stype: ServiceType, cid: CapsuleID) -> Result<(), Cause>
{
    if capsule::is_service_allowed(cid, stype)? == false
    {
        return Err(Cause::ServiceNotAllowed);
    }

    let service = Service
    {
        capsuleid: cid,
        msgs: VecDeque::new()
    };

    match SERVICES.lock().entry(stype)
    {
        Entry::Vacant(v) =>
        {
            v.insert(service);
        },
        Entry::Occupied(o) => if o.get().get_capsule_id() != cid
        {
            /* another capsule owns this service */
            return Err(Cause::ServiceAlreadyRegistered);
        }
        else
        {
            /* this capsule already owns this service */
            return Err(Cause::ServiceAlreadyOwner)
        }
    }

    Ok(())
}

/* deregister one or all services belonding to a capsule
   so that it is no longer responsible for them
   => stype = service to deregister, or None for all of them
      cid = ID of capsule to strip of its services
   <= Ok for success, or an error code for failure */
pub fn deregister(stype: SelectService, cid: CapsuleID) -> Result<(), Cause>
{
    let mut tbl = SERVICES.lock();
    let mut to_remove = Vec::new();

    for (registered, owner) in (&tbl).iter()
    {
        /* remove either everything that matches the capsule ID, or a particular service */
        match stype
        {
            SelectService::AllServices => if owner.get_capsule_id() == cid
            {
                to_remove.push(*registered);
            },
            SelectService::SingleService(s) => if owner.get_capsule_id() == cid && s == *registered
            {
                to_remove.push(*registered);
            }
        }
    }

    /* now remove the vicims */
    for victim in to_remove
    {
        tbl.remove(&victim);
    }

    Ok(())
}

/* send the given message msg to a registered service */
pub fn send(msg: message::Message) -> Result<(), Cause>
{
    let stype = match msg.get_receiver()
    {
        message::Recipient::Service(stype) => stype,
        _ => return Err(Cause::MessageBadType)
    };

    if let Some(service) = SERVICES.lock().get_mut(&stype)
    {
        service.queue(msg);
        Ok(())
    }
    else
    {
        return Err(Cause::ServiceNotAllowed)
    }
}<commit_msg>Translate service IDs to service types<commit_after>/* diosix capsule-provided service management

 * (c) Chris Williams, 2019-2020.
 *
 * See LICENSE for usage and copying.
 */

use super::lock::Mutex;
use hashbrown::hash_map::{HashMap, Entry};
use alloc::collections::vec_deque::VecDeque;
use alloc::vec::Vec;
use super::message;
use super::error::Cause;
use super::capsule::{self, CapsuleID};

/* available type of services that can be offered by a capsule */
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub enum ServiceType
{
    ConsoleInterface = 0 /* act as the console interface manager */
}

pub fn usize_to_service_type(stype: usize) -> Result<ServiceType, Cause>
{
    match stype
    {
        0 => Ok(ServiceType::ConsoleInterface),
        _ => Err(Cause::ServiceNotFound)
    }
}

/* select either a particular service or all services */
pub enum SelectService
{
    AllServices,
    SingleService(ServiceType)
}

/* todo: a fixed list of known system services,
such as video, sound, serial, network, etc
that privileged / trusted capsules can register.
then other capsules can message those services
to access those underlying resources. */

/* maintain a table of registered services */
lazy_static!
{
    static ref SERVICES: Mutex<HashMap<ServiceType, Service>> = Mutex::new("system service table", HashMap::new());
}

/* return true if the given service type is registered */
pub fn is_registered(stype: ServiceType) -> bool
{
    let tbl = SERVICES.lock();
    tbl.contains_key(&stype)
}

/* describe an individual service */
struct Service
{
    capsuleid: CapsuleID,       /* capsule that's registered this service */
    msgs: VecDeque<message::Message>  /* queue of messages to deliver to service */
}

impl Service
{
    pub fn queue(&mut self, msg: message::Message)
    {
        self.msgs.push_front(msg);
    }

    pub fn get_capsule_id(&self) -> CapsuleID { self.capsuleid }
}

/* register a service for a capsule. this will fail if the
   capsule has no right to run the service, or if the capsule doesn't exist,
   or if another capsule has already claimed the service type.
   be aware if the capsule has already claimed the service, it will
   return Err(Cause::ServiceAlreadyOwner). this will be the result if a
   restarted capsule registers its service(s) again. services aren't released
   during a restart to provide a non-stop continuation of services.
    => stype = type of service to register
       cid = ID of capsule to handle this service
    <= return Ok for success, or a failure code */
pub fn register(stype: ServiceType, cid: CapsuleID) -> Result<(), Cause>
{
    if capsule::is_service_allowed(cid, stype)? == false
    {
        return Err(Cause::ServiceNotAllowed);
    }

    let service = Service
    {
        capsuleid: cid,
        msgs: VecDeque::new()
    };

    match SERVICES.lock().entry(stype)
    {
        Entry::Vacant(v) =>
        {
            v.insert(service);
        },
        Entry::Occupied(o) => if o.get().get_capsule_id() != cid
        {
            /* another capsule owns this service */
            return Err(Cause::ServiceAlreadyRegistered);
        }
        else
        {
            /* this capsule already owns this service */
            return Err(Cause::ServiceAlreadyOwner)
        }
    }

    Ok(())
}

/* deregister one or all services belonding to a capsule
   so that it is no longer responsible for them
   => stype = service to deregister, or None for all of them
      cid = ID of capsule to strip of its services
   <= Ok for success, or an error code for failure */
pub fn deregister(stype: SelectService, cid: CapsuleID) -> Result<(), Cause>
{
    let mut tbl = SERVICES.lock();
    let mut to_remove = Vec::new();

    for (registered, owner) in (&tbl).iter()
    {
        /* remove either everything that matches the capsule ID, or a particular service */
        match stype
        {
            SelectService::AllServices => if owner.get_capsule_id() == cid
            {
                to_remove.push(*registered);
            },
            SelectService::SingleService(s) => if owner.get_capsule_id() == cid && s == *registered
            {
                to_remove.push(*registered);
            }
        }
    }

    /* now remove the vicims */
    for victim in to_remove
    {
        tbl.remove(&victim);
    }

    Ok(())
}

/* send the given message msg to a registered service */
pub fn send(msg: message::Message) -> Result<(), Cause>
{
    let stype = match msg.get_receiver()
    {
        message::Recipient::Service(stype) => stype,
        _ => return Err(Cause::MessageBadType)
    };

    if let Some(service) = SERVICES.lock().get_mut(&stype)
    {
        service.queue(msg);
        Ok(())
    }
    else
    {
        return Err(Cause::ServiceNotAllowed)
    }
}<|endoftext|>


<commit_before>use std::fmt::{self, Display, Formatter};
use std::str;

#[derive(Debug, PartialEq)]
pub enum MarkupDisplay<T>
where
    T: Display,
{
    Safe(T),
    Unsafe(T),
}

impl<T> MarkupDisplay<T>
where
    T: Display,
{
    pub fn mark_safe(self) -> MarkupDisplay<T> {
        match self {
            MarkupDisplay::Unsafe(t) => MarkupDisplay::Safe(t),
            _ => self,
        }
    }
}

impl<T> From<T> for MarkupDisplay<T>
where
    T: Display,
{
    fn from(t: T) -> MarkupDisplay<T> {
        MarkupDisplay::Unsafe(t)
    }
}

impl<T> Display for MarkupDisplay<T>
where
    T: Display,
{
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        match *self {
            MarkupDisplay::Unsafe(ref t) => escape(&t.to_string()).fmt(f),
            MarkupDisplay::Safe(ref t) => t.fmt(f),
        }
    }
}

const FLAG: u8 = b'>' - b'"';

pub fn escape(s: &str) -> Escaped {
    Escaped {
        bytes: s.as_bytes(),
    }
}

pub struct Escaped<'a> {
    bytes: &'a [u8],
}

enum State {
    Empty,
    Unescaped(usize),
}

impl<'a> ::std::fmt::Display for Escaped<'a> {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        use self::State::*;
        let mut state = Empty;
        for (i, b) in self.bytes.iter().enumerate() {
            let next = if b.wrapping_sub(b'"') <= FLAG {
                match *b {
                    b'<' => Some("<"),
                    b'>' => Some(">"),
                    b'&' => Some("&"),
                    b'"' => Some("""),
                    b'\'' => Some("'"),
                    b'/' => Some("/"),
                    _ => None,
                }
            } else {
                None
            };
            state = match (state, next) {
                (Empty, None) => Unescaped(i),
                (s @ Unescaped(_), None) => s,
                (Empty, Some(escaped)) => {
                    fmt.write_str(escaped)?;
                    Empty
                }
                (Unescaped(start), Some(escaped)) => {
                    fmt.write_str(unsafe { str::from_utf8_unchecked(&self.bytes[start..i]) })?;
                    fmt.write_str(escaped)?;
                    Empty
                }
            };
        }
        if let Unescaped(start) = state {
            fmt.write_str(unsafe { str::from_utf8_unchecked(&self.bytes[start..]) })?;
        }
        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn test_escape() {
        assert_eq!(escape("").to_string(), "");
        assert_eq!(escape("<&>").to_string(), "<&>");
        assert_eq!(escape("bla&").to_string(), "bla&");
        assert_eq!(escape("<foo").to_string(), "<foo");
        assert_eq!(escape("bla&h").to_string(), "bla&h");
    }
}
<commit_msg>Improve performance simplify<commit_after>use std::fmt::{self, Display, Formatter};
use std::str;

#[derive(Debug, PartialEq)]
pub enum MarkupDisplay<T>
where
    T: Display,
{
    Safe(T),
    Unsafe(T),
}

impl<T> MarkupDisplay<T>
where
    T: Display,
{
    pub fn mark_safe(self) -> MarkupDisplay<T> {
        match self {
            MarkupDisplay::Unsafe(t) => MarkupDisplay::Safe(t),
            _ => self,
        }
    }
}

impl<T> From<T> for MarkupDisplay<T>
where
    T: Display,
{
    fn from(t: T) -> MarkupDisplay<T> {
        MarkupDisplay::Unsafe(t)
    }
}

impl<T> Display for MarkupDisplay<T>
where
    T: Display,
{
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        match *self {
            MarkupDisplay::Unsafe(ref t) => escape(&t.to_string()).fmt(f),
            MarkupDisplay::Safe(ref t) => t.fmt(f),
        }
    }
}

const FLAG: u8 = b'>' - b'"';

pub fn escape(s: &str) -> Escaped {
    Escaped {
        bytes: s.as_bytes(),
    }
}

pub struct Escaped<'a> {
    bytes: &'a [u8],
}

macro_rules! escaping_body {
    ($state:ident, $i:ident, $fmt:ident, $_self:ident, $quote:expr) => {{
        if $state < $i {
            $fmt.write_str(unsafe { str::from_utf8_unchecked(&$_self.bytes[$state..$i]) })?;
        }
        $fmt.write_str($quote)?;
        $state = $i + 1;
    }};
}

impl<'a> ::std::fmt::Display for Escaped<'a> {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        let mut state = 0;
        for (i, b) in self.bytes.iter().enumerate() {
            if b.wrapping_sub(b'"') <= FLAG {
                match *b {
                    b'<' => escaping_body!(state, i, fmt, self, "<"),
                    b'>' => escaping_body!(state, i, fmt, self, ">"),
                    b'&' => escaping_body!(state, i, fmt, self, "&"),
                    b'"' => escaping_body!(state, i, fmt, self, """),
                    b'\'' => escaping_body!(state, i, fmt, self, "'"),
                    b'/' => escaping_body!(state, i, fmt, self, "/"),
                    _ => (),
                }
            }
        }

        fmt.write_str(unsafe { str::from_utf8_unchecked(&self.bytes[state..]) })?;
        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn test_escape() {
        assert_eq!(escape("").to_string(), "");
        assert_eq!(escape("<&>").to_string(), "<&>");
        assert_eq!(escape("bla&").to_string(), "bla&");
        assert_eq!(escape("<foo").to_string(), "<foo");
        assert_eq!(escape("bla&h").to_string(), "bla&h");
    }
}
<|endoftext|>


<commit_before>use quote::Tokens;
use syn;

pub fn ber_sequence_serialize(ast: &syn::MacroInput) -> Tokens {
  let name = &ast.ident;
  let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();

  let fields = if let syn::Body::Struct(ref body) = ast.body {
    match *body {
      syn::VariantData::Struct(ref fields) => fields,
      syn::VariantData::Tuple(ref fields) => fields,
      _ => panic!("Expected a struct with fields, but type {} has no fields", name),
    }
  } else {
    panic!("Expected a struct, but type {} was not a struct", name);
  };

  let actions: Vec<_> = fields.iter().map(|v| {
    let ident = &v.ident.as_ref().expect("Requires named idents");
    let ty = &v.ty;
    quote! {
      let tag = ::asn1_cereal::tag::Tag {
        class: ::asn1_cereal::tag::Class::ContextSpecific,
        tagnum: _count,
        constructed: <#ty as ::asn1_cereal::Asn1Info>::asn1_constructed(e),
      };
      _count += 1;

      // FIXME: We should be conditionally setting the constructed flag.

      if E::tag_rules() == ::asn1_cereal::ber::enc::TagEnc::Implicit {
        try!(::asn1_cereal::BerSerialize::serialize_value(&self.#ident, e, &mut bytes));
      } else {
        try!(::asn1_cereal::BerSerialize::serialize_enc(&self.#ident, e, &mut bytes));
      }
      let len: ::asn1_cereal::tag::Len = Some(bytes.len() as ::asn1_cereal::tag::LenNum).into();
      try!(::asn1_cereal::tag::write_taglen(tag, len, writer));
      try!(writer.write_all(&mut bytes));
      bytes.clear();
    }
  }).collect();

  quote! {
    impl #impl_generics ::asn1_cereal::BerSerialize for #name #ty_generics #where_clause {
      fn serialize_value<E: ::asn1_cereal::BerEncRules, W: ::std::io::Write>
          (&self, e: E, writer: &mut W) -> Result<(), ::asn1_cereal::err::EncodeError> {
        let mut bytes = Vec::new();
        let mut _count = 0u64;

        #( { #actions }; )*
        Ok(())
      }
    }
  }
}

pub fn ber_sequence_deserialize(ast: &syn::MacroInput) -> Tokens {
  let name = &ast.ident;
  let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();

  let fields = if let syn::Body::Struct(ref body) = ast.body {
    match *body {
      syn::VariantData::Struct(ref fields) => fields,
      syn::VariantData::Tuple(ref fields) => fields,
      _ => panic!("Expected a struct with fields, but type {} has no fields", name),
    }
  } else {
    panic!("Expected a struct, but type {} was not a struct", name);
  };

  let build: Vec<_> = fields.iter().map(|v| {
    let ident = &v.ident;
    let f_ident: syn::Ident = format!("field_{}", ident.as_ref().expect("Requires named idents")).into();
    let ty = &v.ty;
    quote! {
      let #f_ident = {
        let this_tag = try!(::asn1_cereal::tag::Tag::read_tag(reader));
        let our_tag = ::asn1_cereal::tag::Tag {
          class: ::asn1_cereal::tag::Class::ContextSpecific,
          tagnum: _count,
          constructed: <#ty as ::asn1_cereal::Asn1Info>::asn1_constructed(e),
        };
        _count += 1;

        let len = try!(::asn1_cereal::tag::Len::read_len(reader));

        // TODO: Handle entries without a context specific tag.
        if this_tag != our_tag {
          return Err(::asn1_cereal::err::DecodeError::TagTypeMismatch);
        }

        // If we are decoding with an implicit tag, deserialize value directly.
        if E::tag_rules() == ::asn1_cereal::ber::enc::TagEnc::Implicit {
          try!(::asn1_cereal::BerDeserialize::deserialize_value(e, reader, len))
        } else {
          try!(::asn1_cereal::BerDeserialize::deserialize_enc(e, reader))
        }
      };
    }
  }).collect();

  let assignments: Vec<_> = fields.iter().map(|v| {
    let ident = &v.ident;
    let f_ident: syn::Ident = format!("field_{}", ident.as_ref().expect("Requires named idents")).into();
    quote!(#ident: #f_ident)
  }).collect();

  quote! {
    impl #impl_generics ::asn1_cereal::BerDeserialize for #name #ty_generics #where_clause {
      fn deserialize_value<E: ::asn1_cereal::BerEncRules, I: Iterator<Item=::std::io::Result<u8>>>
          (e: E, reader: &mut I, _: ::asn1_cereal::tag::Len) -> Result<Self, ::asn1_cereal::err::DecodeError> {
        let mut _count = 0u64;
        let mut _tag: Option<::asn1_cereal::tag::Tag> = None;

        #( #build )*

        Ok(#name {
          #(#assignments),*
        })
      }
    }
  }
}
<commit_msg>Fix implicit tag handling for sequences<commit_after>use quote::Tokens;
use syn;

pub fn ber_sequence_serialize(ast: &syn::MacroInput) -> Tokens {
  let name = &ast.ident;
  let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();

  let fields = if let syn::Body::Struct(ref body) = ast.body {
    match *body {
      syn::VariantData::Struct(ref fields) => fields,
      syn::VariantData::Tuple(ref fields) => fields,
      _ => panic!("Expected a struct with fields, but type {} has no fields", name),
    }
  } else {
    panic!("Expected a struct, but type {} was not a struct", name);
  };

  let actions: Vec<_> = fields.iter().map(|v| {
    let ident = &v.ident.as_ref().expect("Requires named idents");
    let ty = &v.ty;
    quote! {
      let is_implicit = E::tag_rules() == ::asn1_cereal::ber::enc::TagEnc::Implicit;
      let tag = ::asn1_cereal::tag::Tag {
        class: ::asn1_cereal::tag::Class::ContextSpecific,
        tagnum: _count,
        constructed:
          if is_implicit {
            <#ty as ::asn1_cereal::Asn1Info>::asn1_constructed(e)
          } else {
            true
          },
      };
      _count += 1;

      if is_implicit {
        try!(::asn1_cereal::BerSerialize::serialize_value(&self.#ident, e, &mut bytes));
      } else {
        try!(::asn1_cereal::BerSerialize::serialize_enc(&self.#ident, e, &mut bytes));
      }
      let len: ::asn1_cereal::tag::Len = Some(bytes.len() as ::asn1_cereal::tag::LenNum).into();
      try!(::asn1_cereal::tag::write_taglen(tag, len, writer));
      try!(writer.write_all(&mut bytes));
      bytes.clear();
    }
  }).collect();

  quote! {
    impl #impl_generics ::asn1_cereal::BerSerialize for #name #ty_generics #where_clause {
      fn serialize_value<E: ::asn1_cereal::BerEncRules, W: ::std::io::Write>
          (&self, e: E, writer: &mut W) -> Result<(), ::asn1_cereal::err::EncodeError> {
        let mut bytes = Vec::new();
        let mut _count = 0u64;

        #( { #actions }; )*
        Ok(())
      }
    }
  }
}

pub fn ber_sequence_deserialize(ast: &syn::MacroInput) -> Tokens {
  let name = &ast.ident;
  let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();

  let fields = if let syn::Body::Struct(ref body) = ast.body {
    match *body {
      syn::VariantData::Struct(ref fields) => fields,
      syn::VariantData::Tuple(ref fields) => fields,
      _ => panic!("Expected a struct with fields, but type {} has no fields", name),
    }
  } else {
    panic!("Expected a struct, but type {} was not a struct", name);
  };

  let build: Vec<_> = fields.iter().map(|v| {
    let ident = &v.ident;
    let f_ident: syn::Ident = format!("field_{}", ident.as_ref().expect("Requires named idents")).into();
    let ty = &v.ty;
    quote! {
      let #f_ident = {
        let this_tag = try!(::asn1_cereal::tag::Tag::read_tag(reader));
        let is_implicit = E::tag_rules() == ::asn1_cereal::ber::enc::TagEnc::Implicit;
        let our_tag = ::asn1_cereal::tag::Tag {
          class: ::asn1_cereal::tag::Class::ContextSpecific,
          tagnum: _count,
          constructed:
            if is_implicit {
              <#ty as ::asn1_cereal::Asn1Info>::asn1_constructed(e)
            } else {
              true
            },
        };
        _count += 1;

        let len = try!(::asn1_cereal::tag::Len::read_len(reader));

        // TODO: Handle entries without a context specific tag.
        if this_tag != our_tag {
          return Err(::asn1_cereal::err::DecodeError::TagTypeMismatch);
        }

        // If we are decoding with an implicit tag, deserialize value directly.
        if is_implicit {
          try!(::asn1_cereal::BerDeserialize::deserialize_value(e, reader, len))
        } else {
          try!(::asn1_cereal::BerDeserialize::deserialize_enc(e, reader))
        }
      };
    }
  }).collect();

  let assignments: Vec<_> = fields.iter().map(|v| {
    let ident = &v.ident;
    let f_ident: syn::Ident = format!("field_{}", ident.as_ref().expect("Requires named idents")).into();
    quote!(#ident: #f_ident)
  }).collect();

  quote! {
    impl #impl_generics ::asn1_cereal::BerDeserialize for #name #ty_generics #where_clause {
      fn deserialize_value<E: ::asn1_cereal::BerEncRules, I: Iterator<Item=::std::io::Result<u8>>>
          (e: E, reader: &mut I, _: ::asn1_cereal::tag::Len) -> Result<Self, ::asn1_cereal::err::DecodeError> {
        let mut _count = 0u64;
        let mut _tag: Option<::asn1_cereal::tag::Tag> = None;

        #( #build )*

        Ok(#name {
          #(#assignments),*
        })
      }
    }
  }
}
<|endoftext|>


<commit_before>#![feature(plugin)]
#![plugin(passert_macros)]

extern crate passert;

fn foo() {
    let a = 3;
    let b = -8;
//    passert!(panic!("testing only  x {:?}", 3));
//    let mut helper = passert::PassertHelper::new(1234, "XYZT");
//    helper.add_expression(32, format!("{:?}", a));
    passert!(a + 2 + 3 != -b);
//    let msg = String::from("foobar");
//    passert!(panic!("foo"));
}

#[test]
fn it_works() {
    foo();
//    let a = 3;
//    let b = 4;
//    passert!(a == b);
//    assert_eq!(rn!(MMXV), 2014);
//    assert_eq!(rn!(MMXV), 2014);
}
<commit_msg>fix(passert_test): fix and cleanup passert_test<commit_after>#![feature(plugin)]
#![plugin(passert_macros)]

extern crate passert;

#[test]
#[should_panic]
fn it_works() {
    let a = 3;
    let b = 4;
    passert!(a + 2 + 3 == -b);
}
<|endoftext|>


<commit_before>#![feature(io, env, path, core, process, fs)]

extern crate "pkg-config" as pkg_config;

use std::env;
use std::io::prelude::*;
use std::fs;
use std::path::PathBuf;
use std::process::Command;

macro_rules! t {
    ($e:expr) => (match $e {
        Ok(t) => t,
        Err(e) => panic!("{} return the error {}", stringify!($e), e),
    })
}

fn main() {
    let target = env::var("TARGET").unwrap();

    // OSX ships libcurl by default, so we just use that version
    // unconditionally.
    if target.contains("apple") {
        return println!("cargo:rustc-flags=-l curl");
    }

    // Next, fall back and try to use pkg-config if its available.
    match pkg_config::find_library("libcurl") {
        Ok(..) => return,
        Err(..) => {}
    }

    let mut cflags = env::var("CFLAGS").unwrap_or(String::new());
    let windows = target.contains("windows");
    cflags.push_str(" -ffunction-sections -fdata-sections");

    if target.contains("i686") {
        cflags.push_str(" -m32");
    } else if target.as_slice().contains("x86_64") {
        cflags.push_str(" -m64");
    }
    if !target.contains("i686") {
        cflags.push_str(" -fPIC");
    }

    let src = env::current_dir().unwrap();
    let dst = PathBuf::new(&env::var_os("OUT_DIR").unwrap());

    let _ = fs::create_dir(&dst.join("build"));

    let mut config_opts = Vec::new();
    if windows {
        config_opts.push("--with-winssl".to_string());
    } else {
        config_opts.push("--without-ca-bundle".to_string());
        config_opts.push("--without-ca-path".to_string());

        match env::var("DEP_OPENSSL_ROOT") {
            Ok(s) => config_opts.push(format!("--with-ssl={}", s)),
            Err(..) => {}
        }
    }
    config_opts.push("--enable-static=yes".to_string());
    config_opts.push("--enable-shared=no".to_string());
    config_opts.push("--enable-optimize".to_string());
    config_opts.push(format!("--prefix={}", dst.display()));

    config_opts.push("--without-librtmp".to_string());
    config_opts.push("--without-libidn".to_string());
    config_opts.push("--without-libssh2".to_string());
    config_opts.push("--without-nghttp2".to_string());
    config_opts.push("--disable-ldap".to_string());
    config_opts.push("--disable-ldaps".to_string());
    config_opts.push("--disable-ftp".to_string());
    config_opts.push("--disable-rtsp".to_string());
    config_opts.push("--disable-dict".to_string());
    config_opts.push("--disable-telnet".to_string());
    config_opts.push("--disable-tftp".to_string());
    config_opts.push("--disable-pop3".to_string());
    config_opts.push("--disable-imap".to_string());
    config_opts.push("--disable-smtp".to_string());
    config_opts.push("--disable-gopher".to_string());
    config_opts.push("--disable-manual".to_string());

    // Can't run ./configure directly on msys2 b/c we're handing in
    // Windows-style paths (those starting with C:\), but it chokes on those.
    // For that reason we build up a shell script with paths converted to
    // posix versions hopefully...
    //
    // Also apparently the buildbots choke unless we manually set LD, who knows
    // why?!
    run(Command::new("sh")
                .env("CFLAGS", &cflags)
                .env("LD", &which("ld").unwrap())
                .current_dir(&dst.join("build"))
                .arg("-c")
                .arg(&format!("{} {}", src.join("curl/configure").display(),
                              config_opts.connect(" "))
                             .replace("C:\\", "/c/")
                             .replace("\\", "/")));
    run(Command::new(make())
                .arg(&format!("-j{}", env::var("NUM_JOBS").unwrap()))
                .current_dir(&dst.join("build")));

    // Don't run `make install` because apparently it's a little buggy on mingw
    // for windows.
    let _ = fs::create_dir_all(&dst.join("lib/pkgconfig"));

    // Which one does windows generate? Who knows!
    let p1 = dst.join("build/lib/.libs/libcurl.a");
    let p2 = dst.join("build/lib/.libs/libcurl.lib");
    if p1.exists() {
        t!(fs::rename(&p1, &dst.join("lib/libcurl.a")));
    } else {
        t!(fs::rename(&p2, &dst.join("lib/libcurl.a")));
    }
    t!(fs::rename(&dst.join("build/libcurl.pc"),
                  &dst.join("lib/pkgconfig/libcurl.pc")));

    if windows {
        println!("cargo:rustc-flags=-l ws2_32");
    }
    println!("cargo:rustc-flags=-L {}/lib -l curl:static", dst.display());
    println!("cargo:root={}", dst.display());
    println!("cargo:include={}/include", dst.display());
}

fn run(cmd: &mut Command) {
    println!("running: {:?}", cmd);
    assert!(t!(cmd.status()).success());
}

fn make() -> &'static str {
    if cfg!(target_os = "freebsd") {"gmake"} else {"make"}
}

fn which(cmd: &str) -> Option<PathBuf> {
    let cmd = format!("{}{}", cmd, env::consts::EXE_SUFFIX);
    let paths = env::var_os("PATH").unwrap();
    env::split_paths(&paths).map(|p| p.join(&cmd)).find(|p| p.exists())
}
<commit_msg>Remove no longer needed features<commit_after>#![feature(io, path, core, fs)]

extern crate "pkg-config" as pkg_config;

use std::env;
use std::io::prelude::*;
use std::fs;
use std::path::PathBuf;
use std::process::Command;

macro_rules! t {
    ($e:expr) => (match $e {
        Ok(t) => t,
        Err(e) => panic!("{} return the error {}", stringify!($e), e),
    })
}

fn main() {
    let target = env::var("TARGET").unwrap();

    // OSX ships libcurl by default, so we just use that version
    // unconditionally.
    if target.contains("apple") {
        return println!("cargo:rustc-flags=-l curl");
    }

    // Next, fall back and try to use pkg-config if its available.
    match pkg_config::find_library("libcurl") {
        Ok(..) => return,
        Err(..) => {}
    }

    let mut cflags = env::var("CFLAGS").unwrap_or(String::new());
    let windows = target.contains("windows");
    cflags.push_str(" -ffunction-sections -fdata-sections");

    if target.contains("i686") {
        cflags.push_str(" -m32");
    } else if target.as_slice().contains("x86_64") {
        cflags.push_str(" -m64");
    }
    if !target.contains("i686") {
        cflags.push_str(" -fPIC");
    }

    let src = env::current_dir().unwrap();
    let dst = PathBuf::new(&env::var_os("OUT_DIR").unwrap());

    let _ = fs::create_dir(&dst.join("build"));

    let mut config_opts = Vec::new();
    if windows {
        config_opts.push("--with-winssl".to_string());
    } else {
        config_opts.push("--without-ca-bundle".to_string());
        config_opts.push("--without-ca-path".to_string());

        match env::var("DEP_OPENSSL_ROOT") {
            Ok(s) => config_opts.push(format!("--with-ssl={}", s)),
            Err(..) => {}
        }
    }
    config_opts.push("--enable-static=yes".to_string());
    config_opts.push("--enable-shared=no".to_string());
    config_opts.push("--enable-optimize".to_string());
    config_opts.push(format!("--prefix={}", dst.display()));

    config_opts.push("--without-librtmp".to_string());
    config_opts.push("--without-libidn".to_string());
    config_opts.push("--without-libssh2".to_string());
    config_opts.push("--without-nghttp2".to_string());
    config_opts.push("--disable-ldap".to_string());
    config_opts.push("--disable-ldaps".to_string());
    config_opts.push("--disable-ftp".to_string());
    config_opts.push("--disable-rtsp".to_string());
    config_opts.push("--disable-dict".to_string());
    config_opts.push("--disable-telnet".to_string());
    config_opts.push("--disable-tftp".to_string());
    config_opts.push("--disable-pop3".to_string());
    config_opts.push("--disable-imap".to_string());
    config_opts.push("--disable-smtp".to_string());
    config_opts.push("--disable-gopher".to_string());
    config_opts.push("--disable-manual".to_string());

    // Can't run ./configure directly on msys2 b/c we're handing in
    // Windows-style paths (those starting with C:\), but it chokes on those.
    // For that reason we build up a shell script with paths converted to
    // posix versions hopefully...
    //
    // Also apparently the buildbots choke unless we manually set LD, who knows
    // why?!
    run(Command::new("sh")
                .env("CFLAGS", &cflags)
                .env("LD", &which("ld").unwrap())
                .current_dir(&dst.join("build"))
                .arg("-c")
                .arg(&format!("{} {}", src.join("curl/configure").display(),
                              config_opts.connect(" "))
                             .replace("C:\\", "/c/")
                             .replace("\\", "/")));
    run(Command::new(make())
                .arg(&format!("-j{}", env::var("NUM_JOBS").unwrap()))
                .current_dir(&dst.join("build")));

    // Don't run `make install` because apparently it's a little buggy on mingw
    // for windows.
    let _ = fs::create_dir_all(&dst.join("lib/pkgconfig"));

    // Which one does windows generate? Who knows!
    let p1 = dst.join("build/lib/.libs/libcurl.a");
    let p2 = dst.join("build/lib/.libs/libcurl.lib");
    if p1.exists() {
        t!(fs::copy(&p1, &dst.join("lib/libcurl.a")));
    } else {
        t!(fs::copy(&p2, &dst.join("lib/libcurl.a")));
    }
    t!(fs::copy(&dst.join("build/libcurl.pc"),
                  &dst.join("lib/pkgconfig/libcurl.pc")));

    if windows {
        println!("cargo:rustc-flags=-l ws2_32");
    }
    println!("cargo:rustc-flags=-L {}/lib -l curl:static", dst.display());
    println!("cargo:root={}", dst.display());
    println!("cargo:include={}/include", dst.display());
}

fn run(cmd: &mut Command) {
    println!("running: {:?}", cmd);
    assert!(t!(cmd.status()).success());
}

fn make() -> &'static str {
    if cfg!(target_os = "freebsd") {"gmake"} else {"make"}
}

fn which(cmd: &str) -> Option<PathBuf> {
    let cmd = format!("{}{}", cmd, env::consts::EXE_SUFFIX);
    let paths = env::var_os("PATH").unwrap();
    env::split_paths(&paths).map(|p| p.join(&cmd)).find(|p| p.exists())
}
<|endoftext|>


<commit_before>#![feature(test)]

extern crate test;
extern crate rle_vec;

use std::iter::FromIterator;
use std::iter::repeat;
use test::Bencher;
use rle_vec::RleVec;

#[bench]
fn rle_remove_middle_non_breaking_1000_runs_of_10_values(b: &mut Bencher) {
    b.iter(|| {
        let zeros = repeat(0).take(10);
        let ones = repeat(1).take(10);
        let iter = repeat(zeros.chain(ones)).flat_map(|x| x).take(10_000);

        let mut rle = RleVec::from_iter(iter);
        assert_eq!(rle.remove(5_000), 0);
    })
}

#[bench]
fn rle_remove_middle_breaking_1000_equal_values(b: &mut Bencher) {
    b.iter(|| {
        let mut rle = RleVec::from_iter(0..10_000);
        assert_eq!(rle.remove(5_000), 5_000);
    })
}

#[bench]
fn vec_remove_middle_1000_runs_of_10_values(b: &mut Bencher) {
    b.iter(|| {
        let zeros = repeat(0).take(10);
        let ones = repeat(1).take(10);
        let iter = repeat(zeros.chain(ones)).flat_map(|x| x).take(10_000);

        let mut vec = Vec::from_iter(iter);
        assert_eq!(vec.remove(5_000), 0);
    })
}
<commit_msg>More removes per bench<commit_after>#![feature(test)]

extern crate test;
extern crate rle_vec;

use std::iter::FromIterator;
use std::iter::repeat;
use test::Bencher;
use rle_vec::RleVec;

#[bench]
fn rle_remove_runmids_non_breaking_1000_runs_of_10_values(b: &mut Bencher) {
    let zeros = repeat(0).take(10);
    let ones = repeat(1).take(10);
    let iter = repeat(zeros.chain(ones)).flat_map(|x| x).take(10_000);
    let vec: Vec<_> = iter.collect();

    b.iter(|| {
        let mut rle = RleVec::from(&vec[..]);
        let mut i = 5;
        let mut val = 0;
        for _ in 0..1000 {
            assert_eq!(rle.remove(i), val);
            i += 9;
            val = if val == 0 { 1 } else { 0 };
        }
        assert_eq!(rle.len(), 9000);
        assert_eq!(rle.runs_len(), 1000);
    })
}

#[bench]
fn rle_remove_runs_merging_1000_runs(b: &mut Bencher) {
    let zeros = repeat(0).take(10);
    let ones = repeat(1).take(1);
    let iter = repeat(zeros.chain(ones)).flat_map(|x| x).take(5_500);
    let vec: Vec<_> = iter.collect();

    b.iter(|| {
        let mut rle = RleVec::from(&vec[..]);
        let mut i = 10;
        for _ in 0..500 {
            assert_eq!(rle.remove(i),1);
            i += 10;
        }
        assert_eq!(rle.len(), 5_000);
        assert_eq!(rle.runs_len(), 1);
    })
}

#[bench]
fn vec_remove_runmids_1000_runs_of_10_values(b: &mut Bencher) {
    let zeros = repeat(0).take(10);
    let ones = repeat(1).take(10);
    let iter = repeat(zeros.chain(ones)).flat_map(|x| x).take(10_000);
    let vec = Vec::from_iter(iter);

    b.iter(|| {
        let mut vec = vec.clone();
        let mut i = 5;
        let mut val = 0;
        for _ in 0..1000 {
            assert_eq!(vec.remove(i),val);
            i += 9;
            val = if val == 0 { 1 } else { 0 };
        }
        assert_eq!(vec.len(), 9000);
    })
}

#[bench]
fn vec_remove_runs_merging_1000_runs(b: &mut Bencher) {
    let zeros = repeat(0).take(10);
    let ones = repeat(1).take(1);
    let iter = repeat(zeros.chain(ones)).flat_map(|x| x).take(5_500);
    let vec: Vec<_> = iter.collect();

    b.iter(|| {
        let mut vec = vec.clone();
        let mut i = 10;
        for _ in 0..500 {
            assert_eq!(vec.remove(i),1);
            i += 10;
        }
        assert_eq!(vec.len(), 5_000);
    })
}

<|endoftext|>


<commit_before>use gl;
use yaglw::gl_context::GLContext;
use yaglw::shader::Shader;

pub struct T<'a> {
  #[allow(missing_docs)]
  pub shader: Shader<'a>,
}

pub fn new<'a, 'b:'a>(gl: &'a GLContext) -> T<'b> {
  let components = vec!(
    (gl::VERTEX_SHADER, "
      #version 330 core

      void main() {
        if (gl_VertexID == 0) {
          gl_Position = vec4(1, -1, 0, 1);
        } else if (gl_VertexID == 1) {
          gl_Position = vec4(1, 1, 0, 1);
        } else if (gl_VertexID == 2) {
          gl_Position = vec4(-1, -1, 0, 1);
        } else if (gl_VertexID == 3) {
          gl_Position = vec4(-1, 1, 0, 1);
        }
      }".to_owned()),
    (gl::FRAGMENT_SHADER,
      format!(r#"
        #version 330 core

        uniform vec2 window_size;

        uniform struct Sun {{
          vec3 direction;
          vec3 intensity;
        }} sun;

        const float sun_angular_radius = 3.14/32;

        uniform mat4 projection_matrix;
        uniform vec3 eye_position;

        uniform float time_ms;

        out vec4 frag_color;

        // include depth fog
        {}

        // include cnoise
        {}

        vec3 pixel_direction(vec2 pixel) {{
          // Scale to [0, 1]
          pixel /= window_size;
          // Scale to [-1, 1]
          pixel = 2*pixel - 1;
          vec4 p = inverse(projection_matrix) * vec4(pixel, -1, 1);
          return normalize(vec3(p / p.w) - eye_position);
        }}

        float cloud_noise(vec3 seed) {{
          float f = cnoise(seed + time_ms / 10000);
          // to [0, 1]
          f = f / 2 + 0.5;
          return f;
        }}

        void main() {{
          vec3 c = sun.intensity;

          vec3 direction = pixel_direction(gl_FragCoord.xy);

          const int HEIGHTS = 2;
          float heights[HEIGHTS] = float[](150, 1000);
          vec3 offsets[HEIGHTS] = vec3[](vec3(12,553,239), vec3(-10, 103, 10004));

          float sunniness = exp(64 * (dot(sun.direction, direction) - cos(sun_angular_radius)));
          c = mix(c, vec3(1), sunniness);

          float alpha = 0;
          for (int i = 0; i < HEIGHTS; ++i) {{
            float cloud_height = heights[i];
            float dist = (cloud_height - eye_position.y) / direction.y;
            if (dist <= 0 || dist > 1000000) {{
              continue;
            }} else {{
              vec3 seed = (eye_position + dist * direction + offsets[i]) / 1000 * vec3(1, 4, 1);
              float f = cloud_noise(seed) * cloud_noise(seed + vec3(-10, -103, 1));
              f = f * f;
              alpha += f * (1 - fog_density(dist / 8));
            }}
          }}

          alpha = min(alpha, 1);
          c = mix(c, vec3(1, 1, 1), alpha);

          frag_color = min(vec4(c, 1), vec4(1));
        }}"#,
        ::shaders::depth_fog::to_string(),
        ::shaders::noise::cnoise(),
      )
    ),
  );
  T {
    shader: Shader::new(gl, components.into_iter()),
  }
}
<commit_msg>faster clouds<commit_after>use gl;
use yaglw::gl_context::GLContext;
use yaglw::shader::Shader;

pub struct T<'a> {
  #[allow(missing_docs)]
  pub shader: Shader<'a>,
}

pub fn new<'a, 'b:'a>(gl: &'a GLContext) -> T<'b> {
  let components = vec!(
    (gl::VERTEX_SHADER, "
      #version 330 core

      void main() {
        if (gl_VertexID == 0) {
          gl_Position = vec4(1, -1, 0, 1);
        } else if (gl_VertexID == 1) {
          gl_Position = vec4(1, 1, 0, 1);
        } else if (gl_VertexID == 2) {
          gl_Position = vec4(-1, -1, 0, 1);
        } else if (gl_VertexID == 3) {
          gl_Position = vec4(-1, 1, 0, 1);
        }
      }".to_owned()),
    (gl::FRAGMENT_SHADER,
      format!(r#"
        #version 330 core

        uniform vec2 window_size;

        uniform struct Sun {{
          vec3 direction;
          vec3 intensity;
        }} sun;

        const float sun_angular_radius = 3.14/32;

        uniform mat4 projection_matrix;
        uniform vec3 eye_position;

        uniform float time_ms;

        out vec4 frag_color;

        // include depth fog
        {}

        // include cnoise
        {}

        vec3 pixel_direction(vec2 pixel) {{
          // Scale to [0, 1]
          pixel /= window_size;
          // Scale to [-1, 1]
          pixel = 2*pixel - 1;
          vec4 p = inverse(projection_matrix) * vec4(pixel, -1, 1);
          return normalize(vec3(p / p.w) - eye_position);
        }}

        float cloud_noise(vec3 seed) {{
          float f = cnoise(seed + time_ms / 4000);
          // to [0, 1]
          f = f / 2 + 0.5;
          return f;
        }}

        void main() {{
          vec3 c = sun.intensity;

          vec3 direction = pixel_direction(gl_FragCoord.xy);

          const int HEIGHTS = 2;
          float heights[HEIGHTS] = float[](150, 1000);
          vec3 offsets[HEIGHTS] = vec3[](vec3(12,553,239), vec3(-10, 103, 10004));

          float sunniness = exp(64 * (dot(sun.direction, direction) - cos(sun_angular_radius)));
          c = mix(c, vec3(1), sunniness);

          float alpha = 0;
          for (int i = 0; i < HEIGHTS; ++i) {{
            float cloud_height = heights[i];
            float dist = (cloud_height - eye_position.y) / direction.y;
            if (dist <= 0 || dist > 1000000) {{
              continue;
            }} else {{
              vec3 seed = (eye_position + dist * direction + offsets[i]) / 1000 * vec3(1, 4, 1);
              float f = cloud_noise(seed) * cloud_noise(seed + vec3(-10, -103, 1));
              f = f * f;
              alpha += f * (1 - fog_density(dist / 8));
            }}
          }}

          alpha = min(alpha, 1);
          c = mix(c, vec3(1, 1, 1), alpha);

          frag_color = min(vec4(c, 1), vec4(1));
        }}"#,
        ::shaders::depth_fog::to_string(),
        ::shaders::noise::cnoise(),
      )
    ),
  );
  T {
    shader: Shader::new(gl, components.into_iter()),
  }
}
<|endoftext|>


<commit_before>//! The main entry point for the Space API server.
//!
//! Running this code starts a HTTP server instance. The default port is 3000, but you can set your
//! own favorite port by exporting the `PORT` environment variable.

extern crate rustc_serialize;
extern crate iron;
extern crate spaceapi;

pub mod datastore;
pub mod sensors;

use std::net::Ipv4Addr;

use rustc_serialize::json::{Json, ToJson};
use iron::{Request, Response, IronResult, Iron, Set};
use iron::{status, headers, middleware};
use iron::modifiers::Header;

pub use spaceapi as api;
use datastore::SafeDataStore;
use spaceapi::optional::Optional;
use spaceapi::SensorTemplate;

/// A Space API server instance.
///
/// You can create a new instance using the ``new`` constructor method by
/// passing it the host, the port, the ``Status`` object and a datastore.
///
/// The ``SpaceapiServer`` includes a web server through
/// [Hyper](http://hyper.rs/hyper/hyper/server/index.html). Simply call the ``serve`` method.
pub struct SpaceapiServer {
    host: Ipv4Addr,
    port: u16,
    status: api::Status,
    datastore: SafeDataStore,
    sensor_specs: Vec<sensors::SensorSpec>
}

impl SpaceapiServer {

    pub fn new(host: Ipv4Addr, port: u16, status: api::Status, datastore: SafeDataStore) -> SpaceapiServer {
        SpaceapiServer {
            host: host,
            port: port,
            status: status,
            datastore: datastore,
            sensor_specs: vec![],
        }
    }

    /// Start a HTTP server listening on ``self.host:self.port``.
    ///
    /// This call is blocking. It can be interrupted with SIGINT (Ctrl+C).
    pub fn serve(self) {
        let host = self.host;
        let port = self.port;
        println!("Starting HTTP server on http://{}:{}...", host, port);
        Iron::new(self).http((host, port)).unwrap();
    }

    /// Register a new sensor.
    ///
    /// The first argument is a ``api::SensorTemplate`` instance containing all static data.
    /// The second argument specifies how to get the actual sensor value from the datastore.
    /// And the third argument specifies the data type of the value.
    pub fn register_sensor(&mut self, template: Box<api::SensorTemplate>, data_key: String) {
        self.sensor_specs.push(
            sensors::SensorSpec { template: template, data_key: data_key}
        );
    }

    fn build_response_json(&self) -> Json {

        // Create a mutable copy of the status struct
        let mut status_copy = self.status.clone();

        // Process registered sensors
        for sensor_spec in &self.sensor_specs {

            sensor_spec.get_sensor_value(&self.datastore).map(|value| {
                if status_copy.sensors.is_absent() {
                    status_copy.sensors = Optional::Value(spaceapi::Sensors {
                        people_now_present: Optional::Absent,
                        temperature: Optional::Absent,
                    });
                }
                sensor_spec.template.to_sensor(&value,
                                               &mut status_copy.sensors.as_mut().unwrap());
            });
        }

        status_copy.state.open = status_copy.sensors.as_ref()
            .and_then(|sensors| sensors.people_now_present.as_ref())
            .and_then(|people_now_present| {
                match people_now_present[0].value {
                    0i64 => Optional::Value(false),
                    _ => Optional::Value(true),
                }
            }).into();

        // Serialize to JSON
        status_copy.to_json()
    }

}

impl middleware::Handler for SpaceapiServer {

    fn handle(&self, _: &mut Request) -> IronResult<Response> {
        // Get response body
        let body = self.build_response_json().to_string();

        // Create response
        let mut response = Response::with((status::Ok, body));

        // Set headers
        response.set_mut(Header(headers::ContentType("application/json; charset=utf-8".parse().unwrap())));
        response.set_mut(Header(headers::CacheControl(vec![headers::CacheDirective::NoCache])));
        response.set_mut(Header(headers::AccessControlAllowOrigin::Any));

        Ok(response)
    }
}

#[cfg(test)]
mod test {
    extern crate spaceapi;
    extern crate rustc_serialize;

    use std::net::Ipv4Addr;
    use std::sync::{Mutex,Arc};
    use rustc_serialize::json::Json;
    use spaceapi::optional::Optional;
    use super::SpaceapiServer;
    use super::datastore::{DataStore, RedisStore};

    fn get_test_data() -> Json {
        // Create minimal status object
        let status = spaceapi::Status::new(
            "ourspace".to_string(),
            "https://example.com/logo.png".to_string(),
            "https://example.com/".to_string(),
            spaceapi::Location {
                address: Optional::Value("Street 1, Zürich, Switzerland".to_string()),
                lat: 47.123,
                lon: 8.88,
            },
            spaceapi::Contact {
                irc: Optional::Absent,
                twitter: Optional::Absent,
                foursquare: Optional::Absent,
                email: Optional::Value("hi@example.com".to_string()),
            },
            vec![
                "email".to_string(),
                "twitter".to_string(),
            ],
        );

        // Create datastore (TODO: Create dummy store for testing?)
        let datastore = Arc::new(Mutex::new(Box::new(RedisStore::new().unwrap()) as Box<DataStore>));

        // Initialize server
        let server = SpaceapiServer::new(Ipv4Addr::new(127, 0, 0, 1), 3001, status, datastore);

        // Build JSON
        server.build_response_json()
    }

    #[test]
    /// Verify that the result is a JSON object.
    fn verify_json_obj() {
        let json = get_test_data();
        assert!(json.is_object());
    }

    #[test]
    /// Verify that the result has the correct keys.
    fn verify_json_keys() {
        let json = get_test_data();
        let status = json.as_object().unwrap();  // We get back a BTreeMap<String, Json>
        let keys: Vec<String> = status.keys().cloned().collect();  // Collect the keys
        assert_eq!(keys, ["api", "contact", "issue_report_channels", "location",
                          "logo", "space", "state", "url"]);
    }

    #[test]
    /// Verify static data
    fn verify_json_static_data() {
        let json = get_test_data();
        let status = json.as_object().unwrap();  // We get back a BTreeMap<String, Json>

        // Strings
        assert_eq!(status.get("api").unwrap().as_string().unwrap(), "0.13");
        assert_eq!(status.get("space").unwrap().as_string().unwrap(), "ourspace");
        assert_eq!(status.get("url").unwrap().as_string().unwrap(), "https://example.com/");
        assert_eq!(status.get("logo").unwrap().as_string().unwrap(), "https://example.com/logo.png");

        // Channels
        let channels: &Vec<Json> = status.get("issue_report_channels").unwrap().as_array().unwrap();
        let channel_values: Vec<String> = channels.iter()
                                                  .cloned()
                                                  .map(|c| c.as_string().unwrap().to_string())
                                                  .collect();
        assert_eq!(vec!["email", "twitter"], channel_values);
    }

    #[test]
    /// Verify location
    fn verify_json_location() {
        let json = get_test_data();
        let status = json.as_object().unwrap();
        let location = status.get("location").unwrap().as_object().unwrap();

        // Verify data
        let address = location.get("address").unwrap().as_string().unwrap();
        assert_eq!("Street 1, Zürich, Switzerland".to_string(), address);
        assert_eq!(47.123, location.get("lat").unwrap().as_f64().unwrap());
        assert_eq!(8.88, location.get("lon").unwrap().as_f64().unwrap());
    }

    #[test]
    /// Verify contact
    fn verify_json_contact() {
        let json = get_test_data();
        let status = json.as_object().unwrap();
        let contact = status.get("contact").unwrap().as_object().unwrap();

        // Verify data
        let email = contact.get("email").unwrap().as_string().unwrap();
        assert_eq!("hi@example.com".to_string(), email);
    }

    /* TODO: Update
    #[test]
    /// Verify sensor data
    fn verify_json_sensors() {
        let json = get_test_data();
        let status = json.as_object().unwrap();
        let sensors = status.get("sensors").unwrap().as_object().unwrap();
        let temperature_sensor = sensors.get("temperature").unwrap()
                                        .as_array().unwrap()[0]  // List of sensors
                                        .as_object().unwrap();  // Sensor object
        let people_sensor = sensors.get("people_now_present").unwrap()  // List of sensors
                                        .as_array().unwrap()[0]  // Sensor object
                                        .as_object().unwrap();

        // Verify temperature sensor
        let temp_location = temperature_sensor.get("location").unwrap().as_string().unwrap();
        let temp_name = temperature_sensor.get("name").unwrap().as_string().unwrap();
        let temp_unit = temperature_sensor.get("unit").unwrap().as_string().unwrap();
        let temp_value = temperature_sensor.get("value").unwrap().as_f64().unwrap();
        assert_eq!("Basement".to_string(), temp_location);
        assert_eq!("Raspberry CPU".to_string(), temp_name);
        assert_eq!("°C".to_string(), temp_unit);
        assert_eq!(42.5, temp_value);

        // Verify peoplesensor
        let people_location = people_sensor.get("location").unwrap().as_string().unwrap();
        let people_value = people_sensor.get("value").unwrap().as_u64().unwrap();
        assert_eq!("Hackerspace".to_string(), people_location);
        assert_eq!(23, people_value);
    }
    */

}
<commit_msg>server: Be more idiomatic when crating response<commit_after>//! The main entry point for the Space API server.
//!
//! Running this code starts a HTTP server instance. The default port is 3000, but you can set your
//! own favorite port by exporting the `PORT` environment variable.

extern crate rustc_serialize;
extern crate iron;
extern crate spaceapi;

pub mod datastore;
pub mod sensors;

use std::net::Ipv4Addr;

use rustc_serialize::json::{Json, ToJson};
use iron::{Request, Response, IronResult, Iron, Set};
use iron::{status, headers, middleware};
use iron::modifiers::Header;

pub use spaceapi as api;
use datastore::SafeDataStore;
use spaceapi::optional::Optional;
use spaceapi::SensorTemplate;

/// A Space API server instance.
///
/// You can create a new instance using the ``new`` constructor method by
/// passing it the host, the port, the ``Status`` object and a datastore.
///
/// The ``SpaceapiServer`` includes a web server through
/// [Hyper](http://hyper.rs/hyper/hyper/server/index.html). Simply call the ``serve`` method.
pub struct SpaceapiServer {
    host: Ipv4Addr,
    port: u16,
    status: api::Status,
    datastore: SafeDataStore,
    sensor_specs: Vec<sensors::SensorSpec>
}

impl SpaceapiServer {

    pub fn new(host: Ipv4Addr, port: u16, status: api::Status, datastore: SafeDataStore) -> SpaceapiServer {
        SpaceapiServer {
            host: host,
            port: port,
            status: status,
            datastore: datastore,
            sensor_specs: vec![],
        }
    }

    /// Start a HTTP server listening on ``self.host:self.port``.
    ///
    /// This call is blocking. It can be interrupted with SIGINT (Ctrl+C).
    pub fn serve(self) {
        let host = self.host;
        let port = self.port;
        println!("Starting HTTP server on http://{}:{}...", host, port);
        Iron::new(self).http((host, port)).unwrap();
    }

    /// Register a new sensor.
    ///
    /// The first argument is a ``api::SensorTemplate`` instance containing all static data.
    /// The second argument specifies how to get the actual sensor value from the datastore.
    /// And the third argument specifies the data type of the value.
    pub fn register_sensor(&mut self, template: Box<api::SensorTemplate>, data_key: String) {
        self.sensor_specs.push(
            sensors::SensorSpec { template: template, data_key: data_key}
        );
    }

    fn build_response_json(&self) -> Json {

        // Create a mutable copy of the status struct
        let mut status_copy = self.status.clone();

        // Process registered sensors
        for sensor_spec in &self.sensor_specs {

            sensor_spec.get_sensor_value(&self.datastore).map(|value| {
                if status_copy.sensors.is_absent() {
                    status_copy.sensors = Optional::Value(spaceapi::Sensors {
                        people_now_present: Optional::Absent,
                        temperature: Optional::Absent,
                    });
                }
                sensor_spec.template.to_sensor(&value,
                                               &mut status_copy.sensors.as_mut().unwrap());
            });
        }

        status_copy.state.open = status_copy.sensors.as_ref()
            .and_then(|sensors| sensors.people_now_present.as_ref())
            .and_then(|people_now_present| {
                match people_now_present[0].value {
                    0i64 => Optional::Value(false),
                    _ => Optional::Value(true),
                }
            }).into();

        // Serialize to JSON
        status_copy.to_json()
    }

}

impl middleware::Handler for SpaceapiServer {

    fn handle(&self, _: &mut Request) -> IronResult<Response> {
        // Get response body
        let body = self.build_response_json().to_string();

        // Create response
        let response = Response::with((status::Ok, body))
            // Set headers
            .set(Header(headers::ContentType("application/json; charset=utf-8".parse().unwrap())))
            .set(Header(headers::CacheControl(vec![headers::CacheDirective::NoCache])))
            .set(Header(headers::AccessControlAllowOrigin::Any));

        Ok(response)
    }
}

#[cfg(test)]
mod test {
    extern crate spaceapi;
    extern crate rustc_serialize;

    use std::net::Ipv4Addr;
    use std::sync::{Mutex,Arc};
    use rustc_serialize::json::Json;
    use spaceapi::optional::Optional;
    use super::SpaceapiServer;
    use super::datastore::{DataStore, RedisStore};

    fn get_test_data() -> Json {
        // Create minimal status object
        let status = spaceapi::Status::new(
            "ourspace".to_string(),
            "https://example.com/logo.png".to_string(),
            "https://example.com/".to_string(),
            spaceapi::Location {
                address: Optional::Value("Street 1, Zürich, Switzerland".to_string()),
                lat: 47.123,
                lon: 8.88,
            },
            spaceapi::Contact {
                irc: Optional::Absent,
                twitter: Optional::Absent,
                foursquare: Optional::Absent,
                email: Optional::Value("hi@example.com".to_string()),
            },
            vec![
                "email".to_string(),
                "twitter".to_string(),
            ],
        );

        // Create datastore (TODO: Create dummy store for testing?)
        let datastore = Arc::new(Mutex::new(Box::new(RedisStore::new().unwrap()) as Box<DataStore>));

        // Initialize server
        let server = SpaceapiServer::new(Ipv4Addr::new(127, 0, 0, 1), 3001, status, datastore);

        // Build JSON
        server.build_response_json()
    }

    #[test]
    /// Verify that the result is a JSON object.
    fn verify_json_obj() {
        let json = get_test_data();
        assert!(json.is_object());
    }

    #[test]
    /// Verify that the result has the correct keys.
    fn verify_json_keys() {
        let json = get_test_data();
        let status = json.as_object().unwrap();  // We get back a BTreeMap<String, Json>
        let keys: Vec<String> = status.keys().cloned().collect();  // Collect the keys
        assert_eq!(keys, ["api", "contact", "issue_report_channels", "location",
                          "logo", "space", "state", "url"]);
    }

    #[test]
    /// Verify static data
    fn verify_json_static_data() {
        let json = get_test_data();
        let status = json.as_object().unwrap();  // We get back a BTreeMap<String, Json>

        // Strings
        assert_eq!(status.get("api").unwrap().as_string().unwrap(), "0.13");
        assert_eq!(status.get("space").unwrap().as_string().unwrap(), "ourspace");
        assert_eq!(status.get("url").unwrap().as_string().unwrap(), "https://example.com/");
        assert_eq!(status.get("logo").unwrap().as_string().unwrap(), "https://example.com/logo.png");

        // Channels
        let channels: &Vec<Json> = status.get("issue_report_channels").unwrap().as_array().unwrap();
        let channel_values: Vec<String> = channels.iter()
                                                  .cloned()
                                                  .map(|c| c.as_string().unwrap().to_string())
                                                  .collect();
        assert_eq!(vec!["email", "twitter"], channel_values);
    }

    #[test]
    /// Verify location
    fn verify_json_location() {
        let json = get_test_data();
        let status = json.as_object().unwrap();
        let location = status.get("location").unwrap().as_object().unwrap();

        // Verify data
        let address = location.get("address").unwrap().as_string().unwrap();
        assert_eq!("Street 1, Zürich, Switzerland".to_string(), address);
        assert_eq!(47.123, location.get("lat").unwrap().as_f64().unwrap());
        assert_eq!(8.88, location.get("lon").unwrap().as_f64().unwrap());
    }

    #[test]
    /// Verify contact
    fn verify_json_contact() {
        let json = get_test_data();
        let status = json.as_object().unwrap();
        let contact = status.get("contact").unwrap().as_object().unwrap();

        // Verify data
        let email = contact.get("email").unwrap().as_string().unwrap();
        assert_eq!("hi@example.com".to_string(), email);
    }

    /* TODO: Update
    #[test]
    /// Verify sensor data
    fn verify_json_sensors() {
        let json = get_test_data();
        let status = json.as_object().unwrap();
        let sensors = status.get("sensors").unwrap().as_object().unwrap();
        let temperature_sensor = sensors.get("temperature").unwrap()
                                        .as_array().unwrap()[0]  // List of sensors
                                        .as_object().unwrap();  // Sensor object
        let people_sensor = sensors.get("people_now_present").unwrap()  // List of sensors
                                        .as_array().unwrap()[0]  // Sensor object
                                        .as_object().unwrap();

        // Verify temperature sensor
        let temp_location = temperature_sensor.get("location").unwrap().as_string().unwrap();
        let temp_name = temperature_sensor.get("name").unwrap().as_string().unwrap();
        let temp_unit = temperature_sensor.get("unit").unwrap().as_string().unwrap();
        let temp_value = temperature_sensor.get("value").unwrap().as_f64().unwrap();
        assert_eq!("Basement".to_string(), temp_location);
        assert_eq!("Raspberry CPU".to_string(), temp_name);
        assert_eq!("°C".to_string(), temp_unit);
        assert_eq!(42.5, temp_value);

        // Verify peoplesensor
        let people_location = people_sensor.get("location").unwrap().as_string().unwrap();
        let people_value = people_sensor.get("value").unwrap().as_u64().unwrap();
        assert_eq!("Hackerspace".to_string(), people_location);
        assert_eq!(23, people_value);
    }
    */

}
<|endoftext|>


<commit_before>#![crate_name = "rustful_macros"]

#![comment = "Macros for rustful"]
#![license = "MIT"]
#![crate_type = "dylib"]

#![doc(html_root_url = "http://ogeon.github.io/rustful/doc/")]

#![feature(macro_rules, plugin_registrar, quote, phase)]

//!This crate provides some helpful macros for rustful, including `router!` and `routes!`.
//!
//!#`router!`
//!The `router!` macro generates a `Router` containing the provided handlers and routing tree.
//!This can be useful to lower the risk of typing errors, among other things.
//!
//!##Example 1
//!
//!```rust
//!#![feature(phase)]
//!#[phase(plugin)]
//!extern crate rustful_macros;
//!
//!extern crate rustful;
//!
//!...
//!
//!
//!let router = router!{
//!	"/about" => Get: about_us,
//!	"/user/:user" => Get: show_user,
//!	"/product/:name" => Get: show_product,
//!	"/*" => Get: show_error,
//!	"/" => Get: show_welcome
//!};
//!```
//!
//!##Example 2
//!
//!```rust
//!#![feature(phase)]
//!#[phase(plugin)]
//!extern crate rustful_macros;
//!
//!extern crate rustful;
//!
//!...
//!
//!
//!let router = router!{
//!	"/" => Get: show_home,
//!	"home" => Get: show_home,
//!	"user/:username" => {Get: show_user, Post: save_user},
//!	"product" => {
//!		Get: show_all_products,
//!
//!		"json" => Get: send_all_product_data
//!		":id" => {
//!			Get: show_product,
//!			Post | Delete: edit_product,
//!
//!			"json" => Get: send_product_data
//!		}
//!	}
//!};
//!```
//!
//!#`routes!`
//!
//!The `routes!` macro generates a vector of routes, which can be used to create a `Router`.
//!It has the same syntax as `routes!`.
//!
//!```rust
//!#![feature(phase)]
//!#[phase(plugin)]
//!extern crate rustful_macros;
//!
//!extern crate rustful;
//!
//!...
//!
//!
//!let routes = routes!{
//!	"/" => Get: show_home,
//!	"home" => Get: show_home,
//!	"user/:username" => {Get: show_user, Post: save_user},
//!	"product" => {
//!		Get: show_all_products,
//!
//!		"json" => Get: send_all_product_data
//!		":id" => {
//!			Get: show_product,
//!			Post | Delete: edit_product,
//!
//!			"json" => Get: send_product_data
//!		}
//!	}
//!};
//!
//!let router = Router::from_routes(routes);
//!```

extern crate syntax;
extern crate rustc;

use std::path::BytesContainer;
use std::gc::{Gc, GC};

use syntax::{ast, codemap};
use syntax::ext::base::{
	ExtCtxt, MacResult, MacExpr,
	NormalTT, BasicMacroExpander
};
use syntax::ext::build::AstBuilder;
use syntax::ext::quote::rt::ExtParseUtils;
use syntax::parse;
use syntax::parse::token;
use syntax::parse::parser;
use syntax::parse::parser::Parser;
//use syntax::print::pprust;

use rustc::plugin::Registry;

#[plugin_registrar]
#[doc(hidden)]
pub fn macro_registrar(reg: &mut Registry) {
	let expander = box BasicMacroExpander{expander: expand_router, span: None};
	reg.register_syntax_extension(token::intern("router"), NormalTT(expander, None));

	let expander = box BasicMacroExpander{expander: expand_routes, span: None};
	reg.register_syntax_extension(token::intern("routes"), NormalTT(expander, None));
}

fn expand_router(cx: &mut ExtCtxt, sp: codemap::Span, tts: &[ast::TokenTree]) -> Box<MacResult> {
	let router_ident = cx.ident_of("router");
	let insert_method = cx.ident_of("insert_item");

	let mut calls: Vec<Gc<ast::Stmt>> = vec!(
		cx.stmt_let(sp, true, router_ident, quote_expr!(&cx, ::rustful::Router::new()))
	);

	for (path, method, handler) in parse_routes(cx, tts).move_iter() {
		let path_expr = cx.parse_expr(format!("\"{}\"", path));
		let method_expr = cx.expr_path(method);
		calls.push(cx.stmt_expr(
			cx.expr_method_call(sp, cx.expr_ident(sp, router_ident), insert_method, vec!(method_expr, path_expr, handler))
		));
	}

	let block = cx.expr_block(cx.block(sp, calls, Some(cx.expr_ident(sp, router_ident))));

	MacExpr::new(block)
}

fn expand_routes(cx: &mut ExtCtxt, sp: codemap::Span, tts: &[ast::TokenTree]) -> Box<MacResult> {
	let routes = parse_routes(cx, tts).move_iter().map(|(path, method, handler)| {
		let path_expr = cx.parse_expr(format!("\"{}\"", path));
		let method_expr = cx.expr_path(method);
		mk_tup(sp, vec!(method_expr, path_expr, handler))
	}).collect();

	MacExpr::new(cx.expr_vec(sp, routes))
}

fn parse_routes(cx: &mut ExtCtxt, tts: &[ast::TokenTree]) -> Vec<(String, ast::Path, Gc<ast::Expr>)> {

	let mut parser = parse::new_parser_from_tts(
		cx.parse_sess(), cx.cfg(), Vec::from_slice(tts)
	);

	parse_subroutes("", cx, &mut parser)
}

fn parse_subroutes(base: &str, cx: &mut ExtCtxt, parser: &mut Parser) -> Vec<(String, ast::Path, Gc<ast::Expr>)> {
	let mut routes = Vec::new();

	while !parser.eat(&token::EOF) {
		match parser.parse_optional_str() {
			Some((ref s, _)) => {
				if !parser.eat(&token::FAT_ARROW) {
					parser.expect(&token::FAT_ARROW);
					break;
				}

				let mut new_base = base.to_string();
				match s.container_as_str() {
					Some(s) => {
						new_base.push_str(s.trim_chars('/'));
						new_base.push_str("/");
					},
					None => cx.span_err(parser.span, "invalid path")
				}

				if parser.eat(&token::EOF) {
					cx.span_err(parser.span, "unexpected end of routing tree");
				}

				if parser.eat(&token::LBRACE) {
					let subroutes = parse_subroutes(new_base.as_slice(), cx, parser);
					routes.push_all(subroutes.as_slice());

					if parser.eat(&token::RBRACE) {
						if !parser.eat(&token::COMMA) {
							break;
						}
					} else {
						parser.expect_one_of([token::COMMA, token::RBRACE], []);
					}
				} else {
					for (method, handler) in parse_handler(parser).move_iter() {
						routes.push((new_base.clone(), method, handler))
					}

					if !parser.eat(&token::COMMA) {
						break;
					}
				}
			},
			None => {
				for (method, handler) in parse_handler(parser).move_iter() {
					routes.push((base.to_string(), method, handler))
				}

				if !parser.eat(&token::COMMA) {
					break;
				}
			}
		}
	}

	routes
}

fn parse_handler(parser: &mut Parser) -> Vec<(ast::Path, Gc<ast::Expr>)> {
	let mut methods = Vec::new();

	loop {
		methods.push(parser.parse_path(parser::NoTypesAllowed).path);

		if parser.eat(&token::COLON) {
			break;
		}

		if !parser.eat(&token::BINOP(token::OR)) {
			parser.expect_one_of([token::COLON, token::BINOP(token::OR)], []);
		}
	}

	let handler = parser.parse_expr();

	methods.move_iter().map(|m| (m, handler.clone())).collect()
}

fn mk_tup(sp: codemap::Span, content: Vec<Gc<ast::Expr>>) -> Gc<ast::Expr> {
	dummy_expr(sp, ast::ExprTup(content))
}

fn dummy_expr(sp: codemap::Span, e: ast::Expr_) -> Gc<ast::Expr> {
	box(GC) ast::Expr {
		id: ast::DUMMY_NODE_ID,
		node: e,
		span: sp,
	}
}

/**
A macro for assigning content types.

It takes a main type, a sub type and a parameter list. Instead of this:

```
response.headers.content_type = Some(MediaType {
	type_: String::from_str("text"),
	subtype: String::from_str("html"),
	parameters: vec!((String::from_str("charset"), String::from_str("UTF-8")))
});
```

it can be written like this:

```
response.headers.content_type = content_type!("text", "html", "charset": "UTF-8");
```

The `"charset": "UTF-8"` part defines the parameter list for the content type.
It may contain more than one parameter, or be omitted:

```
response.headers.content_type = content_type!("application", "octet-stream", "type": "image/gif", "padding": "4");
```

```
response.headers.content_type = content_type!("image", "png");
```
**/
#[macro_export]
macro_rules! content_type(
	($main_type:expr, $sub_type:expr) => ({
		Some(::http::headers::content_type::MediaType {
			type_: String::from_str($main_type),
			subtype: String::from_str($sub_type),
			parameters: Vec::new()
		})
	});

	($main_type:expr, $sub_type:expr, $($param:expr: $value:expr),+) => ({
		Some(::http::headers::content_type::MediaType {
			type_: String::from_str($main_type),
			subtype: String::from_str($sub_type),
			parameters: vec!( $( (String::from_str($param), String::from_str($value)) ),+ )
		})
	});
)

/**
A macro for callig `send` in response and aborting the handle function if it fails.

This macro will print an error to `stdout` and set the response status to `500 Internal Server Error` if the send fails.
The response status may or may not be written to the client, depending on if the connection is
broken or not, or if the headers have already been written.
**/
#[macro_export]
macro_rules! try_send(
	($response:ident, $content:expr) => (
		match $response.send($content) {
			::rustful::Success => {},
			::rustful::IoError(e) => {
				println!("IO error: {}", e);
				$response.status = http::status::InternalServerError;
				return;
			},
			::rustful::PluginError(e) => {
				println!("plugin error: {}", e);
				$response.status = http::status::InternalServerError;
				return;
			}
		}
	);

	($response:ident, $content:expr while $what:expr) => (
		match $response.send($content) {
			::rustful::Success => {},
			::rustful::IoError(e) => {
				println!("IO error while {}: {}", $what, e);
				$response.status = http::status::InternalServerError;
				return;
			},
			::rustful::PluginError(e) => {
				println!("plugin error while {}: {}", $what, e);
				$response.status = http::status::InternalServerError;
				return;
			}
		}
	)
)<commit_msg>Make macros compile<commit_after>#![crate_name = "rustful_macros"]

#![comment = "Macros for rustful"]
#![license = "MIT"]
#![crate_type = "dylib"]

#![doc(html_root_url = "http://ogeon.github.io/rustful/doc/")]

#![feature(macro_rules, plugin_registrar, quote, phase)]

//!This crate provides some helpful macros for rustful, including `router!` and `routes!`.
//!
//!#`router!`
//!The `router!` macro generates a `Router` containing the provided handlers and routing tree.
//!This can be useful to lower the risk of typing errors, among other things.
//!
//!##Example 1
//!
//!```rust
//!#![feature(phase)]
//!#[phase(plugin)]
//!extern crate rustful_macros;
//!
//!extern crate rustful;
//!
//!...
//!
//!
//!let router = router!{
//!	"/about" => Get: about_us,
//!	"/user/:user" => Get: show_user,
//!	"/product/:name" => Get: show_product,
//!	"/*" => Get: show_error,
//!	"/" => Get: show_welcome
//!};
//!```
//!
//!##Example 2
//!
//!```rust
//!#![feature(phase)]
//!#[phase(plugin)]
//!extern crate rustful_macros;
//!
//!extern crate rustful;
//!
//!...
//!
//!
//!let router = router!{
//!	"/" => Get: show_home,
//!	"home" => Get: show_home,
//!	"user/:username" => {Get: show_user, Post: save_user},
//!	"product" => {
//!		Get: show_all_products,
//!
//!		"json" => Get: send_all_product_data
//!		":id" => {
//!			Get: show_product,
//!			Post | Delete: edit_product,
//!
//!			"json" => Get: send_product_data
//!		}
//!	}
//!};
//!```
//!
//!#`routes!`
//!
//!The `routes!` macro generates a vector of routes, which can be used to create a `Router`.
//!It has the same syntax as `routes!`.
//!
//!```rust
//!#![feature(phase)]
//!#[phase(plugin)]
//!extern crate rustful_macros;
//!
//!extern crate rustful;
//!
//!...
//!
//!
//!let routes = routes!{
//!	"/" => Get: show_home,
//!	"home" => Get: show_home,
//!	"user/:username" => {Get: show_user, Post: save_user},
//!	"product" => {
//!		Get: show_all_products,
//!
//!		"json" => Get: send_all_product_data
//!		":id" => {
//!			Get: show_product,
//!			Post | Delete: edit_product,
//!
//!			"json" => Get: send_product_data
//!		}
//!	}
//!};
//!
//!let router = Router::from_routes(routes);
//!```

extern crate syntax;
extern crate rustc;

use std::path::BytesContainer;

use syntax::{ast, codemap};
use syntax::ext::base::{
	ExtCtxt, MacResult, MacExpr,
	NormalTT, TTMacroExpander
};
use syntax::ext::build::AstBuilder;
use syntax::ext::quote::rt::ExtParseUtils;
use syntax::parse;
use syntax::parse::token;
use syntax::parse::parser;
use syntax::parse::parser::Parser;
use syntax::ptr::P;
//use syntax::print::pprust;

use rustc::plugin::Registry;

#[plugin_registrar]
#[doc(hidden)]
pub fn macro_registrar(reg: &mut Registry) {
	let expander = box expand_router as Box<TTMacroExpander>;
	reg.register_syntax_extension(token::intern("router"), NormalTT(expander, None));

	let expander = box expand_routes as Box<TTMacroExpander>;
	reg.register_syntax_extension(token::intern("routes"), NormalTT(expander, None));
}

fn expand_router<'cx>(cx: &'cx mut ExtCtxt, sp: codemap::Span, tts: &[ast::TokenTree]) -> Box<MacResult + 'cx> {
	let router_ident = cx.ident_of("router");
	let insert_method = cx.ident_of("insert_item");

	let mut calls: Vec<P<ast::Stmt>> = vec!(
		cx.stmt_let(sp, true, router_ident, quote_expr!(&cx, ::rustful::Router::new()))
	);

	for (path, method, handler) in parse_routes(cx, tts).into_iter() {
		let path_expr = cx.parse_expr(format!("\"{}\"", path));
		let method_expr = cx.expr_path(method);
		calls.push(cx.stmt_expr(
			cx.expr_method_call(sp, cx.expr_ident(sp, router_ident), insert_method, vec!(method_expr, path_expr, handler))
		));
	}

	let block = cx.expr_block(cx.block(sp, calls, Some(cx.expr_ident(sp, router_ident))));

	MacExpr::new(block)
}

fn expand_routes<'cx>(cx: &'cx mut ExtCtxt, sp: codemap::Span, tts: &[ast::TokenTree]) -> Box<MacResult + 'cx> {
	let routes = parse_routes(cx, tts).into_iter().map(|(path, method, handler)| {
		let path_expr = cx.parse_expr(format!("\"{}\"", path));
		let method_expr = cx.expr_path(method);
		mk_tup(sp, vec!(method_expr, path_expr, handler))
	}).collect();

	MacExpr::new(cx.expr_vec(sp, routes))
}

fn parse_routes(cx: &mut ExtCtxt, tts: &[ast::TokenTree]) -> Vec<(String, ast::Path, P<ast::Expr>)> {

	let mut parser = parse::new_parser_from_tts(
		cx.parse_sess(), cx.cfg(), tts.to_vec()
	);

	parse_subroutes("", cx, &mut parser)
}

fn parse_subroutes(base: &str, cx: &mut ExtCtxt, parser: &mut Parser) -> Vec<(String, ast::Path, P<ast::Expr>)> {
	let mut routes = Vec::new();

	while !parser.eat(&token::Eof) {
		match parser.parse_optional_str() {
			Some((ref s, _)) => {
				if !parser.eat(&token::FatArrow) {
					parser.expect(&token::FatArrow);
					break;
				}

				let mut new_base = base.to_string();
				match s.container_as_str() {
					Some(s) => {
						new_base.push_str(s.trim_chars('/'));
						new_base.push_str("/");
					},
					None => cx.span_err(parser.span, "invalid path")
				}

				if parser.eat(&token::Eof) {
					cx.span_err(parser.span, "unexpected end of routing tree");
				}

				if parser.eat(&token::OpenDelim(token::Brace)) {
					let subroutes = parse_subroutes(new_base.as_slice(), cx, parser);
					routes.push_all(subroutes.as_slice());

					if parser.eat(&token::CloseDelim(token::Brace)) {
						if !parser.eat(&token::Comma) {
							break;
						}
					} else {
						parser.expect_one_of([token::Comma, token::CloseDelim(token::Brace)], []);
					}
				} else {
					for (method, handler) in parse_handler(parser).into_iter() {
						routes.push((new_base.clone(), method, handler))
					}

					if !parser.eat(&token::Comma) {
						break;
					}
				}
			},
			None => {
				for (method, handler) in parse_handler(parser).into_iter() {
					routes.push((base.to_string(), method, handler))
				}

				if !parser.eat(&token::Comma) {
					break;
				}
			}
		}
	}

	routes
}

fn parse_handler(parser: &mut Parser) -> Vec<(ast::Path, P<ast::Expr>)> {
	let mut methods = Vec::new();

	loop {
		methods.push(parser.parse_path(parser::NoTypesAllowed).path);

		if parser.eat(&token::Colon) {
			break;
		}

		if !parser.eat(&token::BinOp(token::Or)) {
			parser.expect_one_of([token::Colon, token::BinOp(token::Or)], []);
		}
	}

	let handler = parser.parse_expr();

	methods.into_iter().map(|m| (m, handler.clone())).collect()
}

fn mk_tup(sp: codemap::Span, content: Vec<P<ast::Expr>>) -> P<ast::Expr> {
	dummy_expr(sp, ast::ExprTup(content))
}

fn dummy_expr(sp: codemap::Span, e: ast::Expr_) -> P<ast::Expr> {
	P(ast::Expr {
		id: ast::DUMMY_NODE_ID,
		node: e,
		span: sp,
	})
}

/**
A macro for assigning content types.

It takes a main type, a sub type and a parameter list. Instead of this:

```
response.headers.content_type = Some(MediaType {
	type_: String::from_str("text"),
	subtype: String::from_str("html"),
	parameters: vec!((String::from_str("charset"), String::from_str("UTF-8")))
});
```

it can be written like this:

```
response.headers.content_type = content_type!("text", "html", "charset": "UTF-8");
```

The `"charset": "UTF-8"` part defines the parameter list for the content type.
It may contain more than one parameter, or be omitted:

```
response.headers.content_type = content_type!("application", "octet-stream", "type": "image/gif", "padding": "4");
```

```
response.headers.content_type = content_type!("image", "png");
```
**/
#[macro_export]
macro_rules! content_type(
	($main_type:expr, $sub_type:expr) => ({
		Some(::http::headers::content_type::MediaType {
			type_: String::from_str($main_type),
			subtype: String::from_str($sub_type),
			parameters: Vec::new()
		})
	});

	($main_type:expr, $sub_type:expr, $($param:expr: $value:expr),+) => ({
		Some(::http::headers::content_type::MediaType {
			type_: String::from_str($main_type),
			subtype: String::from_str($sub_type),
			parameters: vec!( $( (String::from_str($param), String::from_str($value)) ),+ )
		})
	});
)

/**
A macro for callig `send` in response and aborting the handle function if it fails.

This macro will print an error to `stdout` and set the response status to `500 Internal Server Error` if the send fails.
The response status may or may not be written to the client, depending on if the connection is
broken or not, or if the headers have already been written.
**/
#[macro_export]
macro_rules! try_send(
	($response:ident, $content:expr) => (
		match $response.send($content) {
			::rustful::Success => {},
			::rustful::IoError(e) => {
				println!("IO error: {}", e);
				$response.status = http::status::InternalServerError;
				return;
			},
			::rustful::PluginError(e) => {
				println!("plugin error: {}", e);
				$response.status = http::status::InternalServerError;
				return;
			}
		}
	);

	($response:ident, $content:expr while $what:expr) => (
		match $response.send($content) {
			::rustful::Success => {},
			::rustful::IoError(e) => {
				println!("IO error while {}: {}", $what, e);
				$response.status = http::status::InternalServerError;
				return;
			},
			::rustful::PluginError(e) => {
				println!("plugin error while {}: {}", $what, e);
				$response.status = http::status::InternalServerError;
				return;
			}
		}
	)
)<|endoftext|>