<commit_before>// Bloom
//
// HTTP REST API caching middleware
// Copyright: 2017, Valerian Saliou <valerian@valeriansaliou.name>
// License: Mozilla Public License v2.0 (MPL v2.0)

use futures::future::{self, Future};
use httparse;
use hyper::{Error, Method, StatusCode, Headers};
use hyper::header::{Origin, IfNoneMatch, ETag, EntityTag};
use hyper::server::{Request, Response};
use farmhash;

use super::header::ProxyHeader;
use super::tunnel::ProxyTunnel;
use header::janitor::HeaderJanitor;
use header::request_shard::HeaderRequestBloomRequestShard;
use header::status::{HeaderBloomStatus, HeaderBloomStatusValue};
use cache::read::CacheRead;
use cache::write::CacheWrite;
use cache::route::CacheRoute;
use LINE_FEED;

pub struct ProxyServe;

const CACHED_PARSE_MAX_HEADERS: usize = 100;

pub type ProxyServeFuture = Box<Future<Item = Response, Error = Error>>;

impl ProxyServe {
    pub fn handle(req: Request) -> ProxyServeFuture {
        info!("handled request: {} on {}", req.method(), req.path());

        if req.headers().has::<HeaderRequestBloomRequestShard>() == true {
            match *req.method() {
                Method::Options | Method::Head | Method::Get | Method::Post | Method::Patch |
                Method::Put | Method::Delete => Self::accept(req),
                _ => Self::reject(req, StatusCode::MethodNotAllowed),
            }
        } else {
            Self::reject(req, StatusCode::NotExtended)
        }
    }

    fn accept(req: Request) -> ProxyServeFuture {
        Self::tunnel(req)
    }

    fn reject(req: Request, status: StatusCode) -> ProxyServeFuture {
        let mut headers = Headers::new();

        headers.set::<HeaderBloomStatus>(HeaderBloomStatus(HeaderBloomStatusValue::Reject));

        Self::respond(&req.method(), status, headers, format!("{}", status))
    }

    fn tunnel(req: Request) -> ProxyServeFuture {
        let (method, uri, version, headers, body) = req.deconstruct();
        let (headers, auth, shard) = ProxyHeader::parse_from_request(headers);

        let auth_hash = CacheRoute::hash(&auth);

        let (ns, ns_mask) = CacheRoute::gen_key_cache(
            shard,
            &auth_hash,
            version,
            &method,
            uri.path(),
            uri.query(),
            headers.get::<Origin>(),
        );

        info!("tunneling for ns = {}", ns);

        Box::new(
            CacheRead::acquire(&ns, &method)
                .or_else(|_| Err(Error::Incomplete))
                .and_then(move |result| {
                    match result {
                        Ok(value) => Self::dispatch_cached(&method, &headers, &value),
                        Err(_) => {
                            // Clone method value for closures. Sadly, it looks like Rust borrow \
                            //   checker doesnt discriminate properly on this check.
                            let method_success = method.to_owned();
                            let method_failure = method.to_owned();

                            Box::new(
                                ProxyTunnel::run(&method, &uri, &headers, body, shard)
                                    .and_then(move |tunnel_res| {
                                        CacheWrite::save(
                                            ns,
                                            ns_mask,
                                            auth_hash,
                                            shard,
                                            method,
                                            version,
                                            tunnel_res.status(),
                                            tunnel_res.headers().to_owned(),
                                            tunnel_res.body(),
                                        )
                                    })
                                    .and_then(move |mut result| match result.body {
                                        Ok(body_string) => {
                                            Self::dispatch_fetched(
                                                &method_success,
                                                &result.status,
                                                result.headers,
                                                HeaderBloomStatusValue::Miss,
                                                body_string,
                                                result.value,
                                            )
                                        }
                                        Err(body_string_values) => {
                                            match body_string_values {
                                                Some(body_string) => {
                                                    // Enforce clean headers, has usually they get \
                                                    //   cleaned from cache writer
                                                    HeaderJanitor::clean(&mut result.headers);

                                                    Self::dispatch_fetched(
                                                        &method_success,
                                                        &result.status,
                                                        result.headers,
                                                        HeaderBloomStatusValue::Direct,
                                                        body_string,
                                                        result.value,
                                                    )
                                                }
                                                _ => Self::dispatch_failure(&method_success),
                                            }
                                        }
                                    })
                                    .or_else(move |_| Self::dispatch_failure(&method_failure)),
                            )
                        }
                    }
                }),
        )
    }

    fn dispatch_cached(method: &Method, headers: &Headers, res_string: &str) -> ProxyServeFuture {
        // Process ETag for cached content
        let (res_hash, res_etag) = Self::body_fingerprint(res_string);

        let isnt_modified = match headers.get::<IfNoneMatch>() {
            Some(req_if_none_match) => {
                match *req_if_none_match {
                    IfNoneMatch::Any => true,
                    IfNoneMatch::Items(ref req_etags) => {
                        if let Some(req_etag) = req_etags.first() {
                            req_etag.weak_eq(&EntityTag::new(false, res_hash))
                        } else {
                            false
                        }
                    }
                }
            }
            _ => false,
        };

        // Response not modified for client?
        if isnt_modified == true {
            // Process non-modified + cached headers
            let mut headers = Headers::new();

            ProxyHeader::set_common(&mut headers, res_etag);
            headers.set::<HeaderBloomStatus>(HeaderBloomStatus(HeaderBloomStatusValue::Hit));

            // Serve non-modified response
            return Self::respond(method, StatusCode::NotModified, headers, String::from(""));
        }

        // Response modified
        let mut headers = [httparse::EMPTY_HEADER; CACHED_PARSE_MAX_HEADERS];
        let mut res = httparse::Response::new(&mut headers);

        // Split headers from body
        let mut res_body_string = String::new();
        let mut is_last_line_empty = false;

        for res_line in res_string.lines() {
            if res_body_string.is_empty() == false || is_last_line_empty == true {
                // Write to body
                res_body_string.push_str(res_line.as_ref());
                res_body_string.push_str(LINE_FEED);
            }

            is_last_line_empty = res_line.is_empty();
        }

        match res.parse(res_string.as_bytes()) {
            Ok(_) => {
                // Process cached status
                let code = res.code.unwrap_or(500u16);
                let status = StatusCode::try_from(code).unwrap_or(StatusCode::Unregistered(code));

                // Process cached headers
                let mut headers = Headers::new();

                for header in res.headers {
                    if let (Ok(header_name), Ok(header_value)) =
                        (
                            String::from_utf8(Vec::from(header.name)),
                            String::from_utf8(Vec::from(header.value)),
                        )
                    {
                        headers.set_raw(header_name, header_value);
                    }
                }

                ProxyHeader::set_common(&mut headers, res_etag);
                headers.set::<HeaderBloomStatus>(HeaderBloomStatus(HeaderBloomStatusValue::Hit));

                // Serve cached response
                Self::respond(method, status, headers, res_body_string)
            }
            Err(err) => {
                error!("failed parsing cached response: {}", err);

                Self::dispatch_failure(method)
            }
        }
    }

    fn dispatch_fetched(
        method: &Method,
        status: &StatusCode,
        mut headers: Headers,
        bloom_status: HeaderBloomStatusValue,
        body_string: String,
        result_string: Option<String>,
    ) -> ProxyServeFuture {
        // Process ETag for content?
        if let Some(result_string_value) = result_string {
            let (_, res_etag) = Self::body_fingerprint(&result_string_value);

            ProxyHeader::set_common(&mut headers, res_etag);
        }

        headers.set(HeaderBloomStatus(bloom_status));

        Self::respond(method, *status, headers, body_string)
    }

    fn dispatch_failure(method: &Method) -> ProxyServeFuture {
        let status = StatusCode::BadGateway;

        let mut headers = Headers::new();

        headers.set::<HeaderBloomStatus>(HeaderBloomStatus(HeaderBloomStatusValue::Offline));

        Self::respond(method, status, headers, format!("{}", status))
    }

    fn body_fingerprint(body_string: &str) -> (String, ETag) {
        let body_hash = format!("{:x}", farmhash::fingerprint64(body_string.as_bytes()));
        let body_etag = ETag(EntityTag::new(false, body_hash.to_owned()));

        (body_hash, body_etag)
    }

    fn respond(
        method: &Method,
        status: StatusCode,
        headers: Headers,
        body_string: String,
    ) -> ProxyServeFuture {
        Box::new(future::ok(match method {
            &Method::Get | &Method::Post | &Method::Patch | &Method::Put => {
                Response::new()
                    .with_status(status)
                    .with_headers(headers)
                    .with_body(body_string)
            }
            _ => Response::new().with_status(status).with_headers(headers),
        }))
    }
}
<commit_msg>HTTP DELETE can have a response body<commit_after>// Bloom
//
// HTTP REST API caching middleware
// Copyright: 2017, Valerian Saliou <valerian@valeriansaliou.name>
// License: Mozilla Public License v2.0 (MPL v2.0)

use futures::future::{self, Future};
use httparse;
use hyper::{Error, Method, StatusCode, Headers};
use hyper::header::{Origin, IfNoneMatch, ETag, EntityTag};
use hyper::server::{Request, Response};
use farmhash;

use super::header::ProxyHeader;
use super::tunnel::ProxyTunnel;
use header::janitor::HeaderJanitor;
use header::request_shard::HeaderRequestBloomRequestShard;
use header::status::{HeaderBloomStatus, HeaderBloomStatusValue};
use cache::read::CacheRead;
use cache::write::CacheWrite;
use cache::route::CacheRoute;
use LINE_FEED;

pub struct ProxyServe;

const CACHED_PARSE_MAX_HEADERS: usize = 100;

pub type ProxyServeFuture = Box<Future<Item = Response, Error = Error>>;

impl ProxyServe {
    pub fn handle(req: Request) -> ProxyServeFuture {
        info!("handled request: {} on {}", req.method(), req.path());

        if req.headers().has::<HeaderRequestBloomRequestShard>() == true {
            match *req.method() {
                Method::Options | Method::Head | Method::Get | Method::Post | Method::Patch |
                Method::Put | Method::Delete => Self::accept(req),
                _ => Self::reject(req, StatusCode::MethodNotAllowed),
            }
        } else {
            Self::reject(req, StatusCode::NotExtended)
        }
    }

    fn accept(req: Request) -> ProxyServeFuture {
        Self::tunnel(req)
    }

