<commit_before>// Copyright 2015 Pierre Talbot (IRCAM)

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

//     http://www.apache.org/licenses/LICENSE-2.0

// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use Producer;
use std::str::CharIndices;
use std::cmp::Ordering;

impl<'a> Producer for &'a str
{
  type Stream = StrStream<'a>;
  fn producer(self) -> StrStream<'a> {
    StrStream::new(self)
  }
}

#[derive(Clone)]
pub struct StrStream<'a>
{
  raw_data: &'a str,
  current: CharIndices<'a>
}

impl<'a> StrStream<'a>
{
  fn new(raw_data: &'a str) -> StrStream<'a> {
    StrStream {
      raw_data: raw_data,
      current: raw_data.char_indices()
    }
  }

  pub fn offset(&self) -> usize {
    match self.current.clone().peekable().next() {
      None => self.raw_data.len(),
      Some((idx, _)) => idx
    }
  }

  #[inline(always)]
  fn assert_same_raw_data(&self, other: &StrStream<'a>) {
    debug_assert!(self.raw_data.as_ptr() == other.raw_data.as_ptr(),
      "Operations between two streams are only defined when they share the same raw data.");
  }
}

impl<'a> Iterator for StrStream<'a>
{
  type Item = char;
  fn next(&mut self) -> Option<Self::Item> {
    self.current.next().map(|(_,x)| x)
  }
}

impl<'a> PartialEq for StrStream<'a>
{
  fn eq(&self, other: &Self) -> bool {
    self.assert_same_raw_data(other);
    self.offset() == other.offset()
  }
}

impl<'a> Eq for StrStream<'a> {}

impl<'a> PartialOrd for StrStream<'a>
{
  fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
    self.assert_same_raw_data(other);
    self.offset().partial_cmp(&other.offset())
  }
}

impl<'a> Ord for StrStream<'a>
{
  fn cmp(&self, other: &Self) -> Ordering {
    self.assert_same_raw_data(other);
    self.offset().cmp(&other.offset())
  }
}
<commit_msg>[Runtime] Tests the producer StrStream.<commit_after>// Copyright 2015 Pierre Talbot (IRCAM)

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

//     http://www.apache.org/licenses/LICENSE-2.0

// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

pub use Producer;
use std::str::CharIndices;
use std::cmp::Ordering;

impl<'a> Producer for &'a str
{
  type Stream = StrStream<'a>;
  fn producer(self) -> StrStream<'a> {
    StrStream::new(self)
  }
}

#[derive(Clone)]
pub struct StrStream<'a>
{
  raw_data: &'a str,
  current: CharIndices<'a>
}

impl<'a> StrStream<'a>
{
  fn new(raw_data: &'a str) -> StrStream<'a> {
    StrStream {
      raw_data: raw_data,
      current: raw_data.char_indices()
    }
  }

  fn offset(&self) -> usize {
    match self.current.clone().peekable().next() {
      None => self.raw_data.len(),
      Some((idx, _)) => idx
    }
  }

  #[inline(always)]
  fn assert_same_raw_data(&self, other: &StrStream<'a>) {
    debug_assert!(self.raw_data.as_ptr() == other.raw_data.as_ptr(),
      "Operations between two streams are only defined when they share the same raw data.");
  }
}

impl<'a> Iterator for StrStream<'a>
{
  type Item = char;
  fn next(&mut self) -> Option<Self::Item> {
    self.current.next().map(|(_,x)| x)
  }
}

impl<'a> PartialEq for StrStream<'a>
{
  fn eq(&self, other: &Self) -> bool {
    self.assert_same_raw_data(other);
    self.offset() == other.offset()
  }
}

impl<'a> Eq for StrStream<'a> {}

impl<'a> PartialOrd for StrStream<'a>
{
  fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
    self.assert_same_raw_data(other);
    self.offset().partial_cmp(&other.offset())
  }
}

impl<'a> Ord for StrStream<'a>
{
  fn cmp(&self, other: &Self) -> Ordering {
    self.assert_same_raw_data(other);
    self.offset().cmp(&other.offset())
  }
}

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

  #[test]
  fn test_stream() {
    let abc = "abc";
    let mut s1 = abc.producer();
    let s1_init = s1.clone();
    let mut s2 = s1_init.clone();
    for c in abc.chars() {
      assert!(s1 == s2);
      assert_eq!(s1.next().unwrap(), c);
      assert!(s1 > s1_init);
      assert!(s1 > s2);
      s2 = s1.clone();
    }
    assert_eq!(s1.next(), None);
    assert_eq!(s2.next(), None);
    assert!(s1 > s1_init);
    assert!(s1 == s2);
  }

  #[test]
  fn test_empty_stream() {
    let mut empty = "".producer();
    assert_eq!(empty.offset(), 0);
    assert_eq!(empty.next(), None);
    assert_eq!(empty.next(), None);
    assert_eq!(empty.offset(), 0);
    assert!(empty == empty);
    assert!(!(empty > empty));
    let empty2 = empty.clone();
    assert!(empty == empty2);
    assert!(!(empty > empty2));
  }

  fn test_unrelated_streams<R, F>(op: F) where
   F: FnOnce(&StrStream<'static>, &StrStream<'static>) -> R
  {
    let s1 = "abc".producer();
    let s2 = "def".producer();
    op(&s1, &s2);
  }

  #[test]
  #[should_panic]
  fn unrelated_stream_eq() {
    test_unrelated_streams(|a, b| a == b);
  }

  #[test]
  #[should_panic]
  fn unrelated_stream_partial_ord() {
    test_unrelated_streams(|a, b| a.partial_cmp(b));
  }

  #[test]
  #[should_panic]
  fn unrelated_stream_ord() {
    test_unrelated_streams(|a, b| a.cmp(b));
  }
}
<|endoftext|>


<commit_before>use route::*;
use request::*;
use response::*;

#[test]
fn test_fromuri_trait_i32() {
    let mut orig: String = String::from("1234");
    let mut conv: i32 = FromUri::from_uri(&orig);

    assert_eq!(1234, conv);

    orig = String::from("-4321");
    conv = FromUri::from_uri(&orig);

    assert_eq!(-4321, conv);
}

#[test]
fn test_fromuri_trait_string() {
    let orig: String = String::from("foobar");
    let conv: String = FromUri::from_uri(&orig);

    assert_eq!("foobar", conv);
}

#[test]
fn test_fromuri_trait_float() {
    let mut orig: String = String::from("123.45");
    let mut conv: f32 = FromUri::from_uri(&orig);

    assert_eq!(123.45, conv);

    orig = String::from("-54.321");
    conv = FromUri::from_uri(&orig);

    assert_eq!(-54.321, conv);
}

#[test]
fn test_route_match_fail() {
    let rt = Route::new("/api/v1/foo/<int:foo_id>", Route::_no_op);

    assert_eq!(false, rt.is_match("/api/v1/bar"));
    assert_eq!(false, rt.is_match("/api/v1/foo/asdf"));
}

#[test]
fn test_route_match_simple() {
    let route = Route::new("/api/v1/foo/<foo_stuff>", Route::_no_op);
    let parsed = route.parse("/api/v1/foo/blahblahblah").unwrap();

    assert_eq!("blahblahblah", parsed.get("foo_stuff").unwrap());
}

#[test]
fn test_route_match_single_int() {
    let route = Route::new("/api/v1/foo/<int:foo_id>", Route::_no_op);
    let parsed = route.parse("/api/v1/foo/123").unwrap();

    assert_eq!("123", parsed.get("foo_id").unwrap());
}

#[test]
fn test_route_match_single_str() {
    let rt = Route::new("/api/v1/foo/<str:foo_stuff>", Route::_no_op);
    assert_eq!("blahblahblah", rt.parse("/api/v1/foo/blahblahblah").unwrap().get("foo_stuff").unwrap());
}

#[test]
fn test_route_match_many() {
    let rt = Route::new("/api/v1/foo/<int:foo_id>/bar/<str:bar>/baz/<int:baz_id>", Route::_no_op);
    let rm = rt.parse("/api/v1/foo/123/bar/bar/baz/456").unwrap();

    assert_eq!("123", rm.get("foo_id").unwrap());
    assert_eq!("bar", rm.get("bar").unwrap());
    assert_eq!("456", rm.get("baz_id").unwrap());
}

#[test]
fn test_find_route_native_types() {
    let mut request = Request::new();
    let routes: Vec<Route> = vec![Route::new("/api/v1/foo/<int:foo_id>", Route::_no_op),
                                  Route::new("/api/v1/foo/<int:foo_id>/bar/<int:bar_id>", Route::_no_op)];

    request.method = Method::Get;
    request.path = String::from("/api/v1/foo/42/bar/1234");

    for route in routes {
        match route.is_match(&request.path) {
            false => continue,
            true  => {
                request.params = route.parse(&request.path);
                break;
            },
        }
    }

    let foo_id: i32 = request.get("foo_id");
    let bar_id: i32 = request.get("bar_id");

    assert_eq!(42, foo_id);
    assert_eq!(1234, bar_id);
}
<commit_msg>fix up tests to reflect changes<commit_after>use route::*;
use request::*;

#[test]
fn test_fromuri_trait_i32() {
    let mut orig: String = String::from("1234");
    let mut conv: i32 = FromUri::from_uri(&orig);

    assert_eq!(1234, conv);

    orig = String::from("-4321");
    conv = FromUri::from_uri(&orig);

    assert_eq!(-4321, conv);
}

#[test]
fn test_fromuri_trait_string() {
    let orig: String = String::from("foobar");
    let conv: String = FromUri::from_uri(&orig);

    assert_eq!("foobar", conv);
}

#[test]
fn test_fromuri_trait_float() {
    let mut orig: String = String::from("123.45");
    let mut conv: f32 = FromUri::from_uri(&orig);

    assert_eq!(123.45, conv);

    orig = String::from("-54.321");
    conv = FromUri::from_uri(&orig);

    assert_eq!(-54.321, conv);
}

#[test]
fn test_route_match_fail() {
    let rt = Route::new("/api/v1/foo/<int:foo_id>", vec![Method::Get, Method::Post], Route::_no_op);
    let mut req = Request::new();

    req.path = String::from("/api/v1/bar");
    req.method = Method::Get;

    assert_eq!(false, rt.is_match(&req));

    req.path = String::from("/api/v1/foo");
    req.method = Method::Post;

    assert_eq!(false, rt.is_match(&req));

    req.path = String::from("/api/v1/foo/asdf");
    req.method = Method::Get;

    assert_eq!(false, rt.is_match(&req));

    req.path = String::from("/api/v1/foo/123");
    req.method = Method::Get;

    assert_eq!(true, rt.is_match(&req));
}

#[test]
fn test_route_match_simple() {
    let route = Route::new("/api/v1/foo/<foo_stuff>", vec![Method::Get], Route::_no_op);
    let parsed = route.parse("/api/v1/foo/blahblahblah").unwrap();

    assert_eq!("blahblahblah", parsed.get("foo_stuff").unwrap());
}

#[test]
fn test_route_match_single_int() {
    let route = Route::new("/api/v1/foo/<int:foo_id>", vec![Method::Get], Route::_no_op);
    let parsed = route.parse("/api/v1/foo/123").unwrap();

    assert_eq!("123", parsed.get("foo_id").unwrap());
}

#[test]
fn test_route_match_single_str() {
    let rt = Route::new("/api/v1/foo/<str:foo_stuff>", vec![Method::Get], Route::_no_op);
    assert_eq!("blahblahblah", rt.parse("/api/v1/foo/blahblahblah").unwrap().get("foo_stuff").unwrap());
}

#[test]
fn test_route_match_many() {
    let rt = Route::new("/api/v1/foo/<int:foo_id>/bar/<str:bar>/baz/<int:baz_id>", vec![Method::Get], Route::_no_op);
    let rm = rt.parse("/api/v1/foo/123/bar/bar/baz/456").unwrap();

    assert_eq!("123", rm.get("foo_id").unwrap());
    assert_eq!("bar", rm.get("bar").unwrap());
    assert_eq!("456", rm.get("baz_id").unwrap());
}

#[test]
fn test_find_route_native_types() {
    let mut request = Request::new();
    let routes: Vec<Route> = vec![Route::new("/api/v1/foo/<int:foo_id>", vec![Method::Get], Route::_no_op),
                                  Route::new("/api/v1/foo/<int:foo_id>/bar/<int:bar_id>", vec![Method::Get], Route::_no_op)];

    request.method = Method::Get;
    request.path = String::from("/api/v1/foo/42/bar/1234");

    for route in routes {
        match route.is_match(&request) {
            false => continue,
            true  => {
                request.params = route.parse(&request.path);
                break;
            },
        }
    }

    let foo_id: i32 = request.get("foo_id");
    let bar_id: i32 = request.get("bar_id");

    assert_eq!(42, foo_id);
    assert_eq!(1234, bar_id);
}
<|endoftext|>


<commit_before>mod specifiers_iterator;

macro_rules! fmt {
    ( $format:expr ) => {{
        let result: &str = $format;
        result
    }};
}

#[cfg(test)]
mod tests {
    #[test]
    fn returns_first_argument_if_it_is_the_only_one() {
        let input = String::from("Hello, world!");
        assert_eq!(fmt!(&input), input);
    }
}
<commit_msg>Call out to libc::snprintf to format stuff<commit_after>pub mod specifiers_iterator;

/// A safe-ish wrapper around `libc::snprintf`.
#[macro_export]
macro_rules! fmt {
    ( $format:expr ) => {
        {
            let format: &str = $format;
            String::from(format)
        }
    };

    ( $format:expr, $( $arg:expr ),+ ) => {
        {
            let format: &str = $format;

            let mut result = String::new();

            use $crate::specifiers_iterator::SpecifiersIterator;
            let mut specifiers = SpecifiersIterator::from(format);

            $(
                let local_format = specifiers.next().unwrap_or("");
                // Might fail if `local_format` contains a null byte. TODO decide if this is a good
                // error-handling strategy, or we should report back to user, or panic, or what
                if let Ok(local_format_cstring) = std::ffi::CString::new(local_format) {
                    let buf_size = 1024usize;
                    let buffer: std::vec::Vec<u8> = std::vec::Vec::with_capacity(buf_size);
                    if let Ok(buffer) = std::ffi::CString::new(buffer) {
                        unsafe {
                            let buffer_ptr = buffer.into_raw();
                            // TODO: check how many bytes were written
                            let bytes_written = libc::snprintf(
                                buffer_ptr,
                                buf_size as libc::size_t,
                                local_format_cstring.as_ptr() as *const libc::c_char,
                                $arg);
                            let buffer = std::ffi::CString::from_raw(buffer_ptr);
                            let _bytes_written = bytes_written as usize;
                            if let Ok(formatted) = buffer.into_string() {
                                result.push_str(&formatted);
                            }
                        }
                    }
                }
            )+

            result
        }
    };
}

#[cfg(test)]
mod tests {
    #[test]
    fn returns_first_argument_if_it_is_the_only_one() {
        let input = String::from("Hello, world!");
        assert_eq!(fmt!(&input), input);
    }

    #[test]
    fn replaces_printf_format_with_text_representation_of_an_argument() {
        assert_eq!(fmt!("%i", 42), "42");
        assert_eq!(fmt!("%i", -13), "-13");
        assert_eq!(fmt!("%.3f", 3.1416), "3.142");
        assert_eq!(fmt!("%i %i", 100500, -191), "100500 -191");
    }
}
<|endoftext|>


<commit_before>//! Request logging middleware
use std::env;
use std::fmt;
use std::fmt::{Display, Formatter};

use libc;
use time;
use regex::Regex;

use httprequest::HttpRequest;
use httpresponse::HttpResponse;
use middlewares::{Middleware, Started, Finished};

/// `Middleware` for logging request and response info to the terminal.
///
/// ## Usage
///
/// Create `Logger` middlewares with the specified `format`.
/// Default `Logger` could be created with `default` method, it uses the default format:
///
/// ```ignore
///  %a %t "%r" %s %b "%{Referrer}i" "%{User-Agent}i" %T
/// ```
/// ```rust
/// extern crate actix_web;
/// use actix_web::Application;
/// use actix_web::middlewares::Logger;
///
/// fn main() {
///     let app = Application::default("/")
///         .middleware(Logger::default())
///         .middleware(Logger::new("%a %{User-Agent}i"))
///         .finish();
/// }
/// ```
///
/// ## Format
///
/// `%%`  The percent sign
///
/// `%a`  Remote IP-address (IP-address of proxy if using reverse proxy)
///
/// `%t`  Time when the request was started to process
///
/// `%P`  The process ID of the child that serviced the request
///
/// `%r`  First line of request
///
/// `%s`  Response status code
///
/// `%b`  Size of response in bytes, including HTTP headers
///
/// `%T` Time taken to serve the request, in seconds with floating fraction in .06f format
///
/// `%D`  Time taken to serve the request, in milliseconds
///
/// `%{FOO}i`  request.headers['FOO']
///
/// `%{FOO}o`  response.headers['FOO']
///
/// `%{FOO}e`  os.environ['FOO']
///
pub struct Logger {
    format: Format,
}

impl Logger {
    /// Create `Logger` middleware with the specified `format`.
    pub fn new(format: &str) -> Logger {
        Logger { format: Format::new(format) }
    }
}

impl Default for Logger {
    /// Create `Logger` middleware with format:
    ///
    /// ```ignore
    /// %a %t "%r" %s %b "%{Referrer}i" "%{User-Agent}i" %T
    /// ```
    fn default() -> Logger {
        Logger { format: Format::default() }
    }
}

struct StartTime(time::Tm);

impl Logger {

    fn log(&self, req: &mut HttpRequest, resp: &HttpResponse) {
        let entry_time = req.extensions().get::<StartTime>().unwrap().0;

        let render = |fmt: &mut Formatter| {
            for unit in &self.format.0 {
                unit.render(fmt, req, resp, entry_time)?;
            }
            Ok(())
        };
        info!("{}", FormatDisplay(&render));
    }
}

impl Middleware for Logger {

    fn start(&self, req: &mut HttpRequest) -> Started {
        req.extensions().insert(StartTime(time::now()));
        Started::Done
    }

    fn finish(&self, req: &mut HttpRequest, resp: &HttpResponse) -> Finished {
        self.log(req, resp);
        Finished::Done
    }
}

/// A formatting style for the `Logger`, consisting of multiple
/// `FormatText`s concatenated into one line.
#[derive(Clone)]
#[doc(hidden)]
struct Format(Vec<FormatText>);

impl Default for Format {
    /// Return the default formatting style for the `Logger`:
    fn default() -> Format {
        Format::new(r#"%a %t "%r" %s %b "%{Referrer}i" "%{User-Agent}i" %T"#)
    }
}

impl Format {
    /// Create a `Format` from a format string.
    ///
    /// Returns `None` if the format string syntax is incorrect.
    pub fn new(s: &str) -> Format {
        trace!("Access log format: {}", s);
        let fmt = Regex::new(r"%(\{([A-Za-z0-9\-_]+)\}([ioe])|[atPrsbTD]?)").unwrap();

        let mut idx = 0;
        let mut results = Vec::new();
        for cap in fmt.captures_iter(s) {
            let m = cap.get(0).unwrap();
            let pos = m.start();
            if idx != pos {
                results.push(FormatText::Str(s[idx..pos].to_owned()));
            }
            idx = m.end();

            if let Some(key) = cap.get(2) {
                results.push(
                    match cap.get(3).unwrap().as_str() {
                        "i" => FormatText::RequestHeader(key.as_str().to_owned()),
                        "o" => FormatText::ResponseHeader(key.as_str().to_owned()),
                        "e" => FormatText::EnvironHeader(key.as_str().to_owned()),
                        _ => unreachable!(),
                    })
            } else {
                let m = cap.get(1).unwrap();
                results.push(
                    match m.as_str() {
                        "%" => FormatText::Percent,
                        "a" => FormatText::RemoteAddr,
                        "t" => FormatText::RequestTime,
                        "P" => FormatText::Pid,
                        "r" => FormatText::RequestLine,
                        "s" => FormatText::ResponseStatus,
                        "b" => FormatText::ResponseSize,
                        "T" => FormatText::Time,
                        "D" => FormatText::TimeMillis,
                        _ => FormatText::Str(m.as_str().to_owned()),
                    }
                );
            }
        }
        if idx != s.len() {
            results.push(FormatText::Str(s[idx..].to_owned()));
        }

        Format(results)
    }
}

/// A string of text to be logged. This is either one of the data
/// fields supported by the `Logger`, or a custom `String`.
#[doc(hidden)]
#[derive(Debug, Clone)]
pub enum FormatText {
    Str(String),
    Pid,
    Percent,
    RequestLine,
    RequestTime,
    ResponseStatus,
    ResponseSize,
    Time,
    TimeMillis,
    RemoteAddr,
    RequestHeader(String),
    ResponseHeader(String),
    EnvironHeader(String),
}

impl FormatText {

    fn render(&self, fmt: &mut Formatter,
              req: &HttpRequest,
              resp: &HttpResponse,
              entry_time: time::Tm) -> Result<(), fmt::Error>
    {
        match *self {
            FormatText::Str(ref string) => fmt.write_str(string),
            FormatText::Percent => "%".fmt(fmt),
            FormatText::RequestLine => {
                if req.query_string().is_empty() {
                    fmt.write_fmt(format_args!(
                        "{} {} {:?}",
                        req.method(), req.path(), req.version()))
                } else {
                    fmt.write_fmt(format_args!(
                        "{} {}?{} {:?}",
                        req.method(), req.path(), req.query_string(), req.version()))
                }
            },
            FormatText::ResponseStatus => resp.status().as_u16().fmt(fmt),
            FormatText::ResponseSize => resp.response_size().fmt(fmt),
            FormatText::Pid => unsafe{libc::getpid().fmt(fmt)},
            FormatText::Time => {
                let response_time = time::now() - entry_time;
                let response_time = (response_time.num_seconds() * 1000) as f64 +
                    (response_time.num_nanoseconds().unwrap_or(0) as f64) / 1000000000.0;

                fmt.write_fmt(format_args!("{:.6}", response_time))
            },
            FormatText::TimeMillis => {
                let response_time = time::now() - entry_time;
                let response_time_ms = (response_time.num_seconds() * 1000) as f64 +
                    (response_time.num_nanoseconds().unwrap_or(0) as f64) / 1000000.0;

                fmt.write_fmt(format_args!("{:.6}", response_time_ms))
            },
            FormatText::RemoteAddr => {
                if let Some(addr) = req.remote() {
                    addr.fmt(fmt)
                } else {
                    "-".fmt(fmt)
                }
            }
            FormatText::RequestTime => {
                entry_time.strftime("[%d/%b/%Y:%H:%M:%S %z]")
                    .unwrap()
                    .fmt(fmt)
            }
            FormatText::RequestHeader(ref name) => {
                let s = if let Some(val) = req.headers().get(name) {
                    if let Ok(s) = val.to_str() { s } else { "-" }
                } else {
                    "-"
                };
                fmt.write_fmt(format_args!("{}", s))
            }
            FormatText::ResponseHeader(ref name) => {
                let s = if let Some(val) = resp.headers().get(name) {
                    if let Ok(s) = val.to_str() { s } else { "-" }
                } else {
                    "-"
                };
                fmt.write_fmt(format_args!("{}", s))
            }
            FormatText::EnvironHeader(ref name) => {
                if let Ok(val) = env::var(name) {
                    fmt.write_fmt(format_args!("{}", val))
                } else {
                    "-".fmt(fmt)
                }
            }
        }
    }
}

pub(crate) struct FormatDisplay<'a>(
    &'a Fn(&mut Formatter) -> Result<(), fmt::Error>);

impl<'a> fmt::Display for FormatDisplay<'a> {
    fn fmt(&self, fmt: &mut Formatter) -> Result<(), fmt::Error> {
        (self.0)(fmt)
    }
}

#[cfg(test)]
mod tests {
    use Body;
    use super::*;
    use time;
    use http::{Method, Version, StatusCode};
    use http::header::{self, HeaderMap};