    fn reject(req: Request, status: StatusCode) -> ProxyServeFuture {
        let mut headers = Headers::new();

        headers.set::<HeaderBloomStatus>(HeaderBloomStatus(HeaderBloomStatusValue::Reject));

        Self::respond(&req.method(), status, headers, format!("{}", status))
    }

    fn tunnel(req: Request) -> ProxyServeFuture {
        let (method, uri, version, headers, body) = req.deconstruct();
        let (headers, auth, shard) = ProxyHeader::parse_from_request(headers);

        let auth_hash = CacheRoute::hash(&auth);

        let (ns, ns_mask) = CacheRoute::gen_key_cache(
            shard,
            &auth_hash,
            version,
            &method,
            uri.path(),
            uri.query(),
            headers.get::<Origin>(),
        );

        info!("tunneling for ns = {}", ns);

        Box::new(
            CacheRead::acquire(&ns, &method)
                .or_else(|_| Err(Error::Incomplete))
                .and_then(move |result| {
                    match result {
                        Ok(value) => Self::dispatch_cached(&method, &headers, &value),
                        Err(_) => {
                            // Clone method value for closures. Sadly, it looks like Rust borrow \
                            //   checker doesnt discriminate properly on this check.
                            let method_success = method.to_owned();
                            let method_failure = method.to_owned();

                            Box::new(
                                ProxyTunnel::run(&method, &uri, &headers, body, shard)
                                    .and_then(move |tunnel_res| {
                                        CacheWrite::save(
                                            ns,
                                            ns_mask,
                                            auth_hash,
                                            shard,
                                            method,
                                            version,
                                            tunnel_res.status(),
                                            tunnel_res.headers().to_owned(),
                                            tunnel_res.body(),
                                        )
                                    })
                                    .and_then(move |mut result| match result.body {
                                        Ok(body_string) => {
                                            Self::dispatch_fetched(
                                                &method_success,
                                                &result.status,
                                                result.headers,
                                                HeaderBloomStatusValue::Miss,
                                                body_string,
                                                result.value,
                                            )
                                        }
                                        Err(body_string_values) => {
                                            match body_string_values {
                                                Some(body_string) => {
                                                    // Enforce clean headers, has usually they get \
                                                    //   cleaned from cache writer
                                                    HeaderJanitor::clean(&mut result.headers);

                                                    Self::dispatch_fetched(
                                                        &method_success,
                                                        &result.status,
                                                        result.headers,
                                                        HeaderBloomStatusValue::Direct,
                                                        body_string,
                                                        result.value,
                                                    )
                                                }
                                                _ => Self::dispatch_failure(&method_success),
                                            }
                                        }
                                    })
                                    .or_else(move |_| Self::dispatch_failure(&method_failure)),
                            )
                        }
                    }
                }),
        )
    }

    fn dispatch_cached(method: &Method, headers: &Headers, res_string: &str) -> ProxyServeFuture {
        // Process ETag for cached content
        let (res_hash, res_etag) = Self::body_fingerprint(res_string);

        let isnt_modified = match headers.get::<IfNoneMatch>() {
            Some(req_if_none_match) => {
                match *req_if_none_match {
                    IfNoneMatch::Any => true,
                    IfNoneMatch::Items(ref req_etags) => {
                        if let Some(req_etag) = req_etags.first() {
                            req_etag.weak_eq(&EntityTag::new(false, res_hash))
                        } else {
                            false
                        }
                    }
                }
            }
            _ => false,
        };

        // Response not modified for client?
        if isnt_modified == true {
            // Process non-modified + cached headers
            let mut headers = Headers::new();

            ProxyHeader::set_common(&mut headers, res_etag);
            headers.set::<HeaderBloomStatus>(HeaderBloomStatus(HeaderBloomStatusValue::Hit));

            // Serve non-modified response
            return Self::respond(method, StatusCode::NotModified, headers, String::from(""));
        }

        // Response modified
        let mut headers = [httparse::EMPTY_HEADER; CACHED_PARSE_MAX_HEADERS];
        let mut res = httparse::Response::new(&mut headers);

        // Split headers from body
        let mut res_body_string = String::new();
        let mut is_last_line_empty = false;

        for res_line in res_string.lines() {
            if res_body_string.is_empty() == false || is_last_line_empty == true {
                // Write to body
                res_body_string.push_str(res_line.as_ref());
                res_body_string.push_str(LINE_FEED);
            }

            is_last_line_empty = res_line.is_empty();
        }

        match res.parse(res_string.as_bytes()) {
            Ok(_) => {
                // Process cached status
                let code = res.code.unwrap_or(500u16);
                let status = StatusCode::try_from(code).unwrap_or(StatusCode::Unregistered(code));

                // Process cached headers
                let mut headers = Headers::new();

                for header in res.headers {
                    if let (Ok(header_name), Ok(header_value)) =
                        (
                            String::from_utf8(Vec::from(header.name)),
                            String::from_utf8(Vec::from(header.value)),
                        )
                    {
                        headers.set_raw(header_name, header_value);
                    }
                }

                ProxyHeader::set_common(&mut headers, res_etag);
                headers.set::<HeaderBloomStatus>(HeaderBloomStatus(HeaderBloomStatusValue::Hit));

                // Serve cached response
                Self::respond(method, status, headers, res_body_string)
            }
            Err(err) => {
                error!("failed parsing cached response: {}", err);

                Self::dispatch_failure(method)
            }
        }
    }

    fn dispatch_fetched(
        method: &Method,
        status: &StatusCode,
        mut headers: Headers,
        bloom_status: HeaderBloomStatusValue,
        body_string: String,
        result_string: Option<String>,
    ) -> ProxyServeFuture {
        // Process ETag for content?
        if let Some(result_string_value) = result_string {
            let (_, res_etag) = Self::body_fingerprint(&result_string_value);

            ProxyHeader::set_common(&mut headers, res_etag);
        }

        headers.set(HeaderBloomStatus(bloom_status));

        Self::respond(method, *status, headers, body_string)
    }

    fn dispatch_failure(method: &Method) -> ProxyServeFuture {
        let status = StatusCode::BadGateway;

        let mut headers = Headers::new();

        headers.set::<HeaderBloomStatus>(HeaderBloomStatus(HeaderBloomStatusValue::Offline));

        Self::respond(method, status, headers, format!("{}", status))
    }

    fn body_fingerprint(body_string: &str) -> (String, ETag) {
        let body_hash = format!("{:x}", farmhash::fingerprint64(body_string.as_bytes()));
        let body_etag = ETag(EntityTag::new(false, body_hash.to_owned()));

        (body_hash, body_etag)
    }

    fn respond(
        method: &Method,
        status: StatusCode,
        headers: Headers,
        body_string: String,
    ) -> ProxyServeFuture {
        Box::new(future::ok(match method {
            &Method::Get | &Method::Post | &Method::Patch | &Method::Put | &Method::Delete => {
                Response::new()
                    .with_status(status)
                    .with_headers(headers)
                    .with_body(body_string)
            }
            _ => Response::new().with_status(status).with_headers(headers),
        }))
    }
}
<|endoftext|>


<commit_before>use std::io::Cursor;
use byteorder::{WriteBytesExt, ReadBytesExt, BigEndian};

//mod error;
use error::QuicError;
use error::Result;
use std::io::Read;

#[derive(Debug, PartialEq)]
pub struct StreamFrame {
    pub f: bool,
    pub data_length_present: bool,
    pub data_length: Option<u16>,
    pub stream_id: u32,
    pub offset: u64,
    pub stream_data: Vec<u8>,
}

impl StreamFrame {
    pub fn from_bytes(buf: &Vec<u8>) -> Result<StreamFrame> {
        let mut reader = Cursor::new(buf);
        let first_octet = reader.read_u8()?;

        let f = first_octet & 0x20 > 0;
        let data_length_present = first_octet & 0x10 > 0;

        let oo = (first_octet >> 2) & 0x03;
        let ss = first_octet & 0x03;

        let data_length = match data_length_present {
            true => Some(reader.read_u16::<BigEndian>()?),
            false => None,
        };

        let stream_id = reader.read_uint::<BigEndian>((ss + 1) as usize)? as u32;

        let offset = match oo {
            0 => 0,
            1 => reader.read_u16::<BigEndian>()? as u64,
            2 => reader.read_u32::<BigEndian>()? as u64,
            3 => reader.read_u64::<BigEndian>()?,
            _ => return Err(QuicError::ParseError)
        };

        let mut stream_data = Vec::new();

        match data_length {
            None => {
                reader.read_to_end(&mut stream_data)?;
            },
            Some(length) => {
                let mut reader_handle = reader.take(length as u64);
                reader_handle.read_to_end(&mut stream_data)?;
            }
        }

        Ok(StreamFrame {
            f: f,
            data_length_present: data_length_present,
            data_length: data_length,
            stream_id: stream_id,
            offset: offset,
            stream_data: stream_data,
        })
    }

    pub fn as_bytes(&self) -> Result<Vec<u8>> {
        let mut byte_vector = Vec::new();

        let mut type_byte = 0xc0;

        if self.f {
            type_byte |= 0x20;
        }

        // Signify that we are putting the data length in.
        type_byte |= 0x10;

        if self.offset != 0 {
            if self.offset <= u16::max_value() as u64 {
                type_byte |= 0x04;
            } else if self.offset <= u32::max_value() as u64 {
                type_byte |= 0x08;
            } else if self.offset <= u64::max_value() {
                type_byte |= 0x0c;
            }
        }

        // Skipping 3 byte stream_id's for now.
        if self.stream_id <= (u8::max_value() as u32) {
            type_byte = type_byte | 0x00;
            byte_vector.write_u8(type_byte);
            byte_vector.write_u16::<BigEndian>(self.data_length.unwrap());
            byte_vector.write_u8(self.stream_id as u8);
        } else if self.stream_id <= (u16::max_value() as u32) {
            type_byte = type_byte | 0x01;
            byte_vector.write_u8(type_byte);
            byte_vector.write_u16::<BigEndian>(self.data_length.unwrap());
            byte_vector.write_u16::<BigEndian>(self.stream_id as u16);
        } else {
            type_byte = type_byte | 0x03;
            byte_vector.write_u8(type_byte);
            byte_vector.write_u16::<BigEndian>(self.data_length.unwrap());
            byte_vector.write_u32::<BigEndian>(self.stream_id as u32);
        }

        if self.offset != 0 {
            if self.offset <= u16::max_value() as u64 {
                byte_vector.write_u16::<BigEndian>(self.offset as u16);
            } else if self.offset <= u32::max_value() as u64 {
                byte_vector.write_u32::<BigEndian>(self.offset as u32);
            } else if self.offset <= u64::max_value() {
                byte_vector.write_u64::<BigEndian>(self.offset);
            }
        }

        byte_vector.extend(&self.stream_data);

        Ok(byte_vector)
    }
}

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

    #[test]
    fn serialize_stream_frame() {
        let frame = StreamFrame {
            f: false,
            data_length_present: true,
            data_length: Some(50),
            stream_id: 259,
            offset: 340,
            stream_data: vec![10u8; 50],
        };

        let bytes = frame.as_bytes().unwrap();
        let parsed_frame = StreamFrame::from_bytes(&bytes).unwrap();

        assert_eq!(parsed_frame, frame);
    }
}<commit_msg>add another test, mainly to test travis-ci<commit_after>use std::io::Cursor;
use byteorder::{WriteBytesExt, ReadBytesExt, BigEndian};