    #[test]
    fn test_logger() {
        let logger = Logger::new("%% %{User-Agent}i %{X-Test}o %{HOME}e %D test");

        let mut headers = HeaderMap::new();
        headers.insert(header::USER_AGENT, header::HeaderValue::from_static("ACTIX-WEB"));
        let mut req = HttpRequest::new(
            Method::GET, "/".to_owned(), Version::HTTP_11, headers, String::new());
        let resp = HttpResponse::builder(StatusCode::OK)
            .header("X-Test", "ttt")
            .force_close().body(Body::Empty).unwrap();

        match logger.start(&mut req) {
            Started::Done => (),
            _ => panic!(),
        }
        match logger.finish(&mut req, &resp) {
            Finished::Done => (),
            _ => panic!(),
        }
    }

    #[test]
    fn test_default_format() {
        let format = Format::default();

        let mut headers = HeaderMap::new();
        headers.insert(header::USER_AGENT, header::HeaderValue::from_static("ACTIX-WEB"));
        let req = HttpRequest::new(
            Method::GET, "/".to_owned(), Version::HTTP_11, headers, String::new());
        let resp = HttpResponse::builder(StatusCode::OK)
            .force_close().body(Body::Empty).unwrap();
        let entry_time = time::now();

        let render = |fmt: &mut Formatter| {
            for unit in format.0.iter() {
                unit.render(fmt, &req, &resp, entry_time)?;
            }
            Ok(())
        };
        let s = format!("{}", FormatDisplay(&render));
        assert!(s.contains("GET / HTTP/1.1"));
        assert!(s.contains("200 0"));
        assert!(s.contains("ACTIX-WEB"));

        let req = HttpRequest::new(
            Method::GET, "/".to_owned(), Version::HTTP_11, HeaderMap::new(), "test".to_owned());
        let resp = HttpResponse::builder(StatusCode::OK)
            .force_close().body(Body::Empty).unwrap();
        let entry_time = time::now();

        let render = |fmt: &mut Formatter| {
            for unit in format.0.iter() {
                unit.render(fmt, &req, &resp, entry_time)?;
            }
            Ok(())
        };
        let s = format!("{}", FormatDisplay(&render));
        assert!(s.contains("GET /?test HTTP/1.1"));
    }
}
<commit_msg>use env_logger for logger tests<commit_after>//! Request logging middleware
use std::env;
use std::fmt;
use std::fmt::{Display, Formatter};

use libc;
use time;
use regex::Regex;

use httprequest::HttpRequest;
use httpresponse::HttpResponse;
use middlewares::{Middleware, Started, Finished};

/// `Middleware` for logging request and response info to the terminal.
///
/// ## Usage
///
/// Create `Logger` middlewares with the specified `format`.
/// Default `Logger` could be created with `default` method, it uses the default format:
///
/// ```ignore
///  %a %t "%r" %s %b "%{Referrer}i" "%{User-Agent}i" %T
/// ```
/// ```rust
/// extern crate actix_web;
/// use actix_web::Application;
/// use actix_web::middlewares::Logger;
///
/// fn main() {
///     let app = Application::default("/")
///         .middleware(Logger::default())
///         .middleware(Logger::new("%a %{User-Agent}i"))
///         .finish();
/// }
/// ```
///
/// ## Format
///
/// `%%`  The percent sign
///
/// `%a`  Remote IP-address (IP-address of proxy if using reverse proxy)
///
/// `%t`  Time when the request was started to process
///
/// `%P`  The process ID of the child that serviced the request
///
/// `%r`  First line of request
///
/// `%s`  Response status code
///
/// `%b`  Size of response in bytes, including HTTP headers
///
/// `%T` Time taken to serve the request, in seconds with floating fraction in .06f format
///
/// `%D`  Time taken to serve the request, in milliseconds
///
/// `%{FOO}i`  request.headers['FOO']
///
/// `%{FOO}o`  response.headers['FOO']
///
/// `%{FOO}e`  os.environ['FOO']
///
pub struct Logger {
    format: Format,
}

impl Logger {
    /// Create `Logger` middleware with the specified `format`.
    pub fn new(format: &str) -> Logger {
        Logger { format: Format::new(format) }
    }
}

impl Default for Logger {
    /// Create `Logger` middleware with format:
    ///
    /// ```ignore
    /// %a %t "%r" %s %b "%{Referrer}i" "%{User-Agent}i" %T
    /// ```
    fn default() -> Logger {
        Logger { format: Format::default() }
    }
}

struct StartTime(time::Tm);

impl Logger {

    fn log(&self, req: &mut HttpRequest, resp: &HttpResponse) {
        let entry_time = req.extensions().get::<StartTime>().unwrap().0;

        let render = |fmt: &mut Formatter| {
            for unit in &self.format.0 {
                unit.render(fmt, req, resp, entry_time)?;
            }
            Ok(())
        };
        info!("{}", FormatDisplay(&render));
    }
}

impl Middleware for Logger {

    fn start(&self, req: &mut HttpRequest) -> Started {
        req.extensions().insert(StartTime(time::now()));
        Started::Done
    }

    fn finish(&self, req: &mut HttpRequest, resp: &HttpResponse) -> Finished {
        self.log(req, resp);
        Finished::Done
    }
}

/// A formatting style for the `Logger`, consisting of multiple
/// `FormatText`s concatenated into one line.
#[derive(Clone)]
#[doc(hidden)]
struct Format(Vec<FormatText>);

impl Default for Format {
    /// Return the default formatting style for the `Logger`:
    fn default() -> Format {
        Format::new(r#"%a %t "%r" %s %b "%{Referrer}i" "%{User-Agent}i" %T"#)
    }
}

impl Format {
    /// Create a `Format` from a format string.
    ///
    /// Returns `None` if the format string syntax is incorrect.
    pub fn new(s: &str) -> Format {
        trace!("Access log format: {}", s);
        let fmt = Regex::new(r"%(\{([A-Za-z0-9\-_]+)\}([ioe])|[atPrsbTD]?)").unwrap();

        let mut idx = 0;
        let mut results = Vec::new();
        for cap in fmt.captures_iter(s) {
            let m = cap.get(0).unwrap();
            let pos = m.start();
            if idx != pos {
                results.push(FormatText::Str(s[idx..pos].to_owned()));
            }
            idx = m.end();

            if let Some(key) = cap.get(2) {
                results.push(
                    match cap.get(3).unwrap().as_str() {
                        "i" => FormatText::RequestHeader(key.as_str().to_owned()),
                        "o" => FormatText::ResponseHeader(key.as_str().to_owned()),
                        "e" => FormatText::EnvironHeader(key.as_str().to_owned()),
                        _ => unreachable!(),
                    })
            } else {
                let m = cap.get(1).unwrap();
                results.push(
                    match m.as_str() {
                        "%" => FormatText::Percent,
                        "a" => FormatText::RemoteAddr,
                        "t" => FormatText::RequestTime,
                        "P" => FormatText::Pid,
                        "r" => FormatText::RequestLine,
                        "s" => FormatText::ResponseStatus,
                        "b" => FormatText::ResponseSize,
                        "T" => FormatText::Time,
                        "D" => FormatText::TimeMillis,
                        _ => FormatText::Str(m.as_str().to_owned()),
                    }
                );
            }
        }
        if idx != s.len() {
            results.push(FormatText::Str(s[idx..].to_owned()));
        }

        Format(results)
    }
}

/// A string of text to be logged. This is either one of the data
/// fields supported by the `Logger`, or a custom `String`.
#[doc(hidden)]
#[derive(Debug, Clone)]
pub enum FormatText {
    Str(String),
    Pid,
    Percent,
    RequestLine,
    RequestTime,
    ResponseStatus,
    ResponseSize,
    Time,
    TimeMillis,
    RemoteAddr,
    RequestHeader(String),
    ResponseHeader(String),
    EnvironHeader(String),
}

impl FormatText {

    fn render(&self, fmt: &mut Formatter,
              req: &HttpRequest,
              resp: &HttpResponse,
              entry_time: time::Tm) -> Result<(), fmt::Error>
    {
        match *self {
            FormatText::Str(ref string) => fmt.write_str(string),
            FormatText::Percent => "%".fmt(fmt),
            FormatText::RequestLine => {
                if req.query_string().is_empty() {
                    fmt.write_fmt(format_args!(
                        "{} {} {:?}",
                        req.method(), req.path(), req.version()))
                } else {
                    fmt.write_fmt(format_args!(
                        "{} {}?{} {:?}",
                        req.method(), req.path(), req.query_string(), req.version()))
                }
            },
            FormatText::ResponseStatus => resp.status().as_u16().fmt(fmt),
            FormatText::ResponseSize => resp.response_size().fmt(fmt),
            FormatText::Pid => unsafe{libc::getpid().fmt(fmt)},
            FormatText::Time => {
                let response_time = time::now() - entry_time;
                let response_time = (response_time.num_seconds() * 1000) as f64 +
                    (response_time.num_nanoseconds().unwrap_or(0) as f64) / 1000000000.0;

                fmt.write_fmt(format_args!("{:.6}", response_time))
            },
            FormatText::TimeMillis => {
                let response_time = time::now() - entry_time;
                let response_time_ms = (response_time.num_seconds() * 1000) as f64 +
                    (response_time.num_nanoseconds().unwrap_or(0) as f64) / 1000000.0;

                fmt.write_fmt(format_args!("{:.6}", response_time_ms))
            },
            FormatText::RemoteAddr => {
                if let Some(addr) = req.remote() {
                    addr.fmt(fmt)
                } else {
                    "-".fmt(fmt)
                }
            }
            FormatText::RequestTime => {
                entry_time.strftime("[%d/%b/%Y:%H:%M:%S %z]")
                    .unwrap()
                    .fmt(fmt)
            }
            FormatText::RequestHeader(ref name) => {
                let s = if let Some(val) = req.headers().get(name) {
                    if let Ok(s) = val.to_str() { s } else { "-" }
                } else {
                    "-"
                };
                fmt.write_fmt(format_args!("{}", s))
            }
            FormatText::ResponseHeader(ref name) => {
                let s = if let Some(val) = resp.headers().get(name) {
                    if let Ok(s) = val.to_str() { s } else { "-" }
                } else {
                    "-"
                };
                fmt.write_fmt(format_args!("{}", s))
            }
            FormatText::EnvironHeader(ref name) => {
                if let Ok(val) = env::var(name) {
                    fmt.write_fmt(format_args!("{}", val))
                } else {
                    "-".fmt(fmt)
                }
            }
        }
    }
}

pub(crate) struct FormatDisplay<'a>(
    &'a Fn(&mut Formatter) -> Result<(), fmt::Error>);

impl<'a> fmt::Display for FormatDisplay<'a> {
    fn fmt(&self, fmt: &mut Formatter) -> Result<(), fmt::Error> {
        (self.0)(fmt)
    }
}

#[cfg(test)]
mod tests {
    extern crate env_logger;
    use Body;
    use super::*;
    use time;
    use http::{Method, Version, StatusCode};
    use http::header::{self, HeaderMap};

    #[test]
    fn test_logger() {
        let _ = env_logger::init();
        let logger = Logger::new("%% %{User-Agent}i %{X-Test}o %{HOME}e %D test");

        let mut headers = HeaderMap::new();
        headers.insert(header::USER_AGENT, header::HeaderValue::from_static("ACTIX-WEB"));
        let mut req = HttpRequest::new(
            Method::GET, "/".to_owned(), Version::HTTP_11, headers, String::new());
        let resp = HttpResponse::builder(StatusCode::OK)
            .header("X-Test", "ttt")
            .force_close().body(Body::Empty).unwrap();

        match logger.start(&mut req) {
            Started::Done => (),
            _ => panic!(),
        }
        match logger.finish(&mut req, &resp) {
            Finished::Done => (),
            _ => panic!(),
        }
    }