//mod error;
use error::QuicError;
use error::Result;
use std::io::Read;

#[derive(Debug, PartialEq)]
pub struct StreamFrame {
    pub f: bool,
    pub data_length_present: bool,
    pub data_length: Option<u16>,
    pub stream_id: u32,
    pub offset: u64,
    pub stream_data: Vec<u8>,
}

impl StreamFrame {
    pub fn from_bytes(buf: &Vec<u8>) -> Result<StreamFrame> {
        let mut reader = Cursor::new(buf);
        let first_octet = reader.read_u8()?;

        let f = first_octet & 0x20 > 0;
        let data_length_present = first_octet & 0x10 > 0;

        let oo = (first_octet >> 2) & 0x03;
        let ss = first_octet & 0x03;

        let data_length = match data_length_present {
            true => Some(reader.read_u16::<BigEndian>()?),
            false => None,
        };

        let stream_id = reader.read_uint::<BigEndian>((ss + 1) as usize)? as u32;

        let offset = match oo {
            0 => 0,
            1 => reader.read_u16::<BigEndian>()? as u64,
            2 => reader.read_u32::<BigEndian>()? as u64,
            3 => reader.read_u64::<BigEndian>()?,
            _ => return Err(QuicError::ParseError)
        };

        let mut stream_data = Vec::new();

        match data_length {
            None => {
                reader.read_to_end(&mut stream_data)?;
            },
            Some(length) => {
                let mut reader_handle = reader.take(length as u64);
                reader_handle.read_to_end(&mut stream_data)?;
            }
        }

        Ok(StreamFrame {
            f: f,
            data_length_present: data_length_present,
            data_length: data_length,
            stream_id: stream_id,
            offset: offset,
            stream_data: stream_data,
        })
    }

    pub fn as_bytes(&self) -> Result<Vec<u8>> {
        let mut byte_vector = Vec::new();

        let mut type_byte = 0xc0;

        if self.f {
            type_byte |= 0x20;
        }

        // Signify that we are putting the data length in.
        type_byte |= 0x10;

        if self.offset != 0 {
            if self.offset <= u16::max_value() as u64 {
                type_byte |= 0x04;
            } else if self.offset <= u32::max_value() as u64 {
                type_byte |= 0x08;
            } else if self.offset <= u64::max_value() {
                type_byte |= 0x0c;
            }
        }

        // Skipping 3 byte stream_id's for now.
        if self.stream_id <= (u8::max_value() as u32) {
            type_byte = type_byte | 0x00;
            byte_vector.write_u8(type_byte);
            byte_vector.write_u16::<BigEndian>(self.data_length.unwrap());
            byte_vector.write_u8(self.stream_id as u8);
        } else if self.stream_id <= (u16::max_value() as u32) {
            type_byte = type_byte | 0x01;
            byte_vector.write_u8(type_byte);
            byte_vector.write_u16::<BigEndian>(self.data_length.unwrap());
            byte_vector.write_u16::<BigEndian>(self.stream_id as u16);
        } else {
            type_byte = type_byte | 0x03;
            byte_vector.write_u8(type_byte);
            byte_vector.write_u16::<BigEndian>(self.data_length.unwrap());
            byte_vector.write_u32::<BigEndian>(self.stream_id as u32);
        }

        if self.offset != 0 {
            if self.offset <= u16::max_value() as u64 {
                byte_vector.write_u16::<BigEndian>(self.offset as u16);
            } else if self.offset <= u32::max_value() as u64 {
                byte_vector.write_u32::<BigEndian>(self.offset as u32);
            } else if self.offset <= u64::max_value() {
                byte_vector.write_u64::<BigEndian>(self.offset);
            }
        }

        byte_vector.extend(&self.stream_data);

        Ok(byte_vector)
    }
}

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

    #[test]
    fn serialize_stream_frame_1() {
        let frame = StreamFrame {
            f: false,
            data_length_present: true,
            data_length: Some(50),
            stream_id: 259,
            offset: 340,
            stream_data: vec![10u8; 50],
        };

        let bytes = frame.as_bytes().unwrap();
        let parsed_frame = StreamFrame::from_bytes(&bytes).unwrap();

        assert_eq!(parsed_frame, frame);
    }

    fn serialize_stream_frame_2() {
        let frame = StreamFrame {
            f: false,
            data_length_present: true,
            data_length: Some(1100),
            stream_id: 12590,
            offset: 780123,
            stream_data: vec![10u8; 1100],
        };

        let bytes = frame.as_bytes().unwrap();
        let parsed_frame = StreamFrame::from_bytes(&bytes).unwrap();

        assert_eq!(parsed_frame, frame);
    }
}<|endoftext|>


<commit_before>#[cfg(all(feature = "rustc_ser_type", not(feature = "serde_type")))]
use serialize::json::{Json, ToJson};
#[cfg(feature = "serde_type")]
use serde_json::value::{self, Value as Json};

use helpers::HelperDef;
use registry::Registry;
use context::Context;
use render::{Renderable, RenderContext, RenderError, Helper};

#[derive(Clone, Copy)]
pub struct EachHelper;

impl HelperDef for EachHelper {
    #[cfg(all(feature = "rustc_ser_type", not(feature = "serde_type")))]
    fn call(&self,
            c: &Context,
            h: &Helper,
            r: &Registry,
            rc: &mut RenderContext)
            -> Result<(), RenderError> {
        let value = try!(h.param(0)
                          .ok_or_else(|| RenderError::new("Param not found for helper \"each\"")));

        let template = h.template();

        match template {
            Some(t) => {
                rc.promote_local_vars();
                if let Some(path_root) = value.path_root() {
                    let local_path_root = format!("{}/{}", rc.get_path(), path_root);
                    rc.set_local_path_root(local_path_root);
                }

                debug!("each value {:?}", value.value());
                let rendered = match value.value() {
                    &Json::Array(ref list) => {
                        let len = list.len();
                        for i in 0..len {
                            let mut local_rc = rc.derive();
                            local_rc.set_local_var("@first".to_string(), (i == 0usize).to_json());
                            local_rc.set_local_var("@last".to_string(), (i == len - 1).to_json());
                            local_rc.set_local_var("@index".to_string(), i.to_json());

                            if let Some(inner_path) = value.path() {
                                let new_path = format!("{}/{}.[{}]",
                                                       local_rc.get_path(),
                                                       inner_path,
                                                       i);
                                debug!("each path {:?}", new_path);
                                local_rc.set_path(new_path);
                            }
                            try!(t.render(c, r, &mut local_rc));
                        }
                        Ok(())
                    }
                    &Json::Object(ref obj) => {
                        let mut first: bool = true;
                        for k in obj.keys() {
                            let mut local_rc = rc.derive();
                            local_rc.set_local_var("@first".to_string(), first.to_json());
                            if first {
                                first = false;
                            }

                            local_rc.set_local_var("@key".to_string(), k.to_json());

                            if let Some(inner_path) = value.path() {
                                let new_path = format!("{}/{}.[{}]",
                                                       local_rc.get_path(),
                                                       inner_path,
                                                       k);
                                local_rc.set_path(new_path);
                            }

                            try!(t.render(c, r, &mut local_rc));
                        }

                        Ok(())
                    }
                    _ => {
                        Err(RenderError::new(format!("Param type is not iterable: {:?}", template)))
                    }
                };

                rc.demote_local_vars();
                rendered
            }
            None => Ok(()),
        }
    }

    #[cfg(feature = "serde_type")]
    fn call(&self,
            c: &Context,
            h: &Helper,
            r: &Registry,
            rc: &mut RenderContext)
            -> Result<(), RenderError> {
        let value = try!(h.param(0)
                          .ok_or_else(|| RenderError::new("Param not found for helper \"each\"")));

        let template = h.template();

        match template {
            Some(t) => {
                rc.promote_local_vars();
                if let Some(path_root) = value.path_root() {
                    let local_path_root = format!("{}/{}", rc.get_path(), path_root);
                    rc.set_local_path_root(local_path_root);
                }

                debug!("each value {:?}", value.value());
                let rendered = match value.value() {
                    &Json::Array(ref list) => {
                        let len = list.len();
                        for i in 0..len {
                            let mut local_rc = rc.derive();
                            local_rc.set_local_var("@first".to_string(),
                                                   value::to_value(&(i == 0usize)));
                            local_rc.set_local_var("@last".to_string(),
                                                   value::to_value(&(i == len - 1)));
                            local_rc.set_local_var("@index".to_string(), value::to_value(&i));

                            if let Some(inner_path) = value.path() {
                                let new_path = format!("{}/{}.[{}]",
                                                       local_rc.get_path(),
                                                       inner_path,
                                                       i);
                                debug!("each value {:?}", new_path);
                                local_rc.set_path(new_path);
                            }
                            try!(t.render(c, r, &mut local_rc));
                        }
                        Ok(())
                    }
                    &Json::Object(ref obj) => {
                        let mut first: bool = true;
                        for k in obj.keys() {
                            let mut local_rc = rc.derive();
                            local_rc.set_local_var("@first".to_string(), value::to_value(&first));
                            if first {
                                first = false;
                            }

                            local_rc.set_local_var("@key".to_string(), value::to_value(&k));
                            if let Some(inner_path) = value.path() {
                                let new_path = format!("{}/{}.[{}]",
                                                       local_rc.get_path(),
                                                       inner_path,
                                                       k);
                                debug!("each value {:?}", new_path);
                                local_rc.set_path(new_path);
                            }

                            try!(t.render(c, r, &mut local_rc));
                        }

                        Ok(())
                    }
                    _ => {
                        Err(RenderError::new(format!("Param type is not iterable: {:?}", template)))
                    }
                };
                // rc.set_path(path);
                rc.demote_local_vars();
                rendered
            }
            None => Ok(()),
        }
    }
}

pub static EACH_HELPER: EachHelper = EachHelper;

#[cfg(test)]
mod test {
    use template::Template;
    use registry::Registry;

    use std::collections::BTreeMap;

    #[cfg(all(feature = "rustc_ser_type", not(feature = "serde_type")))]
    use serialize::json::Json;

    #[test]
    fn test_each() {
        let t0 = Template::compile("{{#each this}}{{@first}}|{{@last}}|{{@index}}:\
                                    {{this}}|{{/each}}"
                                       .to_string())
                     .ok()
                     .unwrap();
        let t1 = Template::compile("{{#each this}}{{@first}}|{{@key}}:{{this}}|{{/each}}"
                                       .to_string())
                     .ok()
                     .unwrap();

        let mut handlebars = Registry::new();
        handlebars.register_template("t0", t0);
        handlebars.register_template("t1", t1);

        let r0 = handlebars.render("t0", &vec![1u16, 2u16, 3u16]);
        assert_eq!(r0.ok().unwrap(),
                   "true|false|0:1|false|false|1:2|false|true|2:3|".to_string());

        let mut m: BTreeMap<String, u16> = BTreeMap::new();
        m.insert("ftp".to_string(), 21);
        m.insert("http".to_string(), 80);
        let r1 = handlebars.render("t1", &m);
        assert_eq!(r1.ok().unwrap(), "true|ftp:21|false|http:80|".to_string());
    }

    #[test]
    #[cfg(all(feature = "rustc_ser_type", not(feature = "serde_type")))]
    fn test_each_with_parent() {

        let json_str = r#"{"a":{"a":99,"c":[{"d":100},{"d":200}]}}"#;

        let data = Json::from_str(json_str).unwrap();
        // println!("data: {}", data);

        // previously, to access the parent in an each block,
        // a user would need to specify ../../b, as the path
        // that is computed includes the array index: ./a.c.[0]
        let t0 = Template::compile("{{#each a.c}} d={{d}} b={{../a.a}} {{/each}}".to_string())
                     .ok()
                     .unwrap();

        let mut handlebars = Registry::new();
        handlebars.register_template("t0", t0);

        let r1 = handlebars.render("t0", &data);
        assert_eq!(r1.ok().unwrap(), " d=100 b=99  d=200 b=99 ".to_string());
    }

    #[test]
    #[cfg(all(feature = "rustc_ser_type", not(feature = "serde_type")))]
    fn test_nested_each_with_parent() {

        let json_str = r#"{"a": [{"b": [{"d": 100}], "c": 200}]}"#;

        let data = Json::from_str(json_str).unwrap();
        let t0 = Template::compile("{{#each a}}{{#each b}}{{d}}:{{../c}}{{/each}}{{/each}}"
                                       .to_string())
                     .ok()
                     .unwrap();

        let mut handlebars = Registry::new();
        handlebars.register_template("t0", t0);

        let r1 = handlebars.render("t0", &data);
        assert_eq!(r1.ok().unwrap(), "100:200".to_string());
    }

    #[test]
    #[cfg(all(feature = "rustc_ser_type", not(feature = "serde_type")))]
    fn test_nested_each() {

        let json_str = r#"{"a": [{"b": true}], "b": [[1, 2, 3],[4, 5]]}"#;

        let data = Json::from_str(json_str).unwrap();
        let t0 = Template::compile("{{#each b}}{{#if ../a}}{{#each this}}{{this}}{{/each}}{{/if}}{{/each}}".to_string())
            .ok()
            .unwrap();

        let mut handlebars = Registry::new();
        handlebars.register_template("t0", t0);

        let r1 = handlebars.render("t0", &data);
        assert_eq!(r1.ok().unwrap(), "12345".to_string());
    }

    #[test]
    fn test_nested_array() {
        let t0 = Template::compile("{{#each this.[0]}}{{this}}{{/each}}".to_owned()).ok().unwrap();

        let mut handlebars = Registry::new();
        handlebars.register_template("t0", t0);

        let r0 = handlebars.render("t0", &(vec![vec![1, 2, 3]]));

        assert_eq!(r0.ok().unwrap(), "123".to_string());
    }