    #[test]
    fn test_default_format() {
        let format = Format::default();

        let mut headers = HeaderMap::new();
        headers.insert(header::USER_AGENT, header::HeaderValue::from_static("ACTIX-WEB"));
        let req = HttpRequest::new(
            Method::GET, "/".to_owned(), Version::HTTP_11, headers, String::new());
        let resp = HttpResponse::builder(StatusCode::OK)
            .force_close().body(Body::Empty).unwrap();
        let entry_time = time::now();

        let render = |fmt: &mut Formatter| {
            for unit in format.0.iter() {
                unit.render(fmt, &req, &resp, entry_time)?;
            }
            Ok(())
        };
        let s = format!("{}", FormatDisplay(&render));
        assert!(s.contains("GET / HTTP/1.1"));
        assert!(s.contains("200 0"));
        assert!(s.contains("ACTIX-WEB"));

        let req = HttpRequest::new(
            Method::GET, "/".to_owned(), Version::HTTP_11, HeaderMap::new(), "test".to_owned());
        let resp = HttpResponse::builder(StatusCode::OK)
            .force_close().body(Body::Empty).unwrap();
        let entry_time = time::now();

        let render = |fmt: &mut Formatter| {
            for unit in format.0.iter() {
                unit.render(fmt, &req, &resp, entry_time)?;
            }
            Ok(())
        };
        let s = format!("{}", FormatDisplay(&render));
        assert!(s.contains("GET /?test HTTP/1.1"));
    }
}
<|endoftext|>


<commit_before>// Copyright 2017 tokio-jsonrpc Developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://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.

//! The codecs to encode and decode messages from a stream of bytes.
//!
//! You can choose to use either line separated one ([Line](struct.Line.html)) or
//! boundary separated one ([Boundary](struct.Boundary.html)). The first one needs the
//! messages to be separated by newlines and not to contain newlines in their representation. On
//! the other hand, it can recover from syntax error in a message and you can respond with an error
//! instead of terminating the connection.

use std::io::{Error, ErrorKind, Result as IoResult};

use tokio_io::codec::{Decoder, Encoder};
use bytes::{BufMut, BytesMut};
use serde_json::de::Deserializer;
use serde_json::ser::to_vec;
use serde_json::error::Error as SerdeError;

use message::{decoded_to_parsed, from_slice, from_str, Message, Parsed};

/// A helper to wrap the error
fn err_map(e: SerdeError) -> Error {
    Error::new(ErrorKind::Other, e)
}

/// A helper trait to unify `Line` and `DirtyLine`
trait PositionCache {
    fn position(&mut self) -> &mut usize;
}

/// An encoding function reused by [`Line`], [`DirtyLine`] and [`Boundary`]
fn encode_codec(msg: &Message, buf: &mut BytesMut) -> IoResult<()> {
    let encoded = to_vec(&msg).map_err(err_map)?;
    // As discovered the hard way, we must not overwrite buf, but append to it.
    buf.reserve(encoded.len() + 1);
    buf.put_slice(&encoded);
    buf.put(b'\n');
    Ok(())
}

fn decode_codec<Cache, Convert>(
    cache: &mut Cache, buf: &mut BytesMut, convert: Convert
) -> IoResult<Option<Parsed>>
where
    Cache: PositionCache,
    Convert: FnOnce(&[u8]) -> Parsed,
{
    // Where did we stop scanning before? Scan only the new part
    let start_pos = cache.position();
    if let Some(i) = buf[*start_pos..].iter().position(|&b| b == b'\n') {
        let end_pos = *start_pos + i;
        let line = buf.split_to(end_pos);
        buf.split_to(1);
        // We'll start from the beginning next time.
        *start_pos = 0;
        Ok(Some(convert(&line)))
    } else {
        // Mark where we ended scanning.
        *start_pos = buf.len();
        Ok(None)
    }
}

/// A codec working with JSONRPC 2.0 messages.
///
/// This produces or encodes [Message](../message/enum.Message.hmtl). It separates the records by
/// newlines, so it can recover from syntax error.s
///
/// Note that the produced items is a `Result`, to allow not terminating the stream on
/// protocol-level errors.
#[derive(Debug, Default)]
pub struct Line(usize);

impl Line {
    /// A constructor
    pub fn new() -> Self {
        Line(0)
    }
}

impl PositionCache for Line {
    fn position(&mut self) -> &mut usize {
        &mut self.0
    }
}

impl Encoder for Line {
    type Item = Message;
    type Error = Error;
    fn encode(&mut self, msg: Message, buf: &mut BytesMut) -> IoResult<()> {
        encode_codec(&msg, buf)
    }
}

impl Decoder for Line {
    type Item = Parsed;
    type Error = Error;
    fn decode(&mut self, src: &mut BytesMut) -> IoResult<Option<Parsed>> {
        decode_codec(self, src, from_slice)
    }
}

/// A codec working with JSONRPC 2.0 messages on top of badly encoded utf-8.
///
/// This works like the [Line](struct.Line.html) codec. However, it can cope with the input not
/// being valid utf-8. That is arguably broken, nevertheless found in the wild and sometimes the
/// only thing left to be done is to cope with it. This copes with it by running the input through
/// the `String::from_utf8_lossy` conversion, effectively replacing anything that is not valid with
/// these special utf-8 WTF question marks (U+FFFD).
///
/// In contrast, Line errors on such invalid inputs. Encoding is the same for both codecs, however.
#[derive(Debug, Default)]
pub struct DirtyLine(usize);

impl DirtyLine {
    /// A constructor
    pub fn new() -> Self {
        DirtyLine(0)
    }
}

impl PositionCache for DirtyLine {
    fn position(&mut self) -> &mut usize {
        &mut self.0
    }
}

impl Decoder for DirtyLine {
    type Item = Parsed;
    type Error = Error;
    fn decode(&mut self, src: &mut BytesMut) -> IoResult<Option<Parsed>> {
        decode_codec(self, src, |bytes| {
            from_str(String::from_utf8_lossy(bytes).as_ref())
        })
    }
}

impl Encoder for DirtyLine {
    type Item = Message;
    type Error = Error;
    fn encode(&mut self, msg: Message, buf: &mut BytesMut) -> IoResult<()> {
        encode_codec(&msg, buf)
    }
}

/// A codec working with JSONRPC 2.0 messages.
///
/// This produces or encodes [Message](../message/enum.Message.hmtl). It takes the JSON object
/// boundaries, so it works with both newline-separated and object-separated encoding. It produces
/// newline-separated stream, which is more generic.
pub struct Boundary;

impl Encoder for Boundary {
    type Item = Message;
    type Error = Error;
    fn encode(&mut self, msg: Message, buf: &mut BytesMut) -> IoResult<()> {
        encode_codec(&msg, buf)
    }
}

impl Decoder for Boundary {
    type Item = Parsed;
    type Error = Error;
    fn decode(&mut self, src: &mut BytesMut) -> IoResult<Option<Parsed>> {
        let (decoded, pos) = {
            let mut deserializer = Deserializer::from_slice(src).into_iter();
            let decoded = deserializer.next().and_then(|result| match result {
                Err(ref e) if e.is_eof() => None,
                other => Some(decoded_to_parsed(other)),
            });
            (decoded, deserializer.byte_offset())
        };

        // It did read some data from the input. Find out how many and cut them off.
        src.split_to(pos);
        Ok(decoded)
    }
}

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

    #[test]
    fn encode() {
        let mut output = BytesMut::with_capacity(10);
        let mut codec = Line::new();
        let msg = Message::notification("notif".to_owned(), None);
        let encoded = BytesMut::from(&b"{\"jsonrpc\":\"2.0\",\"method\":\"notif\"}\n"[..]);
        codec.encode(msg.clone(), &mut output).unwrap();
        assert_eq!(encoded, output);
        let mut dirty_codec = DirtyLine::new();
        output.clear();
        dirty_codec.encode(msg, &mut output).unwrap();
        assert_eq!(encoded, output);
    }

    fn get_buf(input: &[u8]) -> BytesMut {
        BytesMut::from(input)
    }