    #[test]
    fn test_empty_key() {
        let t0 = Template::compile("{{#each this}}{{@key}}-{{value}}\n{{/each}}".to_owned())
                     .unwrap();

        let mut handlebars = Registry::new();
        handlebars.register_template("t0", t0);

        let r0 = handlebars.render("t0",
                                   &({
                                       let mut rv = BTreeMap::new();
                                       rv.insert("foo".to_owned(), {
                                           let mut rv = BTreeMap::new();
                                           rv.insert("value".to_owned(), "bar".to_owned());
                                           rv
                                       });
                                       rv.insert("".to_owned(), {
                                           let mut rv = BTreeMap::new();
                                           rv.insert("value".to_owned(), "baz".to_owned());
                                           rv
                                       });
                                       rv
                                   }))
                           .unwrap();

        let mut r0_sp: Vec<_> = r0.split('\n').collect();
        r0_sp.sort();

        assert_eq!(r0_sp, vec!["", "-baz", "foo-bar"]);
    }

}
<commit_msg>(feat) added support for else in each block [#99]<commit_after>#[cfg(all(feature = "rustc_ser_type", not(feature = "serde_type")))]
use serialize::json::{Json, ToJson};
#[cfg(feature = "serde_type")]
use serde_json::value::{self, Value as Json};

use helpers::HelperDef;
use registry::Registry;
use context::{Context, JsonTruthy};
use render::{Renderable, RenderContext, RenderError, Helper};

#[derive(Clone, Copy)]
pub struct EachHelper;

impl HelperDef for EachHelper {
    #[cfg(all(feature = "rustc_ser_type", not(feature = "serde_type")))]
    fn call(&self,
            c: &Context,
            h: &Helper,
            r: &Registry,
            rc: &mut RenderContext)
            -> Result<(), RenderError> {
        let value = try!(h.param(0)
                          .ok_or_else(|| RenderError::new("Param not found for helper \"each\"")));

        let template = h.template();

        match template {
            Some(t) => {
                rc.promote_local_vars();
                if let Some(path_root) = value.path_root() {
                    let local_path_root = format!("{}/{}", rc.get_path(), path_root);
                    rc.set_local_path_root(local_path_root);
                }

                debug!("each value {:?}", value.value());
                let rendered = match (value.value().is_truthy(), value.value()) {
                    (true, &Json::Array(ref list)) => {
                        let len = list.len();
                        for i in 0..len {
                            let mut local_rc = rc.derive();
                            local_rc.set_local_var("@first".to_string(), (i == 0usize).to_json());
                            local_rc.set_local_var("@last".to_string(), (i == len - 1).to_json());
                            local_rc.set_local_var("@index".to_string(), i.to_json());

                            if let Some(inner_path) = value.path() {
                                let new_path = format!("{}/{}.[{}]",
                                                       local_rc.get_path(),
                                                       inner_path,
                                                       i);
                                debug!("each path {:?}", new_path);
                                local_rc.set_path(new_path);
                            }
                            try!(t.render(c, r, &mut local_rc));
                        }
                        Ok(())
                    }
                    (true, &Json::Object(ref obj)) => {
                        let mut first: bool = true;
                        for k in obj.keys() {
                            let mut local_rc = rc.derive();
                            local_rc.set_local_var("@first".to_string(), first.to_json());
                            if first {
                                first = false;
                            }

                            local_rc.set_local_var("@key".to_string(), k.to_json());

                            if let Some(inner_path) = value.path() {
                                let new_path = format!("{}/{}.[{}]",
                                                       local_rc.get_path(),
                                                       inner_path,
                                                       k);
                                local_rc.set_path(new_path);
                            }

                            try!(t.render(c, r, &mut local_rc));
                        }

                        Ok(())
                    }
                    (false, _) => {
                        if let Some(else_template) = h.inverse() {
                            try!(else_template.render(c, r, rc));
                        }
                        Ok(())
                    }
                    _ => {
                        Err(RenderError::new(format!("Param type is not iterable: {:?}", template)))
                    }
                };

                rc.demote_local_vars();
                rendered
            }
            None => Ok(()),
        }
    }

    #[cfg(feature = "serde_type")]
    fn call(&self,
            c: &Context,
            h: &Helper,
            r: &Registry,
            rc: &mut RenderContext)
            -> Result<(), RenderError> {
        let value = try!(h.param(0)
                          .ok_or_else(|| RenderError::new("Param not found for helper \"each\"")));

        let template = h.template();

        match template {
            Some(t) => {
                rc.promote_local_vars();
                if let Some(path_root) = value.path_root() {
                    let local_path_root = format!("{}/{}", rc.get_path(), path_root);
                    rc.set_local_path_root(local_path_root);
                }

                debug!("each value {:?}", value.value());
                let rendered = match (value.value().is_truthy(), value.value()) {
                    (true, &Json::Array(ref list)) => {
                        let len = list.len();
                        for i in 0..len {
                            let mut local_rc = rc.derive();
                            local_rc.set_local_var("@first".to_string(),
                                                   value::to_value(&(i == 0usize)));
                            local_rc.set_local_var("@last".to_string(),
                                                   value::to_value(&(i == len - 1)));
                            local_rc.set_local_var("@index".to_string(), value::to_value(&i));

                            if let Some(inner_path) = value.path() {
                                let new_path = format!("{}/{}.[{}]",
                                                       local_rc.get_path(),
                                                       inner_path,
                                                       i);
                                debug!("each value {:?}", new_path);
                                local_rc.set_path(new_path);
                            }
                            try!(t.render(c, r, &mut local_rc));
                        }
                        Ok(())
                    }
                    (true, &Json::Object(ref obj)) => {
                        let mut first: bool = true;
                        for k in obj.keys() {
                            let mut local_rc = rc.derive();
                            local_rc.set_local_var("@first".to_string(), value::to_value(&first));
                            if first {
                                first = false;
                            }

                            local_rc.set_local_var("@key".to_string(), value::to_value(&k));
                            if let Some(inner_path) = value.path() {
                                let new_path = format!("{}/{}.[{}]",
                                                       local_rc.get_path(),
                                                       inner_path,
                                                       k);
                                debug!("each value {:?}", new_path);
                                local_rc.set_path(new_path);
                            }

                            try!(t.render(c, r, &mut local_rc));
                        }

                        Ok(())
                    }
                    (false, _) => {
                        if let Some(else_template) = h.inverse() {
                            try!(else_template.render(c, r, rc));
                        }
                        Ok(())
                    }
                    _ => {
                        Err(RenderError::new(format!("Param type is not iterable: {:?}", template)))
                    }
                };
                // rc.set_path(path);
                rc.demote_local_vars();
                rendered
            }
            None => Ok(()),
        }
    }
}

pub static EACH_HELPER: EachHelper = EachHelper;

#[cfg(test)]
mod test {
    use template::Template;
    use registry::Registry;

    use std::collections::BTreeMap;

    #[cfg(all(feature = "rustc_ser_type", not(feature = "serde_type")))]
    use serialize::json::Json;

    #[test]
    fn test_each() {
        let t0 = Template::compile("{{#each this}}{{@first}}|{{@last}}|{{@index}}:\
                                    {{this}}|{{/each}}"
                                       .to_string())
                     .ok()
                     .unwrap();
        let t1 = Template::compile("{{#each this}}{{@first}}|{{@key}}:{{this}}|{{/each}}"
                                       .to_string())
                     .ok()
                     .unwrap();

        let mut handlebars = Registry::new();
        handlebars.register_template("t0", t0);
        handlebars.register_template("t1", t1);

        let r0 = handlebars.render("t0", &vec![1u16, 2u16, 3u16]);
        assert_eq!(r0.ok().unwrap(),
                   "true|false|0:1|false|false|1:2|false|true|2:3|".to_string());

        let mut m: BTreeMap<String, u16> = BTreeMap::new();
        m.insert("ftp".to_string(), 21);
        m.insert("http".to_string(), 80);
        let r1 = handlebars.render("t1", &m);
        assert_eq!(r1.ok().unwrap(), "true|ftp:21|false|http:80|".to_string());
    }

    #[test]
    #[cfg(all(feature = "rustc_ser_type", not(feature = "serde_type")))]
    fn test_each_with_parent() {

        let json_str = r#"{"a":{"a":99,"c":[{"d":100},{"d":200}]}}"#;

        let data = Json::from_str(json_str).unwrap();
        // println!("data: {}", data);

        // previously, to access the parent in an each block,
        // a user would need to specify ../../b, as the path
        // that is computed includes the array index: ./a.c.[0]
        let t0 = Template::compile("{{#each a.c}} d={{d}} b={{../a.a}} {{/each}}".to_string())
                     .ok()
                     .unwrap();

        let mut handlebars = Registry::new();
        handlebars.register_template("t0", t0);

        let r1 = handlebars.render("t0", &data);
        assert_eq!(r1.ok().unwrap(), " d=100 b=99  d=200 b=99 ".to_string());
    }

    #[test]
    #[cfg(all(feature = "rustc_ser_type", not(feature = "serde_type")))]
    fn test_nested_each_with_parent() {

        let json_str = r#"{"a": [{"b": [{"d": 100}], "c": 200}]}"#;

        let data = Json::from_str(json_str).unwrap();
        let t0 = Template::compile("{{#each a}}{{#each b}}{{d}}:{{../c}}{{/each}}{{/each}}"
                                       .to_string())
                     .ok()
                     .unwrap();

        let mut handlebars = Registry::new();
        handlebars.register_template("t0", t0);

        let r1 = handlebars.render("t0", &data);
        assert_eq!(r1.ok().unwrap(), "100:200".to_string());
    }

    #[test]
    #[cfg(all(feature = "rustc_ser_type", not(feature = "serde_type")))]
    fn test_nested_each() {

        let json_str = r#"{"a": [{"b": true}], "b": [[1, 2, 3],[4, 5]]}"#;

        let data = Json::from_str(json_str).unwrap();
        let t0 = Template::compile("{{#each b}}{{#if ../a}}{{#each this}}{{this}}{{/each}}{{/if}}{{/each}}".to_string())
            .ok()
            .unwrap();

        let mut handlebars = Registry::new();
        handlebars.register_template("t0", t0);

        let r1 = handlebars.render("t0", &data);
        assert_eq!(r1.ok().unwrap(), "12345".to_string());
    }

    #[test]
    fn test_nested_array() {
        let t0 = Template::compile("{{#each this.[0]}}{{this}}{{/each}}".to_owned()).ok().unwrap();

        let mut handlebars = Registry::new();
        handlebars.register_template("t0", t0);

        let r0 = handlebars.render("t0", &(vec![vec![1, 2, 3]]));

        assert_eq!(r0.ok().unwrap(), "123".to_string());
    }

    #[test]
    fn test_empty_key() {
        let t0 = Template::compile("{{#each this}}{{@key}}-{{value}}\n{{/each}}".to_owned())
                     .unwrap();

        let mut handlebars = Registry::new();
        handlebars.register_template("t0", t0);

        let r0 = handlebars.render("t0",
                                   &({
                                       let mut rv = BTreeMap::new();
                                       rv.insert("foo".to_owned(), {
                                           let mut rv = BTreeMap::new();
                                           rv.insert("value".to_owned(), "bar".to_owned());
                                           rv
                                       });
                                       rv.insert("".to_owned(), {
                                           let mut rv = BTreeMap::new();
                                           rv.insert("value".to_owned(), "baz".to_owned());
                                           rv
                                       });
                                       rv
                                   }))
                           .unwrap();

        let mut r0_sp: Vec<_> = r0.split('\n').collect();
        r0_sp.sort();

        assert_eq!(r0_sp, vec!["", "-baz", "foo-bar"]);
    }

    #[test]
    fn test_each_else() {
        let t0 = Template::compile("{{#each a}}1{{else}}empty{{/each}}".to_owned()).unwrap();
        let mut handlebars = Registry::new();
        handlebars.register_template("t0", t0);
        let m1 = btreemap! {
            "a".to_string() => Vec::<String>::new(),
        };
        let r0 = handlebars.render("t0", &m1).unwrap();
        assert_eq!(r0, "empty");

        let m2 = btreemap!{
            "b".to_string() => Vec::<String>::new()
        };
        let r1 = handlebars.render("t0", &m2).unwrap();
        assert_eq!(r1, "empty");
    }

}
<|endoftext|>


<commit_before>use regex::Regex;
use std::error;
use std::result;

use ::sql::errors::SqlError;

type Result<T> = result::Result<T, Box<error::Error>>;

pub trait SqlSafe {
    fn is_sql_safe(&self) -> Result<&str>;
}

lazy_static! {
    static ref SQL_CHECK: Regex =
        Regex::new(r"([^\d\w\s@\.-]+)").expect("FAULT  SQL_CHECK");
}

impl SqlSafe for str {
    fn is_sql_safe<'a>(&'a self) -> Result<&'a str> {
        if Regex::is_match(&SQL_CHECK, self) {
            let cap = SQL_CHECK.captures(self).expect("FAULT  SQL_CHECK");
            Err(Box::new(SqlError::UnsafeCharacters(cap[1].to_string())))
        } else {
            Ok(self)
        }
    }
}
<commit_msg>Fixes trait SqlSafe to be type independent<commit_after>use regex::Regex;
use std::error;
use std::result;

use ::sql::errors::SqlError;

type Result<T> = result::Result<T, Box<error::Error>>;

pub trait SqlSafe {
    fn is_sql_safe(&self) -> Result<&Self>;
}

lazy_static! {
    static ref SQL_CHECK: Regex =
        Regex::new(r"([^\d\w\s@\.-]+)").expect("FAULT  SQL_CHECK");
}

impl<'a> SqlSafe for &'a str {
    fn is_sql_safe<'b>(&self) -> Result<&Self> {
        if Regex::is_match(&SQL_CHECK, self) {
            let cap = SQL_CHECK.captures(self).expect("FAULT  SQL_CHECK");
            Err(Box::new(SqlError::UnsafeCharacters(cap[1].to_string())))
        } else {
            Ok(self)
        }
    }
}
<|endoftext|>


<commit_before>// This file is part of Twig (ported to Rust).
//
// For the copyright and license information, please view the LICENSE
// file that was distributed with this source code.

/// Twig generic error

use std::fmt::{self, Display};

#[macro_use]
pub mod macros;
// use std Error-trait to improve cross-crate compatibility
// don't mix it up with Err(X)
pub use std::error;
pub mod api;

// generic wrapper around some ErrorCode - adds location support
#[derive(Debug)]
pub struct Error<T>
    where T: api::ErrorCode
{
    // the exception codes are going to be enums
    // - i.e. Exception<MY_ENUM> implements std::error::Error without any boilerplate
    // to MY_ENUM. Hurray! :-)
    code: T,
    // I decided to call this field `code` instead of `error` to not confuse it with the Error trait
    location: Location,
    // chaining is required by std::error::Error
    cause: Option<Box<error::Error>>,
}

impl<T> Error<T>
    where T: api::ErrorCode
{
    pub fn new(code: T, location: Location) -> Error<T> {
        Error {
            code: code,
            location: location,
            cause: None
        }
    }

    #[allow(dead_code)] // only used by tests
    pub fn code(&self) -> &T {
        &self.code
    }

    pub fn location(&self) -> &Location {
        &self.location
    }

    pub fn caused_by<X: 'static + error::Error>(mut self, cause: X) -> Self {
        self.cause = Some(Box::new(cause));

        self
    }

    pub fn causes<X>(self, wrapper: Error<X>) -> Error<X> where
        X: api::ErrorCode
    {
        wrapper.caused_by(self)
    }

    // iterate along the error-chain.
    pub fn iter(&self) -> ErrorIter {
        ErrorIter {
            next: Some(self),
        }
    }
}

impl<T> error::Error for Error<T>
    where T: api::ErrorCode
{
    fn description(&self) -> &str {
        // delegate the error description to the ErrorCode
        &self.code.description()
    }

    fn cause<'a>(&'a self) -> Option<&'a error::Error> {
        // dereference from Option<Box<T>> to Option<&T>
        self.cause.as_ref().map(|x| &**x)
    }
}

pub struct ErrorIter<'a> {
    next: Option<&'a error::Error>
}

impl<'a> Iterator for ErrorIter<'a> {
    type Item = &'a error::Error;