    #[test]
    fn decode() {
        fn one(input: &[u8], rest: &[u8]) -> IoResult<Option<Parsed>> {
            let mut codec = Line::new();
            let mut buf = get_buf(input);
            let result = codec.decode(&mut buf);
            assert_eq!(rest, &buf);
            // On all the valid inputs, DirtyLine should act the same as Line
            let mut dirty_codec = DirtyLine::new();
            let mut buf = get_buf(input);
            let dirty = dirty_codec.decode(&mut buf);
            assert_eq!(rest, &buf);
            assert_eq!(result.as_ref().unwrap(), dirty.as_ref().unwrap());
            result
        }

        let notif = Message::notification("notif".to_owned(), None);
        let msgstring = Vec::from(&b"{\"jsonrpc\":\"2.0\",\"method\":\"notif\"}\n"[..]);
        // A single message, nothing is left
        assert_eq!(one(&msgstring, b"").unwrap(), Some(Ok(notif.clone())));
        // The first message is decoded, the second stays in the buffer
        let mut twomsgs = msgstring.clone();
        twomsgs.extend_from_slice(&msgstring);
        assert_eq!(one(&twomsgs, &msgstring).unwrap(), Some(Ok(notif.clone())));
        // The second message is incomplete, but stays there
        let incomplete = Vec::from(&br#"{"jsonrpc": "2.0", "method":""#[..]);
        let mut oneandhalf = msgstring.clone();
        oneandhalf.extend_from_slice(&incomplete);
        assert_eq!(
            one(&oneandhalf, &incomplete).unwrap(),
            Some(Ok(notif.clone()))
        );
        // An incomplete message ‒ nothing gets out and everything stays
        assert_eq!(one(&incomplete, &incomplete).unwrap(), None);
        // A syntax error is reported as an error (and eaten, but that's no longer interesting)
        match one(b"{]\n", b"") {
            Ok(Some(Err(Broken::SyntaxError(_)))) => (),
            other => panic!("Something unexpected: {:?}", other),
        };
    }

    /// Test with invalid utf-8 in a string
    #[test]
    fn decode_nonunicode() {
        let broken_input = b"{\"jsonrpc\":\"2.0\",\"method\":\"Hello \xF0\x90\x80World\"}\n";
        let mut codec = Line::new();
        let mut buf = get_buf(broken_input);
        // The ordinary line codec gives up
        let result = codec.decode(&mut buf).unwrap();
        match result {
            Some(Err(Broken::SyntaxError(_))) => (),
            other => panic!("Something unexpected: {:?}", other),
        };
        buf = get_buf(broken_input);
        // But the dirty one just keeps going on
        let mut dirty = DirtyLine::new();
        let result = dirty.decode(&mut buf).unwrap();
        assert_eq!(
            result,
            Some(Ok(Message::notification("Hello �World".to_owned(), None)))
        );
    }

    /// Not enough data for a whole message
    #[test]
    fn decode_boundary_short() {
        let mut buf = get_buf(b"{\"jsonrpc\":\"");
        assert!(Boundary.decode(&mut buf).unwrap().is_none());
        assert_eq!(&buf, &b"{\"jsonrpc\":\""[..]);
    }

    /// There's more than one message, but not two.
    ///
    /// And they are not separated by whitespace (to test the boundary decoding).
    #[test]
    fn decode_boundary_prefix() {
        let mut buf = get_buf(b"\n\n {\"jsonrpc\":\"2.0\",\"method\":\"notif\"}{\"");
        assert_eq!(
            Boundary.decode(&mut buf).unwrap().unwrap(),
            Ok(Message::notification("notif".to_owned(), None))
        );
        assert_eq!(&buf, &b"{\""[..]);
    }
}
<commit_msg>Fix a couple of typos in doc comments.<commit_after>// Copyright 2017 tokio-jsonrpc Developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://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.

//! The codecs to encode and decode messages from a stream of bytes.
//!
//! You can choose to use either line separated one ([Line](struct.Line.html)) or
//! boundary separated one ([Boundary](struct.Boundary.html)). The first one needs the
//! messages to be separated by newlines and not to contain newlines in their representation. On
//! the other hand, it can recover from syntax error in a message and you can respond with an error
//! instead of terminating the connection.

use std::io::{Error, ErrorKind, Result as IoResult};

use tokio_io::codec::{Decoder, Encoder};
use bytes::{BufMut, BytesMut};
use serde_json::de::Deserializer;
use serde_json::ser::to_vec;
use serde_json::error::Error as SerdeError;

use message::{decoded_to_parsed, from_slice, from_str, Message, Parsed};

/// A helper to wrap the error
fn err_map(e: SerdeError) -> Error {
    Error::new(ErrorKind::Other, e)
}

/// A helper trait to unify `Line` and `DirtyLine`
trait PositionCache {
    fn position(&mut self) -> &mut usize;
}

/// An encoding function reused by [`Line`], [`DirtyLine`] and [`Boundary`]
fn encode_codec(msg: &Message, buf: &mut BytesMut) -> IoResult<()> {
    let encoded = to_vec(&msg).map_err(err_map)?;
    // As discovered the hard way, we must not overwrite buf, but append to it.
    buf.reserve(encoded.len() + 1);
    buf.put_slice(&encoded);
    buf.put(b'\n');
    Ok(())
}

fn decode_codec<Cache, Convert>(
    cache: &mut Cache, buf: &mut BytesMut, convert: Convert
) -> IoResult<Option<Parsed>>
where
    Cache: PositionCache,
    Convert: FnOnce(&[u8]) -> Parsed,
{
    // Where did we stop scanning before? Scan only the new part
    let start_pos = cache.position();
    if let Some(i) = buf[*start_pos..].iter().position(|&b| b == b'\n') {
        let end_pos = *start_pos + i;
        let line = buf.split_to(end_pos);
        buf.split_to(1);
        // We'll start from the beginning next time.
        *start_pos = 0;
        Ok(Some(convert(&line)))
    } else {
        // Mark where we ended scanning.
        *start_pos = buf.len();
        Ok(None)
    }
}

/// A codec working with JSONRPC 2.0 messages.
///
/// This produces or encodes [Message](../message/enum.Message.html). It separates the records by
/// newlines, so it can recover from syntax errors.
///
/// Note that the produced items is a `Result`, to allow not terminating the stream on
/// protocol-level errors.
#[derive(Debug, Default)]
pub struct Line(usize);

impl Line {
    /// A constructor
    pub fn new() -> Self {
        Line(0)
    }
}

impl PositionCache for Line {
    fn position(&mut self) -> &mut usize {
        &mut self.0
    }
}

impl Encoder for Line {
    type Item = Message;
    type Error = Error;
    fn encode(&mut self, msg: Message, buf: &mut BytesMut) -> IoResult<()> {
        encode_codec(&msg, buf)
    }
}

impl Decoder for Line {
    type Item = Parsed;
    type Error = Error;
    fn decode(&mut self, src: &mut BytesMut) -> IoResult<Option<Parsed>> {
        decode_codec(self, src, from_slice)
    }
}

/// A codec working with JSONRPC 2.0 messages on top of badly encoded utf-8.
///
/// This works like the [Line](struct.Line.html) codec. However, it can cope with the input not
/// being valid utf-8. That is arguably broken, nevertheless found in the wild and sometimes the
/// only thing left to be done is to cope with it. This copes with it by running the input through
/// the `String::from_utf8_lossy` conversion, effectively replacing anything that is not valid with
/// these special utf-8 WTF question marks (U+FFFD).
///
/// In contrast, Line errors on such invalid inputs. Encoding is the same for both codecs, however.
#[derive(Debug, Default)]
pub struct DirtyLine(usize);

impl DirtyLine {
    /// A constructor
    pub fn new() -> Self {
        DirtyLine(0)
    }
}

impl PositionCache for DirtyLine {
    fn position(&mut self) -> &mut usize {
        &mut self.0
    }
}

impl Decoder for DirtyLine {
    type Item = Parsed;
    type Error = Error;
    fn decode(&mut self, src: &mut BytesMut) -> IoResult<Option<Parsed>> {
        decode_codec(self, src, |bytes| {
            from_str(String::from_utf8_lossy(bytes).as_ref())
        })
    }
}

impl Encoder for DirtyLine {
    type Item = Message;
    type Error = Error;
    fn encode(&mut self, msg: Message, buf: &mut BytesMut) -> IoResult<()> {
        encode_codec(&msg, buf)
    }
}

/// A codec working with JSONRPC 2.0 messages.
///
/// This produces or encodes [Message](../message/enum.Message.html). It takes the JSON object
/// boundaries, so it works with both newline-separated and object-separated encoding. It produces
/// newline-separated stream, which is more generic.
pub struct Boundary;

impl Encoder for Boundary {
    type Item = Message;
    type Error = Error;
    fn encode(&mut self, msg: Message, buf: &mut BytesMut) -> IoResult<()> {
        encode_codec(&msg, buf)
    }
}

impl Decoder for Boundary {
    type Item = Parsed;
    type Error = Error;
    fn decode(&mut self, src: &mut BytesMut) -> IoResult<Option<Parsed>> {
        let (decoded, pos) = {
            let mut deserializer = Deserializer::from_slice(src).into_iter();
            let decoded = deserializer.next().and_then(|result| match result {
                Err(ref e) if e.is_eof() => None,
                other => Some(decoded_to_parsed(other)),
            });
            (decoded, deserializer.byte_offset())
        };

        // It did read some data from the input. Find out how many and cut them off.
        src.split_to(pos);
        Ok(decoded)
    }
}

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

    #[test]
    fn encode() {
        let mut output = BytesMut::with_capacity(10);
        let mut codec = Line::new();
        let msg = Message::notification("notif".to_owned(), None);
        let encoded = BytesMut::from(&b"{\"jsonrpc\":\"2.0\",\"method\":\"notif\"}\n"[..]);
        codec.encode(msg.clone(), &mut output).unwrap();
        assert_eq!(encoded, output);
        let mut dirty_codec = DirtyLine::new();
        output.clear();
        dirty_codec.encode(msg, &mut output).unwrap();
        assert_eq!(encoded, output);
    }

    fn get_buf(input: &[u8]) -> BytesMut {
        BytesMut::from(input)
    }

    #[test]
    fn decode() {
        fn one(input: &[u8], rest: &[u8]) -> IoResult<Option<Parsed>> {
            let mut codec = Line::new();
            let mut buf = get_buf(input);
            let result = codec.decode(&mut buf);
            assert_eq!(rest, &buf);
            // On all the valid inputs, DirtyLine should act the same as Line
            let mut dirty_codec = DirtyLine::new();
            let mut buf = get_buf(input);
            let dirty = dirty_codec.decode(&mut buf);
            assert_eq!(rest, &buf);
            assert_eq!(result.as_ref().unwrap(), dirty.as_ref().unwrap());
            result
        }

        let notif = Message::notification("notif".to_owned(), None);
        let msgstring = Vec::from(&b"{\"jsonrpc\":\"2.0\",\"method\":\"notif\"}\n"[..]);
        // A single message, nothing is left
        assert_eq!(one(&msgstring, b"").unwrap(), Some(Ok(notif.clone())));
        // The first message is decoded, the second stays in the buffer
        let mut twomsgs = msgstring.clone();
        twomsgs.extend_from_slice(&msgstring);
        assert_eq!(one(&twomsgs, &msgstring).unwrap(), Some(Ok(notif.clone())));
        // The second message is incomplete, but stays there
        let incomplete = Vec::from(&br#"{"jsonrpc": "2.0", "method":""#[..]);
        let mut oneandhalf = msgstring.clone();
        oneandhalf.extend_from_slice(&incomplete);
        assert_eq!(
            one(&oneandhalf, &incomplete).unwrap(),
            Some(Ok(notif.clone()))
        );
        // An incomplete message ‒ nothing gets out and everything stays
        assert_eq!(one(&incomplete, &incomplete).unwrap(), None);
        // A syntax error is reported as an error (and eaten, but that's no longer interesting)
        match one(b"{]\n", b"") {
            Ok(Some(Err(Broken::SyntaxError(_)))) => (),
            other => panic!("Something unexpected: {:?}", other),
        };
    }

    /// Test with invalid utf-8 in a string
    #[test]
    fn decode_nonunicode() {
        let broken_input = b"{\"jsonrpc\":\"2.0\",\"method\":\"Hello \xF0\x90\x80World\"}\n";
        let mut codec = Line::new();
        let mut buf = get_buf(broken_input);
        // The ordinary line codec gives up
        let result = codec.decode(&mut buf).unwrap();
        match result {
            Some(Err(Broken::SyntaxError(_))) => (),
            other => panic!("Something unexpected: {:?}", other),
        };
        buf = get_buf(broken_input);
        // But the dirty one just keeps going on
        let mut dirty = DirtyLine::new();
        let result = dirty.decode(&mut buf).unwrap();
        assert_eq!(
            result,
            Some(Ok(Message::notification("Hello �World".to_owned(), None)))
        );
    }

    /// Not enough data for a whole message
    #[test]
    fn decode_boundary_short() {
        let mut buf = get_buf(b"{\"jsonrpc\":\"");
        assert!(Boundary.decode(&mut buf).unwrap().is_none());
        assert_eq!(&buf, &b"{\"jsonrpc\":\""[..]);
    }

    /// There's more than one message, but not two.
    ///
    /// And they are not separated by whitespace (to test the boundary decoding).
    #[test]
    fn decode_boundary_prefix() {
        let mut buf = get_buf(b"\n\n {\"jsonrpc\":\"2.0\",\"method\":\"notif\"}{\"");
        assert_eq!(
            Boundary.decode(&mut buf).unwrap().unwrap(),
            Ok(Message::notification("notif".to_owned(), None))
        );
        assert_eq!(&buf, &b"{\""[..]);
    }
}
<|endoftext|>


<commit_before>use std::fmt;
use std::marker::PhantomFn;
use libc::{c_char, c_void};

use block::Block;
use runtime::{Class, Object, Sel};

pub struct Encoding {
    code: &'static str,
}

macro_rules! static_encoding {
    ($s:expr) => (Encoding { code: concat!($s, "\0") });
}

impl Encoding {
    pub fn as_str(&self) -> &str {
        &self.code[..self.code.len() - 1]
    }

    pub fn as_ptr(&self) -> *const c_char {
        self.as_str().as_ptr() as *const c_char
    }

    pub fn unknown() -> Encoding { static_encoding!("?") }
}

impl Clone for Encoding {
    fn clone(&self) -> Encoding {
        Encoding { code: self.code }
    }
}

impl PartialEq for Encoding {
    fn eq(&self, other: &Encoding) -> bool {
        self.as_str() == other.as_str()
    }
}

impl<'a> PartialEq<&'a str> for Encoding {
    fn eq(&self, other: &&'a str) -> bool {
        self.as_str() == *other
    }
}

impl fmt::Debug for Encoding {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.as_str())
    }
}

/// Types that have an Objective-C type encoding.
///
/// For more information, see Apple's documentation:
/// https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html
pub trait Encode : PhantomFn<Self> {
    /// Returns the Objective-C type encoding for Self.
    fn encode() -> Encoding;
}

macro_rules! encode_impls {
    ($($t:ty : $s:expr,)*) => ($(
        impl Encode for $t {
            fn encode() -> Encoding { static_encoding!($s) }
        }
    )*);
}

encode_impls!(
    i8: "c",
    i16: "s",
    i32: "i",
    i64: "q",
    u8: "C",
    u16: "S",
    u32: "I",
    u64: "Q",
    f32: "f",
    f64: "d",
    bool: "B",
    (): "v",
    *mut c_char: "*",
    *const c_char: "r*",
    *mut c_void: "^v",
    *const c_void: "r^v",
    Sel: ":",
);

impl Encode for isize {
    #[cfg(target_pointer_width = "32")]
    fn encode() -> Encoding { i32::encode() }

    #[cfg(target_pointer_width = "64")]
    fn encode() -> Encoding { i64::encode() }
}

impl Encode for usize {
    #[cfg(target_pointer_width = "32")]
    fn encode() -> Encoding { u32::encode() }

    #[cfg(target_pointer_width = "64")]
    fn encode() -> Encoding { u64::encode() }
}

macro_rules! encode_message_impl {
    ($code:expr, $name:ident) => (
        encode_message_impl!($code, $name,);
    );
    ($code:expr, $name:ident, $($t:ident),*) => (
        impl<'a $(, $t)*> $crate::Encode for &'a $name<$($t),*> {
            fn encode() -> Encoding { static_encoding!($code) }
        }

        impl<'a $(, $t)*> $crate::Encode for &'a mut $name<$($t),*> {
            fn encode() -> Encoding { static_encoding!($code) }
        }

        impl<'a $(, $t)*> $crate::Encode for Option<&'a $name<$($t),*>> {
            fn encode() -> Encoding { static_encoding!($code) }
        }

        impl<'a $(, $t)*> $crate::Encode for Option<&'a mut $name<$($t),*>> {
            fn encode() -> Encoding { static_encoding!($code) }
        }

        impl<$($t),*> $crate::Encode for *const $name<$($t),*> {
            fn encode() -> Encoding { static_encoding!($code) }
        }

        impl<$($t),*> $crate::Encode for *mut $name<$($t),*> {
            fn encode() -> Encoding { static_encoding!($code) }
        }
    );
}

encode_message_impl!("@", Object);

encode_message_impl!("#", Class);

encode_message_impl!("@?", Block, A, R);

#[cfg(test)]
mod tests {
    use runtime::{Class, Object, Sel};
    use super::Encode;

    #[test]
    fn test_encode() {
        assert!(u32::encode() == "I");
        assert!(<()>::encode() == "v");
        assert!(<&Object>::encode() == "@");
        assert!(<*mut Object>::encode() == "@");
        assert!(<&Class>::encode() == "#");
        assert!(Sel::encode() == ":");
    }
}
<commit_msg>Added docs for Encoding.<commit_after>use std::fmt;
use std::marker::PhantomFn;
use libc::{c_char, c_void};

use block::Block;
use runtime::{Class, Object, Sel};

/// An Objective-C type encoding.
///
/// For more information, see Apple's documentation:
/// https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html
pub struct Encoding {
    code: &'static str,
}

macro_rules! static_encoding {
    ($s:expr) => (Encoding { code: concat!($s, "\0") });
}

impl Encoding {
    /// Returns self as a `str`.
    pub fn as_str(&self) -> &str {
        &self.code[..self.code.len() - 1]
    }

    /// Returns self as a pointer to a nul-terminated C string.
    pub fn as_ptr(&self) -> *const c_char {
        self.as_str().as_ptr() as *const c_char
    }

    /// Returns the `Encoding` for an unknown type.
    pub fn unknown() -> Encoding { static_encoding!("?") }
}

impl Clone for Encoding {
    fn clone(&self) -> Encoding {
        Encoding { code: self.code }
    }
}

impl PartialEq for Encoding {
    fn eq(&self, other: &Encoding) -> bool {
        self.as_str() == other.as_str()
    }
}

impl<'a> PartialEq<&'a str> for Encoding {
    fn eq(&self, other: &&'a str) -> bool {
        self.as_str() == *other
    }
}

impl fmt::Debug for Encoding {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.as_str())
    }
}

/// Types that have an Objective-C type encoding.
pub trait Encode : PhantomFn<Self> {
    /// Returns the Objective-C type encoding for Self.
    fn encode() -> Encoding;
}

macro_rules! encode_impls {
    ($($t:ty : $s:expr,)*) => ($(
        impl Encode for $t {
            fn encode() -> Encoding { static_encoding!($s) }
        }
    )*);
}

encode_impls!(
    i8: "c",
    i16: "s",
    i32: "i",
    i64: "q",
    u8: "C",
    u16: "S",
    u32: "I",
    u64: "Q",
    f32: "f",
    f64: "d",
    bool: "B",
    (): "v",
    *mut c_char: "*",
    *const c_char: "r*",
    *mut c_void: "^v",
    *const c_void: "r^v",
    Sel: ":",
);

impl Encode for isize {
    #[cfg(target_pointer_width = "32")]
    fn encode() -> Encoding { i32::encode() }

    #[cfg(target_pointer_width = "64")]
    fn encode() -> Encoding { i64::encode() }
}

impl Encode for usize {
    #[cfg(target_pointer_width = "32")]
    fn encode() -> Encoding { u32::encode() }

    #[cfg(target_pointer_width = "64")]
    fn encode() -> Encoding { u64::encode() }
}

macro_rules! encode_message_impl {
    ($code:expr, $name:ident) => (
        encode_message_impl!($code, $name,);
    );
    ($code:expr, $name:ident, $($t:ident),*) => (
        impl<'a $(, $t)*> $crate::Encode for &'a $name<$($t),*> {
            fn encode() -> Encoding { static_encoding!($code) }
        }

        impl<'a $(, $t)*> $crate::Encode for &'a mut $name<$($t),*> {
            fn encode() -> Encoding { static_encoding!($code) }
        }

        impl<'a $(, $t)*> $crate::Encode for Option<&'a $name<$($t),*>> {
            fn encode() -> Encoding { static_encoding!($code) }
        }

        impl<'a $(, $t)*> $crate::Encode for Option<&'a mut $name<$($t),*>> {
            fn encode() -> Encoding { static_encoding!($code) }
        }

        impl<$($t),*> $crate::Encode for *const $name<$($t),*> {
            fn encode() -> Encoding { static_encoding!($code) }
        }

        impl<$($t),*> $crate::Encode for *mut $name<$($t),*> {
            fn encode() -> Encoding { static_encoding!($code) }
        }
    );
}

encode_message_impl!("@", Object);

encode_message_impl!("#", Class);

encode_message_impl!("@?", Block, A, R);

#[cfg(test)]
mod tests {
    use runtime::{Class, Object, Sel};
    use super::Encode;

    #[test]
    fn test_encode() {
        assert!(u32::encode() == "I");
        assert!(<()>::encode() == "v");
        assert!(<&Object>::encode() == "@");
        assert!(<*mut Object>::encode() == "@");
        assert!(<&Class>::encode() == "#");
        assert!(Sel::encode() == ":");
    }
}
<|endoftext|>