    fn next(&mut self) -> Option<Self::Item> {
        return match self.next {
            Some(err) => {
                self.next = err.cause();
                Some(err)
            }
            None => None,
        }
    }
}

impl<T> Display for Error<T>
    where T: api::ErrorCode
{
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        try!(write!(f, "{error_code} at {location}\n",
            error_code = self.code,
            location = self.location));

        match self.cause {
            None => Ok(()),
            Some(ref cause) => write!(f, " - caused by: {}", cause),
        }
    }
}

#[derive(Debug)]
pub struct Location {
    // this might be a bit redundant - but we just store everything we can get.
    // we don't need to be super performant on exceptions - because we try to avoid them :-)
    //
    // note that the module_path is currently only displayed in Debug output due to this redundancy
    pub module_path : &'static str, // e.g. twig::lexer::job::state::shared_traits
    pub filename : &'static str,    // e.g. /src/lexer/job/state/shared_traits.rs
    pub line : u32,
    pub column : u32,
}

impl Display for Location {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(f, "{filename}:{line}:{column}",
            filename = self.filename,
            line     = self.line,
            column   = self.column)
    }
}
<commit_msg>fix: do not re-export std-modules<commit_after>// This file is part of Twig (ported to Rust).
//
// For the copyright and license information, please view the LICENSE
// file that was distributed with this source code.

/// Twig generic error

use std::fmt::{self, Display};
use std::error;

#[macro_use]
pub mod macros;
// use std Error-trait to improve cross-crate compatibility
// don't mix it up with Err(X)
pub mod api;

// generic wrapper around some ErrorCode - adds location support
#[derive(Debug)]
pub struct Error<T>
    where T: api::ErrorCode
{
    // the exception codes are going to be enums
    // - i.e. Exception<MY_ENUM> implements std::error::Error without any boilerplate
    // to MY_ENUM. Hurray! :-)
    code: T,
    // I decided to call this field `code` instead of `error` to not confuse it with the Error trait
    location: Location,
    // chaining is required by std::error::Error
    cause: Option<Box<error::Error>>,
}

impl<T> Error<T>
    where T: api::ErrorCode
{
    pub fn new(code: T, location: Location) -> Error<T> {
        Error {
            code: code,
            location: location,
            cause: None
        }
    }

    #[allow(dead_code)] // only used by tests
    pub fn code(&self) -> &T {
        &self.code
    }

    pub fn location(&self) -> &Location {
        &self.location
    }

    pub fn caused_by<X: 'static + error::Error>(mut self, cause: X) -> Self {
        self.cause = Some(Box::new(cause));

        self
    }

    pub fn causes<X>(self, wrapper: Error<X>) -> Error<X> where
        X: api::ErrorCode
    {
        wrapper.caused_by(self)
    }

    // iterate along the error-chain.
    pub fn iter(&self) -> ErrorIter {
        ErrorIter {
            next: Some(self),
        }
    }
}

impl<T> error::Error for Error<T>
    where T: api::ErrorCode
{
    fn description(&self) -> &str {
        // delegate the error description to the ErrorCode
        &self.code.description()
    }

    fn cause<'a>(&'a self) -> Option<&'a error::Error> {
        // dereference from Option<Box<T>> to Option<&T>
        self.cause.as_ref().map(|x| &**x)
    }
}

pub struct ErrorIter<'a> {
    next: Option<&'a error::Error>
}

impl<'a> Iterator for ErrorIter<'a> {
    type Item = &'a error::Error;

    fn next(&mut self) -> Option<Self::Item> {
        return match self.next {
            Some(err) => {
                self.next = err.cause();
                Some(err)
            }
            None => None,
        }
    }
}

impl<T> Display for Error<T>
    where T: api::ErrorCode
{
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        try!(write!(f, "{error_code} at {location}\n",
            error_code = self.code,
            location = self.location));

        match self.cause {
            None => Ok(()),
            Some(ref cause) => write!(f, " - caused by: {}", cause),
        }
    }
}

#[derive(Debug)]
pub struct Location {
    // this might be a bit redundant - but we just store everything we can get.
    // we don't need to be super performant on exceptions - because we try to avoid them :-)
    //
    // note that the module_path is currently only displayed in Debug output due to this redundancy
    pub module_path : &'static str, // e.g. twig::lexer::job::state::shared_traits
    pub filename : &'static str,    // e.g. /src/lexer/job/state/shared_traits.rs
    pub line : u32,
    pub column : u32,
}

impl Display for Location {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(f, "{filename}:{line}:{column}",
            filename = self.filename,
            line     = self.line,
            column   = self.column)
    }
}
<|endoftext|>


<commit_before>use super::*;

fn default_parse_cli_args(mut args: Vec<&str>) -> CliArgs {
    let global_config = GlobalConfig::new();
    args.insert(0, "makers");
    parse_args(&global_config, "makers", false, Some(args))
}

fn default_parsed_cli_args() -> CliArgs {
    let mut cli_args = CliArgs::new();

    cli_args.command = "makers".to_string();
    cli_args.task = "default".to_string();
    cli_args.profile = Some("development".to_string());

    cli_args
}

fn assert_cli_args(cli_args_ref1: &CliArgs, cli_args_ref2: &CliArgs) {
    let cli_args1 = cli_args_ref1.clone();
    let cli_args2 = cli_args_ref2.clone();

    assert_eq!(cli_args1.command, cli_args2.command);
    assert_eq!(cli_args1.build_file, cli_args2.build_file);
    assert_eq!(cli_args1.task, cli_args2.task);
    assert_eq!(cli_args1.profile.unwrap(), cli_args2.profile.unwrap());
    assert_eq!(cli_args1.log_level, cli_args2.log_level);
    assert_eq!(cli_args1.disable_color, cli_args2.disable_color);
    assert_eq!(cli_args1.cwd, cli_args2.cwd);
    assert_eq!(cli_args1.env, cli_args2.env);
    assert_eq!(cli_args1.env_file, cli_args2.env_file);
    assert_eq!(cli_args1.disable_workspace, cli_args2.disable_workspace);
    assert_eq!(cli_args1.disable_on_error, cli_args2.disable_on_error);
    assert_eq!(cli_args1.allow_private, cli_args2.allow_private);
    assert_eq!(cli_args1.skip_init_end_tasks, cli_args2.skip_init_end_tasks);
    assert_eq!(cli_args1.skip_tasks_pattern, cli_args2.skip_tasks_pattern);
    assert_eq!(
        cli_args1.disable_check_for_updates,
        cli_args2.disable_check_for_updates
    );
    assert_eq!(cli_args1.print_only, cli_args2.print_only);
    assert_eq!(cli_args1.list_all_steps, cli_args2.list_all_steps);
    assert_eq!(cli_args1.diff_execution_plan, cli_args2.diff_execution_plan);
    assert_eq!(cli_args1.experimental, cli_args2.experimental);
    assert_eq!(cli_args1.arguments, cli_args2.arguments);
    assert_eq!(cli_args1.output_format, cli_args2.output_format);
    assert_eq!(cli_args1.output_file, cli_args2.output_file);
    assert_eq!(cli_args1.print_time_summary, cli_args2.print_time_summary);
}

#[test]
fn parse_args_makers() {
    let global_config = GlobalConfig::new();
    let cli_args = parse_args(&global_config, "makers", false, Some(vec!["makers"]));

    let expected = default_parsed_cli_args();

    assert_cli_args(&cli_args, &expected);
}

#[test]
fn parse_args_cargo_make() {
    let global_config = GlobalConfig::new();
    let cli_args = parse_args(&global_config, "make", true, Some(vec!["cargo", "make"]));

    let mut expected = default_parsed_cli_args();
    expected.command = "cargo make".to_string();

    assert_cli_args(&cli_args, &expected);
}

#[test]
fn parse_args_makefile() {
    let mut cli_args = default_parse_cli_args(vec!["--makefile", "./mymakefile.toml"]);

    let mut expected = default_parsed_cli_args();
    expected.build_file = Some("./mymakefile.toml".to_string());

    assert_cli_args(&cli_args, &expected);

    cli_args = default_parse_cli_args(vec!["--makefile", "./mymakefile.toml", "taskname"]);
    expected.task = "taskname".to_string();
    expected.arguments = Some(vec![]);
    assert_cli_args(&cli_args, &expected);
}

#[test]
fn parse_args_task() {
    let mut cli_args = default_parse_cli_args(vec!["--task", "sometask"]);

    let mut expected = default_parsed_cli_args();
    expected.task = "sometask".to_string();

    assert_cli_args(&cli_args, &expected);

    cli_args = default_parse_cli_args(vec!["-t", "sometask"]);
    assert_cli_args(&cli_args, &expected);

    cli_args = default_parse_cli_args(vec!["sometask"]);
    expected.arguments = Some(vec![]);
    assert_cli_args(&cli_args, &expected);
}

#[test]
fn parse_args_profile() {
    let mut cli_args = default_parse_cli_args(vec!["--profile", "someprofile"]);

    let mut expected = default_parsed_cli_args();
    expected.profile = Some("someprofile".to_string());

    assert_cli_args(&cli_args, &expected);

    cli_args = default_parse_cli_args(vec!["-p", "someprofile"]);
    assert_cli_args(&cli_args, &expected);

    cli_args = default_parse_cli_args(vec!["--profile", "someprofile", "taskname"]);
    expected.task = "taskname".to_string();
    expected.arguments = Some(vec![]);
    assert_cli_args(&cli_args, &expected);
}

#[test]
fn parse_args_cwd() {
    let cli_args = default_parse_cli_args(vec!["--cwd", "./mydir/subdir/"]);

    let mut expected = default_parsed_cli_args();
    expected.cwd = Some("./mydir/subdir/".to_string());

    assert_cli_args(&cli_args, &expected);
}

#[test]
fn parse_args_no_workspace() {
    let cli_args = default_parse_cli_args(vec!["--no-workspace"]);

    let mut expected = default_parsed_cli_args();
    expected.disable_workspace = true;

    assert_cli_args(&cli_args, &expected);
}

#[test]
fn parse_args_allow_private() {
    let cli_args = default_parse_cli_args(vec!["--allow-private"]);

    let mut expected = default_parsed_cli_args();
    expected.allow_private = true;

    assert_cli_args(&cli_args, &expected);
}

#[test]
fn parse_args_skip_init_end_tasks() {
    let cli_args = default_parse_cli_args(vec!["--skip-init-end-tasks"]);

    let mut expected = default_parsed_cli_args();
    expected.skip_init_end_tasks = true;

    assert_cli_args(&cli_args, &expected);
}