<commit_before>/*
 * This file is part of the uutils coreutils package.
 *
 * (c) Alex Lyon <arcterus@mail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

// #[macro_export]
// macro_rules! main { ($($arg:tt)+) => ({
//     extern crate uu_arch;
//     use std::io::Write;
//     use uu_arch::uumain;

//     fn main() {
//         uucore::panic::install_sigpipe_hook();

//         let code = uumain(uucore::args().collect());
//         // Since stdout is line-buffered by default, we need to ensure any pending
//         // writes are flushed before exiting. Ideally, this should be enforced by
//         // each utility.
//         //
//         // See: https://github.com/rust-lang/rust/issues/23818
//         //
//         std::io::stdout().flush().expect("could not flush stdout");
//         std::process::exit(code);
//     }
// })}

// extern crate proc_macro;
// use proc_macro::TokenStream;
// #[proc_macro_attribute]
// pub fn hello(attr: TokenStream, item: TokenStream) -> TokenStream {
//     let result = quote! {
//         fn main() {
//             uucore::panic::install_sigpipe_hook();
//             std::io::stdout().flush().expect("could not flush stdout");
//             std::process::exit(code);
//         }
//     };
// }

#[macro_export]
macro_rules! executable(
    () => ({
        let module = module_path!();
        let module = module.split("::").next().unwrap_or(module);
        if &module[0..3] == "uu_" {
            &module[3..]
        } else {
            module
        }
    })
);

#[macro_export]
macro_rules! show_error(
    ($($args:tt)+) => ({
        eprint!("{}: error: ", executable!());
        eprintln!($($args)+);
    })
);

#[macro_export]
macro_rules! show_warning(
    ($($args:tt)+) => ({
        eprint!("{}: warning: ", executable!());
        eprintln!($($args)+);
    })
);

#[macro_export]
macro_rules! show_info(
    ($($args:tt)+) => ({
        eprint!("{}: ", executable!());
        eprintln!($($args)+);
    })
);

#[macro_export]
macro_rules! show_usage_error(
    ($($args:tt)+) => ({
        eprint!("{}: ", executable!());
        eprintln!($($args)+);
        eprintln!("Try '{} --help' for more information.", executable!());
    })
);

#[macro_export]
macro_rules! crash(
    ($exitcode:expr, $($args:tt)+) => ({
        show_error!($($args)+);
        ::std::process::exit($exitcode)
    })
);

#[macro_export]
macro_rules! exit(
    ($exitcode:expr) => ({
        ::std::process::exit($exitcode)
    })
);

#[macro_export]
macro_rules! crash_if_err(
    ($exitcode:expr, $exp:expr) => (
        match $exp {
            Ok(m) => m,
            Err(f) => crash!($exitcode, "{}", f),
        }
    )
);

#[macro_export]
macro_rules! return_if_err(
    ($exitcode:expr, $exp:expr) => (
        match $exp {
            Ok(m) => m,
            Err(f) => {
                show_error!("{}", f);
                return $exitcode;
            }
        }
    )
);

#[macro_export]
macro_rules! safe_write(
    ($fd:expr, $($args:tt)+) => (
        match write!($fd, $($args)+) {
            Ok(_) => {}
            Err(f) => panic!(f.to_string())
        }
    )
);

#[macro_export]
macro_rules! safe_writeln(
    ($fd:expr, $($args:tt)+) => (
        match writeln!($fd, $($args)+) {
            Ok(_) => {}
            Err(f) => panic!(f.to_string())
        }
    )
);

#[macro_export]
macro_rules! safe_unwrap(
    ($exp:expr) => (
        match $exp {
            Ok(m) => m,
            Err(f) => crash!(1, "{}", f.to_string())
        }
    )
);

//-- message templates

//-- message templates : general

#[macro_export]
macro_rules! snippet_list_join_oxford {
    ($conjunction:expr, $valOne:expr, $valTwo:expr) => (
        format!("{}, {} {}", $valOne, $conjunction, $valTwo)
    );
    ($conjunction:expr, $valOne:expr, $valTwo:expr $(, $remainingVals:expr)*) => (
        format!("{}, {}", $valOne, snippet_list_join_inner!($conjunction, $valTwo $(, $remainingVals)*))
    );
}

#[macro_export]
macro_rules! snippet_list_join_or {
    ($valOne:expr, $valTwo:expr) => (
        format!("{} or {}", $valOne, $valTwo)
    );
    ($valOne:expr, $valTwo:expr $(, $remainingVals:expr)*) => (
        format!("{}, {}", $valOne, snippet_list_join_oxford!("or", $valTwo $(, $remainingVals)*))
    );
}

//-- message templates : invalid input

#[macro_export]
macro_rules! msg_invalid_input {
    ($reason: expr) => {
        format!("invalid input: {}", $reason)
    };
}

#[macro_export]
macro_rules! snippet_no_file_at_path {
    ($path:expr) => {
        format!("nonexistent path {}", $path)
    };
}

// -- message templates : invalid input : flag

#[macro_export]
macro_rules! msg_invalid_opt_use {
    ($about:expr, $flag:expr) => {
        msg_invalid_input!(format!("The '{}' option {}", $flag, $about))
    };
    ($about:expr, $longflag:expr, $shortflag:expr) => {
        msg_invalid_input!(format!(
            "The '{}' ('{}') option {}",
            $longflag, $shortflag, $about
        ))
    };
}

#[macro_export]
macro_rules! msg_opt_only_usable_if {
    ($clause:expr, $flag:expr) => {
        msg_invalid_opt_use!(format!("only usable if {}", $clause), $flag)
    };
    ($clause:expr, $longflag:expr, $shortflag:expr) => {
        msg_invalid_opt_use!(format!("only usable if {}", $clause), $longflag, $shortflag)
    };
}

#[macro_export]
macro_rules! msg_opt_invalid_should_be {
    ($expects:expr, $received:expr, $flag:expr) => {
        msg_invalid_opt_use!(
            format!("expects {}, but was provided {}", $expects, $received),
            $flag
        )
    };
    ($expects:expr, $received:expr, $longflag:expr, $shortflag:expr) => {
        msg_invalid_opt_use!(
            format!("expects {}, but was provided {}", $expects, $received),
            $longflag,
            $shortflag
        )
    };
}

// -- message templates : invalid input : args

#[macro_export]
macro_rules! msg_arg_invalid_value {
    ($expects:expr, $received:expr) => {
        msg_invalid_input!(format!(
            "expects its argument to be {}, but was provided {}",
            $expects, $received
        ))
    };
}

#[macro_export]
macro_rules! msg_args_invalid_value {
    ($expects:expr, $received:expr) => {
        msg_invalid_input!(format!(
            "expects its arguments to be {}, but was provided {}",
            $expects, $received
        ))
    };
    ($msg:expr) => {
        msg_invalid_input!($msg)
    };
}

#[macro_export]
macro_rules! msg_args_nonexistent_file {
    ($received:expr) => {
        msg_args_invalid_value!("paths to files", snippet_no_file_at_path!($received))
    };
}

#[macro_export]
macro_rules! msg_wrong_number_of_arguments {
    () => {
        msg_args_invalid_value!("wrong number of arguments")
    };
    ($min:expr, $max:expr) => {
        msg_args_invalid_value!(format!("expects {}-{} arguments", $min, $max))
    };
    ($exact:expr) => {
        if $exact == 1 {
            msg_args_invalid_value!("expects 1 argument")
        } else {
            msg_args_invalid_value!(format!("expects {} arguments", $exact))
        }
    };
}

// -- message templates : invalid input : input combinations

#[macro_export]
macro_rules! msg_expects_one_of {
    ($valOne:expr $(, $remainingVals:expr)*) => (
        msg_invalid_input!(format!("expects one of {}", snippet_list_join_or!($valOne $(, $remainingVals)*)))
    );
}

#[macro_export]
macro_rules! msg_expects_no_more_than_one_of {
    ($valOne:expr $(, $remainingVals:expr)*) => (
        msg_invalid_input!(format!("expects no more than one of {}", snippet_list_join_or!($valOne $(, $remainingVals)*))) ;
    );
}
<commit_msg>docs/polish ~ remove outdated commentary<commit_after>/*
 * This file is part of the uutils coreutils package.
 *
 * (c) Alex Lyon <arcterus@mail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

#[macro_export]
macro_rules! executable(
    () => ({
        let module = module_path!();
        let module = module.split("::").next().unwrap_or(module);
        if &module[0..3] == "uu_" {
            &module[3..]
        } else {
            module
        }
    })
);

#[macro_export]
macro_rules! show_error(
    ($($args:tt)+) => ({
        eprint!("{}: error: ", executable!());
        eprintln!($($args)+);
    })
);

#[macro_export]
macro_rules! show_warning(
    ($($args:tt)+) => ({
        eprint!("{}: warning: ", executable!());
        eprintln!($($args)+);
    })
);

#[macro_export]
macro_rules! show_info(
    ($($args:tt)+) => ({
        eprint!("{}: ", executable!());
        eprintln!($($args)+);
    })
);

#[macro_export]
macro_rules! show_usage_error(
    ($($args:tt)+) => ({
        eprint!("{}: ", executable!());
        eprintln!($($args)+);
        eprintln!("Try '{} --help' for more information.", executable!());
    })
);

#[macro_export]
macro_rules! crash(
    ($exitcode:expr, $($args:tt)+) => ({
        show_error!($($args)+);
        ::std::process::exit($exitcode)
    })
);

#[macro_export]
macro_rules! exit(
    ($exitcode:expr) => ({
        ::std::process::exit($exitcode)
    })
);

#[macro_export]
macro_rules! crash_if_err(
    ($exitcode:expr, $exp:expr) => (
        match $exp {
            Ok(m) => m,
            Err(f) => crash!($exitcode, "{}", f),
        }
    )
);

#[macro_export]
macro_rules! return_if_err(
    ($exitcode:expr, $exp:expr) => (
        match $exp {
            Ok(m) => m,
            Err(f) => {
                show_error!("{}", f);
                return $exitcode;
            }
        }
    )
);

#[macro_export]
macro_rules! safe_write(
    ($fd:expr, $($args:tt)+) => (
        match write!($fd, $($args)+) {
            Ok(_) => {}
            Err(f) => panic!(f.to_string())
        }
    )
);

#[macro_export]
macro_rules! safe_writeln(
    ($fd:expr, $($args:tt)+) => (
        match writeln!($fd, $($args)+) {
            Ok(_) => {}
            Err(f) => panic!(f.to_string())
        }
    )
);

#[macro_export]
macro_rules! safe_unwrap(
    ($exp:expr) => (
        match $exp {
            Ok(m) => m,
            Err(f) => crash!(1, "{}", f.to_string())
        }
    )
);

//-- message templates

//-- message templates : general

#[macro_export]
macro_rules! snippet_list_join_oxford {
    ($conjunction:expr, $valOne:expr, $valTwo:expr) => (
        format!("{}, {} {}", $valOne, $conjunction, $valTwo)
    );
    ($conjunction:expr, $valOne:expr, $valTwo:expr $(, $remainingVals:expr)*) => (
        format!("{}, {}", $valOne, snippet_list_join_inner!($conjunction, $valTwo $(, $remainingVals)*))
    );
}

#[macro_export]
macro_rules! snippet_list_join_or {
    ($valOne:expr, $valTwo:expr) => (
        format!("{} or {}", $valOne, $valTwo)
    );
    ($valOne:expr, $valTwo:expr $(, $remainingVals:expr)*) => (
        format!("{}, {}", $valOne, snippet_list_join_oxford!("or", $valTwo $(, $remainingVals)*))
    );
}

//-- message templates : invalid input

#[macro_export]
macro_rules! msg_invalid_input {
    ($reason: expr) => {
        format!("invalid input: {}", $reason)
    };
}

#[macro_export]
macro_rules! snippet_no_file_at_path {
    ($path:expr) => {
        format!("nonexistent path {}", $path)
    };
}

// -- message templates : invalid input : flag

#[macro_export]
macro_rules! msg_invalid_opt_use {
    ($about:expr, $flag:expr) => {
        msg_invalid_input!(format!("The '{}' option {}", $flag, $about))
    };
    ($about:expr, $longflag:expr, $shortflag:expr) => {
        msg_invalid_input!(format!(
            "The '{}' ('{}') option {}",
            $longflag, $shortflag, $about
        ))
    };
}

#[macro_export]
macro_rules! msg_opt_only_usable_if {
    ($clause:expr, $flag:expr) => {
        msg_invalid_opt_use!(format!("only usable if {}", $clause), $flag)
    };
    ($clause:expr, $longflag:expr, $shortflag:expr) => {
        msg_invalid_opt_use!(format!("only usable if {}", $clause), $longflag, $shortflag)
    };
}

#[macro_export]
macro_rules! msg_opt_invalid_should_be {
    ($expects:expr, $received:expr, $flag:expr) => {
        msg_invalid_opt_use!(
            format!("expects {}, but was provided {}", $expects, $received),
            $flag
        )
    };
    ($expects:expr, $received:expr, $longflag:expr, $shortflag:expr) => {
        msg_invalid_opt_use!(
            format!("expects {}, but was provided {}", $expects, $received),
            $longflag,
            $shortflag
        )
    };
}

// -- message templates : invalid input : args

#[macro_export]
macro_rules! msg_arg_invalid_value {
    ($expects:expr, $received:expr) => {
        msg_invalid_input!(format!(
            "expects its argument to be {}, but was provided {}",
            $expects, $received
        ))
    };
}

#[macro_export]
macro_rules! msg_args_invalid_value {
    ($expects:expr, $received:expr) => {
        msg_invalid_input!(format!(
            "expects its arguments to be {}, but was provided {}",
            $expects, $received
        ))
    };
    ($msg:expr) => {
        msg_invalid_input!($msg)
    };
}

#[macro_export]
macro_rules! msg_args_nonexistent_file {
    ($received:expr) => {
        msg_args_invalid_value!("paths to files", snippet_no_file_at_path!($received))
    };
}

#[macro_export]
macro_rules! msg_wrong_number_of_arguments {
    () => {
        msg_args_invalid_value!("wrong number of arguments")
    };
    ($min:expr, $max:expr) => {
        msg_args_invalid_value!(format!("expects {}-{} arguments", $min, $max))
    };
    ($exact:expr) => {
        if $exact == 1 {
            msg_args_invalid_value!("expects 1 argument")
        } else {
            msg_args_invalid_value!(format!("expects {} arguments", $exact))
        }
    };
}

// -- message templates : invalid input : input combinations

#[macro_export]
macro_rules! msg_expects_one_of {
    ($valOne:expr $(, $remainingVals:expr)*) => (
        msg_invalid_input!(format!("expects one of {}", snippet_list_join_or!($valOne $(, $remainingVals)*)))
    );
}

#[macro_export]
macro_rules! msg_expects_no_more_than_one_of {
    ($valOne:expr $(, $remainingVals:expr)*) => (
        msg_invalid_input!(format!("expects no more than one of {}", snippet_list_join_or!($valOne $(, $remainingVals)*))) ;
    );
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */

use crate::dom::bindings::codegen::Bindings::XRSpaceBinding;
use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::reflector::reflect_dom_object;
use crate::dom::bindings::root::{Dom, DomRoot};
use crate::dom::eventtarget::EventTarget;
use crate::dom::globalscope::GlobalScope;
use crate::dom::xrreferencespace::XRReferenceSpace;
use crate::dom::xrsession::XRSession;
use dom_struct::dom_struct;
use euclid::Transform3D;
use webvr_traits::WebVRFrameData;

#[dom_struct]
pub struct XRSpace {
    eventtarget: EventTarget,
    session: Dom<XRSession>,
}

impl XRSpace {
    pub fn new_inherited(session: &XRSession) -> XRSpace {
        XRSpace {
            eventtarget: EventTarget::new_inherited(),
            session: Dom::from_ref(session),
        }
    }

    #[allow(unused)]
    pub fn new(global: &GlobalScope, session: &XRSession) -> DomRoot<XRSpace> {
        reflect_dom_object(
            Box::new(XRSpace::new_inherited(session)),
            global,
            XRSpaceBinding::Wrap,
        )
    }
}

impl XRSpace {
    /// Gets viewer pose represented by this space
    pub fn get_viewer_pose(&self, base_pose: &WebVRFrameData) -> Transform3D<f64> {
        if let Some(reference) = self.downcast::<XRReferenceSpace>() {
            reference.get_viewer_pose(base_pose)
        } else {
            unreachable!()
        }
    }

    /// Gets pose represented by this space
    ///
    /// Does not apply originOffset, use get_viewer_pose instead if you need it
    pub fn get_pose(&self, base_pose: &WebVRFrameData) -> Transform3D<f64> {
        if let Some(reference) = self.downcast::<XRReferenceSpace>() {
            reference.get_pose(base_pose)
        } else {
            unreachable!()
        }
    }
}
<commit_msg>Allow unused<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */

use crate::dom::bindings::codegen::Bindings::XRSpaceBinding;
use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::reflector::reflect_dom_object;
use crate::dom::bindings::root::{Dom, DomRoot};
use crate::dom::eventtarget::EventTarget;
use crate::dom::globalscope::GlobalScope;
use crate::dom::xrreferencespace::XRReferenceSpace;
use crate::dom::xrsession::XRSession;
use dom_struct::dom_struct;
use euclid::Transform3D;
use webvr_traits::WebVRFrameData;

#[dom_struct]
pub struct XRSpace {
    eventtarget: EventTarget,
    session: Dom<XRSession>,
}

impl XRSpace {
    pub fn new_inherited(session: &XRSession) -> XRSpace {
        XRSpace {
            eventtarget: EventTarget::new_inherited(),
            session: Dom::from_ref(session),
        }
    }

    #[allow(unused)]
    pub fn new(global: &GlobalScope, session: &XRSession) -> DomRoot<XRSpace> {
        reflect_dom_object(
            Box::new(XRSpace::new_inherited(session)),
            global,
            XRSpaceBinding::Wrap,
        )
    }
}

impl XRSpace {
    /// Gets viewer pose represented by this space
    #[allow(unused)]
    pub fn get_viewer_pose(&self, base_pose: &WebVRFrameData) -> Transform3D<f64> {
        if let Some(reference) = self.downcast::<XRReferenceSpace>() {
            reference.get_viewer_pose(base_pose)
        } else {
            unreachable!()
        }
    }

    /// Gets pose represented by this space
    ///
    /// Does not apply originOffset, use get_viewer_pose instead if you need it
    #[allow(unused)]
    pub fn get_pose(&self, base_pose: &WebVRFrameData) -> Transform3D<f64> {
        if let Some(reference) = self.downcast::<XRReferenceSpace>() {
            reference.get_pose(base_pose)
        } else {
            unreachable!()
        }
    }
}
<|endoftext|>


<commit_before>use std::borrow::Cow;
use std::ffi::OsStr;
use std::path::{Path, PathBuf};

use globset;
use log::debug;
use walkdir;

use crate::template::{self, TemplateInput};
use crate::version::{self, Version};

use super::{config, VutConfig, VutError};

#[derive(Debug)]
struct TemplateSpec<'a> {
    include_globset: globset::GlobSet,
    def: &'a config::TemplateDef,
}

impl<'a> TemplateSpec<'a> {
    pub fn from_config_template(def: &'a config::TemplateDef) -> Result<Self, VutError> {
        let include_globset = def.globs.build_globset()?;

        Ok(Self { include_globset, def })
    }
}

pub fn generate_template_output(
    config: &VutConfig,
    root_path: &Path,
    dir_entries: &[walkdir::DirEntry],
    template_input: &TemplateInput,
) -> Result<(), VutError> {
    let specs = build_template_specs(config)?;

    let mut processed_files: Vec<PathBuf> = Vec::new();
    let mut generated_files: Vec<PathBuf> = Vec::new();

    for spec in specs.iter() {
        debug!("{:?}", spec);

        let include_globset = &spec.include_globset;
        let def = spec.def;

        let start_path: Cow<Path> = match &def.start_path {
            Some(p) => Cow::Owned(root_path.join(p)),
            None => Cow::Borrowed(root_path),
        };

        let output_path = match &def.output_path {
            Some(p) => Cow::Owned(root_path.join(p)),
            None => start_path.clone(),
        };

        let processor = def.processor.as_ref().map(|s| s.as_str()).unwrap_or("vut");
        let encoding = def.encoding.as_ref().map(|s| s.as_str());

        let template_files_iter = dir_entries
            .iter()
            .map(|entry| entry.path())
            // Exclude files outside the start path
            .filter(|path| path.starts_with(&start_path))
            // Only include template files
            .filter_map(|path| {
                // Make path relative, as we only want to match on the path
                // relative to the start path.
                let rel_path = path.strip_prefix(&start_path).unwrap();

                if include_globset.is_match(rel_path) {
                    Some((path, rel_path))
                } else {
                    None
                }
            });

        for (path, rel_path) in template_files_iter {
            debug!("Processing template {}", path.display());

            let output_file_name: &OsStr = path.file_stem().unwrap();
            let output_file_path = output_path.join(rel_path.with_file_name(output_file_name));

            template::generate_template_with_processor_name(
                processor,
                path,
                &output_file_path,
                &template_input,
                encoding,
            )
            .map_err(|err| VutError::TemplateGenerate(err))?;

            processed_files.push(path.to_path_buf());
            generated_files.push(output_file_path);
        }
    }

    Ok(())
}

pub fn generate_template_input(version: &Version) -> Result<TemplateInput, VutError> {
    let mut template_input = TemplateInput::new();

    let split_prerelease = version
        .prerelease
        .as_ref()
        .map_or(None, |p| version::split_numbered_prerelease(p));
    let split_build = version
        .build
        .as_ref()
        .map_or(None, |b| version::split_numbered_prerelease(b));

    template_input
        .values
        .insert("FullVersion".to_owned(), version.to_string());
    template_input.values.insert(
        "Version".to_owned(),
        Version {
            build: None,
            ..version.clone()
        }
        .to_string(),
    );
    template_input.values.insert(
        "MajorMinorPatch".to_owned(),
        format!("{}.{}.{}", version.major, version.minor, version.patch),
    );
    template_input
        .values
        .insert("MajorMinor".to_owned(), format!("{}.{}", version.major, version.minor));
    template_input
        .values
        .insert("Major".to_owned(), format!("{}", version.major));
    template_input
        .values
        .insert("Minor".to_owned(), format!("{}", version.minor));
    template_input
        .values
        .insert("Patch".to_owned(), format!("{}", version.patch));
    template_input.values.insert(
        "Prerelease".to_owned(),
        version.prerelease.as_ref().map_or("", |p| p).to_owned(),
    );
    template_input.values.insert(
        "PrereleasePrefix".to_owned(),
        split_prerelease
            .and_then(|sp| Some(sp.0.to_owned()))
            .unwrap_or_else(|| "".to_owned()),
    );
    template_input.values.insert(
        "PrereleaseNumber".to_owned(),
        split_prerelease
            .and_then(|sp| Some(format!("{}", sp.1)))
            .unwrap_or_else(|| "".to_owned()),
    );
    template_input
        .values
        .insert("Build".to_owned(), version.build.as_ref().map_or("", |b| b).to_owned());
    template_input.values.insert(
        "BuildPrefix".to_owned(),
        split_build
            .and_then(|sp| Some(sp.0.to_owned()))
            .unwrap_or_else(|| "".to_owned()),
    );
    template_input.values.insert(
        "BuildNumber".to_owned(),
        split_build
            .and_then(|sp| Some(format!("{}", sp.1)))
            .unwrap_or_else(|| "".to_owned()),
    );

    Ok(template_input)
}

fn build_template_specs(config: &VutConfig) -> Result<Vec<TemplateSpec>, VutError> {
    let mut specs: Vec<TemplateSpec> = Vec::new();

    for cfg_t in config.template.iter() {
        let spec = TemplateSpec::from_config_template(cfg_t)?;

        specs.push(spec);
    }

    Ok(specs)
}
<commit_msg>refactor: Use intermediate variable in generate_template_input() for values to reduce line length<commit_after>use std::borrow::Cow;
use std::ffi::OsStr;
use std::path::{Path, PathBuf};

use globset;
use log::debug;
use walkdir;

use crate::template::{self, TemplateInput};
use crate::version::{self, Version};

use super::{config, VutConfig, VutError};

#[derive(Debug)]
struct TemplateSpec<'a> {
    include_globset: globset::GlobSet,
    def: &'a config::TemplateDef,
}

impl<'a> TemplateSpec<'a> {
    pub fn from_config_template(def: &'a config::TemplateDef) -> Result<Self, VutError> {
        let include_globset = def.globs.build_globset()?;

        Ok(Self { include_globset, def })
    }
}

pub fn generate_template_output(
    config: &VutConfig,
    root_path: &Path,
    dir_entries: &[walkdir::DirEntry],
    template_input: &TemplateInput,
) -> Result<(), VutError> {
    let specs = build_template_specs(config)?;

    let mut processed_files: Vec<PathBuf> = Vec::new();
    let mut generated_files: Vec<PathBuf> = Vec::new();

    for spec in specs.iter() {
        debug!("{:?}", spec);

        let include_globset = &spec.include_globset;
        let def = spec.def;

        let start_path: Cow<Path> = match &def.start_path {
            Some(p) => Cow::Owned(root_path.join(p)),
            None => Cow::Borrowed(root_path),
        };

        let output_path = match &def.output_path {
            Some(p) => Cow::Owned(root_path.join(p)),
            None => start_path.clone(),
        };

        let processor = def.processor.as_ref().map(|s| s.as_str()).unwrap_or("vut");
        let encoding = def.encoding.as_ref().map(|s| s.as_str());

        let template_files_iter = dir_entries
            .iter()
            .map(|entry| entry.path())
            // Exclude files outside the start path
            .filter(|path| path.starts_with(&start_path))
            // Only include template files
            .filter_map(|path| {
                // Make path relative, as we only want to match on the path
                // relative to the start path.
                let rel_path = path.strip_prefix(&start_path).unwrap();

                if include_globset.is_match(rel_path) {
                    Some((path, rel_path))
                } else {
                    None
                }
            });

        for (path, rel_path) in template_files_iter {
            debug!("Processing template {}", path.display());

            let output_file_name: &OsStr = path.file_stem().unwrap();
            let output_file_path = output_path.join(rel_path.with_file_name(output_file_name));

            template::generate_template_with_processor_name(
                processor,
                path,
                &output_file_path,
                &template_input,
                encoding,
            )
            .map_err(|err| VutError::TemplateGenerate(err))?;

            processed_files.push(path.to_path_buf());
            generated_files.push(output_file_path);
        }
    }

    Ok(())
}

pub fn generate_template_input(version: &Version) -> Result<TemplateInput, VutError> {
    let mut template_input = TemplateInput::new();
    let values = &mut template_input.values;

    let split_prerelease = version
        .prerelease
        .as_ref()
        .map_or(None, |p| version::split_numbered_prerelease(p));
    let split_build = version
        .build
        .as_ref()
        .map_or(None, |b| version::split_numbered_prerelease(b));

    values.insert("FullVersion".to_owned(), version.to_string());
    values.insert(
        "Version".to_owned(),
        Version {
            build: None,
            ..version.clone()
        }
        .to_string(),
    );
    values.insert(
        "MajorMinorPatch".to_owned(),
        format!("{}.{}.{}", version.major, version.minor, version.patch),
    );
    values.insert("MajorMinor".to_owned(), format!("{}.{}", version.major, version.minor));
    values.insert("Major".to_owned(), format!("{}", version.major));
    values.insert("Minor".to_owned(), format!("{}", version.minor));
    values.insert("Patch".to_owned(), format!("{}", version.patch));
    values.insert(
        "Prerelease".to_owned(),
        version.prerelease.as_ref().map_or("", |p| p).to_owned(),
    );
    values.insert(
        "PrereleasePrefix".to_owned(),
        split_prerelease
            .and_then(|sp| Some(sp.0.to_owned()))
            .unwrap_or_else(|| "".to_owned()),
    );
    values.insert(
        "PrereleaseNumber".to_owned(),
        split_prerelease
            .and_then(|sp| Some(format!("{}", sp.1)))
            .unwrap_or_else(|| "".to_owned()),
    );
    values.insert("Build".to_owned(), version.build.as_ref().map_or("", |b| b).to_owned());
    values.insert(
        "BuildPrefix".to_owned(),
        split_build
            .and_then(|sp| Some(sp.0.to_owned()))
            .unwrap_or_else(|| "".to_owned()),
    );
    values.insert(
        "BuildNumber".to_owned(),
        split_build
            .and_then(|sp| Some(format!("{}", sp.1)))
            .unwrap_or_else(|| "".to_owned()),
    );

    Ok(template_input)
}

fn build_template_specs(config: &VutConfig) -> Result<Vec<TemplateSpec>, VutError> {
    let mut specs: Vec<TemplateSpec> = Vec::new();

    for cfg_t in config.template.iter() {
        let spec = TemplateSpec::from_config_template(cfg_t)?;

        specs.push(spec);
    }

    Ok(specs)
}
<|endoftext|>