#[test]
fn parse_args_skip_tasks() {
    let mut cli_args = default_parse_cli_args(vec!["--skip-tasks", "pre-.*"]);

    let mut expected = default_parsed_cli_args();
    expected.skip_tasks_pattern = Some("pre-.*".to_string());

    assert_cli_args(&cli_args, &expected);

    cli_args = default_parse_cli_args(vec!["--skip-tasks", "pre-.*", "taskname"]);
    expected.task = "taskname".to_string();
    expected.arguments = Some(vec![]);
    assert_cli_args(&cli_args, &expected);
}

#[test]
fn parse_args_env_file() {
    let mut cli_args = default_parse_cli_args(vec!["--env-file", "./.env"]);

    let mut expected = default_parsed_cli_args();
    expected.env_file = Some("./.env".to_string());

    assert_cli_args(&cli_args, &expected);

    cli_args = default_parse_cli_args(vec!["--env-file", "./.env", "taskname"]);
    expected.task = "taskname".to_string();
    expected.arguments = Some(vec![]);
    assert_cli_args(&cli_args, &expected);
}

#[test]
fn parse_args_env() {
    let mut cli_args = default_parse_cli_args(vec!["--env", "K=V"]);

    let mut expected = default_parsed_cli_args();
    expected.env = Some(vec!["K=V".to_string()]);

    assert_cli_args(&cli_args, &expected);

    cli_args = default_parse_cli_args(vec!["-e", "K=V"]);
    assert_cli_args(&cli_args, &expected);

    cli_args = default_parse_cli_args(vec!["-e", "K1=V1", "-e", "K2=V2", "--env", "K3=V3"]);
    expected.env = Some(vec![
        "K1=V1".to_string(),
        "K2=V2".to_string(),
        "K3=V3".to_string(),
    ]);
    assert_cli_args(&cli_args, &expected);

    cli_args = default_parse_cli_args(vec![
        "-e", "K1=V1", "-e", "K2=V2", "--env", "K3=V3", "taskname",
    ]);
    expected.task = "taskname".to_string();
    expected.arguments = Some(vec![]);
    assert_cli_args(&cli_args, &expected);
}

#[test]
fn parse_args_loglevel() {
    let mut cli_args = default_parse_cli_args(vec!["--loglevel", "verbose"]);

    let mut expected = default_parsed_cli_args();
    expected.log_level = "verbose".to_string();

    assert_cli_args(&cli_args, &expected);

    cli_args = default_parse_cli_args(vec!["-l", "verbose"]);
    assert_cli_args(&cli_args, &expected);

    cli_args = default_parse_cli_args(vec!["--loglevel", "verbose", "taskname"]);
    expected.task = "taskname".to_string();
    expected.arguments = Some(vec![]);
    assert_cli_args(&cli_args, &expected);
}

#[test]
fn parse_args_verbose() {
    let mut cli_args = default_parse_cli_args(vec!["--verbose"]);

    let mut expected = default_parsed_cli_args();
    expected.log_level = "verbose".to_string();

    assert_cli_args(&cli_args, &expected);

    cli_args = default_parse_cli_args(vec!["-v"]);
    assert_cli_args(&cli_args, &expected);
}

#[test]
fn parse_args_no_color() {
    let cli_args = default_parse_cli_args(vec!["--no-color"]);

    let mut expected = default_parsed_cli_args();
    expected.disable_color = true;

    assert_cli_args(&cli_args, &expected);
}

#[test]
fn parse_args_time_summary() {
    let cli_args = default_parse_cli_args(vec!["--time-summary"]);

    let mut expected = default_parsed_cli_args();
    expected.print_time_summary = true;

    assert_cli_args(&cli_args, &expected);
}

#[test]
fn parse_args_experimental() {
    let cli_args = default_parse_cli_args(vec!["--experimental"]);

    let mut expected = default_parsed_cli_args();
    expected.experimental = true;

    assert_cli_args(&cli_args, &expected);
}

#[test]
fn parse_args_disable_check_for_updates() {
    let cli_args = default_parse_cli_args(vec!["--disable-check-for-updates"]);

    let mut expected = default_parsed_cli_args();
    expected.disable_check_for_updates = true;

    assert_cli_args(&cli_args, &expected);
}

#[test]
fn parse_args_output_format() {
    let mut cli_args = default_parse_cli_args(vec!["--output-format", "autocomplete"]);

    let mut expected = default_parsed_cli_args();
    expected.output_format = "autocomplete".to_string();

    assert_cli_args(&cli_args, &expected);

    cli_args = default_parse_cli_args(vec!["--output-format", "autocomplete", "taskname"]);
    expected.task = "taskname".to_string();
    expected.arguments = Some(vec![]);
    assert_cli_args(&cli_args, &expected);
}

#[test]
fn parse_args_output_file() {
    let mut cli_args = default_parse_cli_args(vec!["--output-file", "./out"]);

    let mut expected = default_parsed_cli_args();
    expected.output_file = Some("./out".to_string());

    assert_cli_args(&cli_args, &expected);

    cli_args = default_parse_cli_args(vec!["--output-file", "./out", "taskname"]);
    expected.task = "taskname".to_string();
    expected.arguments = Some(vec![]);
    assert_cli_args(&cli_args, &expected);
}

#[test]
fn parse_args_print_steps() {
    let cli_args = default_parse_cli_args(vec!["--print-steps"]);

    let mut expected = default_parsed_cli_args();
    expected.print_only = true;

    assert_cli_args(&cli_args, &expected);
}

#[test]
fn parse_args_list_all_steps() {
    let cli_args = default_parse_cli_args(vec!["--list-all-steps"]);

    let mut expected = default_parsed_cli_args();
    expected.list_all_steps = true;

    assert_cli_args(&cli_args, &expected);
}

#[test]
fn parse_args_list_category_steps() {
    let mut cli_args = default_parse_cli_args(vec!["--list-category-steps", "build"]);

    let mut expected = default_parsed_cli_args();
    expected.list_category_steps = Some("build".to_string());

    assert_cli_args(&cli_args, &expected);

    cli_args = default_parse_cli_args(vec!["--list-category-steps", "build", "taskname"]);
    expected.task = "taskname".to_string();
    expected.arguments = Some(vec![]);
    assert_cli_args(&cli_args, &expected);
}

#[test]
fn parse_args_diff_steps() {
    let cli_args = default_parse_cli_args(vec!["--diff-steps"]);

    let mut expected = default_parsed_cli_args();
    expected.diff_execution_plan = true;

    assert_cli_args(&cli_args, &expected);
}

#[test]
fn parse_args_task_cmd() {
    let mut cli_args = default_parse_cli_args(vec!["task1"]);

    let mut expected = default_parsed_cli_args();
    expected.task = "task1".to_string();
    expected.arguments = Some(vec![]);

    assert_cli_args(&cli_args, &expected);

    cli_args = default_parse_cli_args(vec!["--", "task1"]);
    assert_cli_args(&cli_args, &expected);

    cli_args = default_parse_cli_args(vec!["task1", "arg1", "arg2"]);
    expected.arguments = Some(vec!["arg1".to_string(), "arg2".to_string()]);
    assert_cli_args(&cli_args, &expected);

    cli_args = default_parse_cli_args(vec!["--", "task1", "arg1", "arg2"]);
    assert_cli_args(&cli_args, &expected);
}
<commit_msg>fix tests for CI<commit_after>use super::*;

fn default_parse_cli_args(mut args: Vec<&str>) -> CliArgs {
    let global_config = GlobalConfig::new();
    args.insert(0, "makers");
    parse_args(&global_config, "makers", false, Some(args))
}

fn default_parsed_cli_args() -> CliArgs {
    let mut cli_args = CliArgs::new();

    cli_args.command = "makers".to_string();
    cli_args.task = "default".to_string();
    cli_args.profile = Some("development".to_string());

    if ci_info::is_ci() {
        cli_args.time_summary = Some(true);
    }

    cli_args
}

fn assert_cli_args(cli_args_ref1: &CliArgs, cli_args_ref2: &CliArgs) {
    let cli_args1 = cli_args_ref1.clone();
    let cli_args2 = cli_args_ref2.clone();

    assert_eq!(cli_args1.command, cli_args2.command);
    assert_eq!(cli_args1.build_file, cli_args2.build_file);
    assert_eq!(cli_args1.task, cli_args2.task);
    assert_eq!(cli_args1.profile.unwrap(), cli_args2.profile.unwrap());
    assert_eq!(cli_args1.log_level, cli_args2.log_level);
    assert_eq!(cli_args1.disable_color, cli_args2.disable_color);
    assert_eq!(cli_args1.cwd, cli_args2.cwd);
    assert_eq!(cli_args1.env, cli_args2.env);
    assert_eq!(cli_args1.env_file, cli_args2.env_file);
    assert_eq!(cli_args1.disable_workspace, cli_args2.disable_workspace);
    assert_eq!(cli_args1.disable_on_error, cli_args2.disable_on_error);
    assert_eq!(cli_args1.allow_private, cli_args2.allow_private);
    assert_eq!(cli_args1.skip_init_end_tasks, cli_args2.skip_init_end_tasks);
    assert_eq!(cli_args1.skip_tasks_pattern, cli_args2.skip_tasks_pattern);
    assert_eq!(
        cli_args1.disable_check_for_updates,
        cli_args2.disable_check_for_updates
    );
    assert_eq!(cli_args1.print_only, cli_args2.print_only);
    assert_eq!(cli_args1.list_all_steps, cli_args2.list_all_steps);
    assert_eq!(cli_args1.diff_execution_plan, cli_args2.diff_execution_plan);
    assert_eq!(cli_args1.experimental, cli_args2.experimental);
    assert_eq!(cli_args1.arguments, cli_args2.arguments);
    assert_eq!(cli_args1.output_format, cli_args2.output_format);
    assert_eq!(cli_args1.output_file, cli_args2.output_file);
    assert_eq!(cli_args1.print_time_summary, cli_args2.print_time_summary);
}

#[test]
fn parse_args_makers() {
    let global_config = GlobalConfig::new();
    let cli_args = parse_args(&global_config, "makers", false, Some(vec!["makers"]));

    let expected = default_parsed_cli_args();

    assert_cli_args(&cli_args, &expected);
}

#[test]
fn parse_args_cargo_make() {
    let global_config = GlobalConfig::new();
    let cli_args = parse_args(&global_config, "make", true, Some(vec!["cargo", "make"]));

    let mut expected = default_parsed_cli_args();
    expected.command = "cargo make".to_string();

    assert_cli_args(&cli_args, &expected);
}

#[test]
fn parse_args_makefile() {
    let mut cli_args = default_parse_cli_args(vec!["--makefile", "./mymakefile.toml"]);

    let mut expected = default_parsed_cli_args();
    expected.build_file = Some("./mymakefile.toml".to_string());

    assert_cli_args(&cli_args, &expected);

    cli_args = default_parse_cli_args(vec!["--makefile", "./mymakefile.toml", "taskname"]);
    expected.task = "taskname".to_string();
    expected.arguments = Some(vec![]);
    assert_cli_args(&cli_args, &expected);
}

#[test]
fn parse_args_task() {
    let mut cli_args = default_parse_cli_args(vec!["--task", "sometask"]);

    let mut expected = default_parsed_cli_args();
    expected.task = "sometask".to_string();

    assert_cli_args(&cli_args, &expected);

    cli_args = default_parse_cli_args(vec!["-t", "sometask"]);
    assert_cli_args(&cli_args, &expected);

    cli_args = default_parse_cli_args(vec!["sometask"]);
    expected.arguments = Some(vec![]);
    assert_cli_args(&cli_args, &expected);
}

#[test]
fn parse_args_profile() {
    let mut cli_args = default_parse_cli_args(vec!["--profile", "someprofile"]);

    let mut expected = default_parsed_cli_args();
    expected.profile = Some("someprofile".to_string());

    assert_cli_args(&cli_args, &expected);

    cli_args = default_parse_cli_args(vec!["-p", "someprofile"]);
    assert_cli_args(&cli_args, &expected);

    cli_args = default_parse_cli_args(vec!["--profile", "someprofile", "taskname"]);
    expected.task = "taskname".to_string();
    expected.arguments = Some(vec![]);
    assert_cli_args(&cli_args, &expected);
}

#[test]
fn parse_args_cwd() {
    let cli_args = default_parse_cli_args(vec!["--cwd", "./mydir/subdir/"]);

    let mut expected = default_parsed_cli_args();
    expected.cwd = Some("./mydir/subdir/".to_string());

    assert_cli_args(&cli_args, &expected);
}

#[test]
fn parse_args_no_workspace() {
    let cli_args = default_parse_cli_args(vec!["--no-workspace"]);

    let mut expected = default_parsed_cli_args();
    expected.disable_workspace = true;

    assert_cli_args(&cli_args, &expected);
}

#[test]
fn parse_args_allow_private() {
    let cli_args = default_parse_cli_args(vec!["--allow-private"]);

    let mut expected = default_parsed_cli_args();
    expected.allow_private = true;

    assert_cli_args(&cli_args, &expected);
}

#[test]
fn parse_args_skip_init_end_tasks() {
    let cli_args = default_parse_cli_args(vec!["--skip-init-end-tasks"]);

    let mut expected = default_parsed_cli_args();
    expected.skip_init_end_tasks = true;

    assert_cli_args(&cli_args, &expected);
}

#[test]
fn parse_args_skip_tasks() {
    let mut cli_args = default_parse_cli_args(vec!["--skip-tasks", "pre-.*"]);

    let mut expected = default_parsed_cli_args();
    expected.skip_tasks_pattern = Some("pre-.*".to_string());

    assert_cli_args(&cli_args, &expected);

    cli_args = default_parse_cli_args(vec!["--skip-tasks", "pre-.*", "taskname"]);
    expected.task = "taskname".to_string();
    expected.arguments = Some(vec![]);
    assert_cli_args(&cli_args, &expected);
}

#[test]
fn parse_args_env_file() {
    let mut cli_args = default_parse_cli_args(vec!["--env-file", "./.env"]);

    let mut expected = default_parsed_cli_args();
    expected.env_file = Some("./.env".to_string());

    assert_cli_args(&cli_args, &expected);

    cli_args = default_parse_cli_args(vec!["--env-file", "./.env", "taskname"]);
    expected.task = "taskname".to_string();
    expected.arguments = Some(vec![]);
    assert_cli_args(&cli_args, &expected);
}

#[test]
fn parse_args_env() {
    let mut cli_args = default_parse_cli_args(vec!["--env", "K=V"]);

    let mut expected = default_parsed_cli_args();
    expected.env = Some(vec!["K=V".to_string()]);

    assert_cli_args(&cli_args, &expected);

    cli_args = default_parse_cli_args(vec!["-e", "K=V"]);
    assert_cli_args(&cli_args, &expected);

    cli_args = default_parse_cli_args(vec!["-e", "K1=V1", "-e", "K2=V2", "--env", "K3=V3"]);
    expected.env = Some(vec![
        "K1=V1".to_string(),
        "K2=V2".to_string(),
        "K3=V3".to_string(),
    ]);
    assert_cli_args(&cli_args, &expected);

    cli_args = default_parse_cli_args(vec![
        "-e", "K1=V1", "-e", "K2=V2", "--env", "K3=V3", "taskname",
    ]);
    expected.task = "taskname".to_string();
    expected.arguments = Some(vec![]);
    assert_cli_args(&cli_args, &expected);
}

#[test]
fn parse_args_loglevel() {
    let mut cli_args = default_parse_cli_args(vec!["--loglevel", "verbose"]);

    let mut expected = default_parsed_cli_args();
    expected.log_level = "verbose".to_string();

    assert_cli_args(&cli_args, &expected);

    cli_args = default_parse_cli_args(vec!["-l", "verbose"]);
    assert_cli_args(&cli_args, &expected);

    cli_args = default_parse_cli_args(vec!["--loglevel", "verbose", "taskname"]);
    expected.task = "taskname".to_string();
    expected.arguments = Some(vec![]);
    assert_cli_args(&cli_args, &expected);
}

#[test]
fn parse_args_verbose() {
    let mut cli_args = default_parse_cli_args(vec!["--verbose"]);

    let mut expected = default_parsed_cli_args();
    expected.log_level = "verbose".to_string();

    assert_cli_args(&cli_args, &expected);

    cli_args = default_parse_cli_args(vec!["-v"]);
    assert_cli_args(&cli_args, &expected);
}

#[test]
fn parse_args_no_color() {
    let cli_args = default_parse_cli_args(vec!["--no-color"]);

    let mut expected = default_parsed_cli_args();
    expected.disable_color = true;

    assert_cli_args(&cli_args, &expected);
}

#[test]
fn parse_args_time_summary() {
    let cli_args = default_parse_cli_args(vec!["--time-summary"]);

    let mut expected = default_parsed_cli_args();
    expected.print_time_summary = true;

    assert_cli_args(&cli_args, &expected);
}

#[test]
fn parse_args_experimental() {
    let cli_args = default_parse_cli_args(vec!["--experimental"]);

    let mut expected = default_parsed_cli_args();
    expected.experimental = true;

    assert_cli_args(&cli_args, &expected);
}

#[test]
fn parse_args_disable_check_for_updates() {
    let cli_args = default_parse_cli_args(vec!["--disable-check-for-updates"]);

    let mut expected = default_parsed_cli_args();
    expected.disable_check_for_updates = true;

    assert_cli_args(&cli_args, &expected);
}

#[test]
fn parse_args_output_format() {
    let mut cli_args = default_parse_cli_args(vec!["--output-format", "autocomplete"]);

    let mut expected = default_parsed_cli_args();
    expected.output_format = "autocomplete".to_string();

    assert_cli_args(&cli_args, &expected);

    cli_args = default_parse_cli_args(vec!["--output-format", "autocomplete", "taskname"]);
    expected.task = "taskname".to_string();
    expected.arguments = Some(vec![]);
    assert_cli_args(&cli_args, &expected);
}

#[test]
fn parse_args_output_file() {
    let mut cli_args = default_parse_cli_args(vec!["--output-file", "./out"]);

    let mut expected = default_parsed_cli_args();
    expected.output_file = Some("./out".to_string());

    assert_cli_args(&cli_args, &expected);

    cli_args = default_parse_cli_args(vec!["--output-file", "./out", "taskname"]);
    expected.task = "taskname".to_string();
    expected.arguments = Some(vec![]);
    assert_cli_args(&cli_args, &expected);
}

#[test]
fn parse_args_print_steps() {
    let cli_args = default_parse_cli_args(vec!["--print-steps"]);

    let mut expected = default_parsed_cli_args();
    expected.print_only = true;

    assert_cli_args(&cli_args, &expected);
}

#[test]
fn parse_args_list_all_steps() {
    let cli_args = default_parse_cli_args(vec!["--list-all-steps"]);

    let mut expected = default_parsed_cli_args();
    expected.list_all_steps = true;

    assert_cli_args(&cli_args, &expected);
}

#[test]
fn parse_args_list_category_steps() {
    let mut cli_args = default_parse_cli_args(vec!["--list-category-steps", "build"]);

    let mut expected = default_parsed_cli_args();
    expected.list_category_steps = Some("build".to_string());

    assert_cli_args(&cli_args, &expected);

    cli_args = default_parse_cli_args(vec!["--list-category-steps", "build", "taskname"]);
    expected.task = "taskname".to_string();
    expected.arguments = Some(vec![]);
    assert_cli_args(&cli_args, &expected);
}

#[test]
fn parse_args_diff_steps() {
    let cli_args = default_parse_cli_args(vec!["--diff-steps"]);

    let mut expected = default_parsed_cli_args();
    expected.diff_execution_plan = true;

    assert_cli_args(&cli_args, &expected);
}

#[test]
fn parse_args_task_cmd() {
    let mut cli_args = default_parse_cli_args(vec!["task1"]);

    let mut expected = default_parsed_cli_args();
    expected.task = "task1".to_string();
    expected.arguments = Some(vec![]);

    assert_cli_args(&cli_args, &expected);

    cli_args = default_parse_cli_args(vec!["--", "task1"]);
    assert_cli_args(&cli_args, &expected);

    cli_args = default_parse_cli_args(vec!["task1", "arg1", "arg2"]);
    expected.arguments = Some(vec!["arg1".to_string(), "arg2".to_string()]);
    assert_cli_args(&cli_args, &expected);

    cli_args = default_parse_cli_args(vec!["--", "task1", "arg1", "arg2"]);
    assert_cli_args(&cli_args, &expected);
}
<|endoftext|>