<commit_before>/*
 * Copyright 2020 Google LLC All Rights Reserved.
 *
 * 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 serde::{Deserialize, Serialize};
use slog::{o, warn, Logger};

use metrics::Metrics;

use crate::extensions::filters::CAPTURED_BYTES;
use crate::extensions::{
    CreateFilterArgs, DownstreamContext, DownstreamResponse, Error, Filter, FilterFactory,
    UpstreamContext, UpstreamResponse,
};

mod metrics;

#[derive(Serialize, Deserialize, Debug)]
/// Strategy to apply for acquiring a set of bytes in the UDP packet
enum Strategy {
    #[serde(rename = "PREFIX")]
    /// Looks for the set of bytes at the beginning of the packet
    Prefix,
    #[serde(rename = "SUFFIX")]
    /// Look for the set of bytes at the end of the packet
    Suffix,
}

#[derive(Serialize, Deserialize, Debug)]
struct Config {
    #[serde(default)]
    strategy: Strategy,
    /// the number of bytes to capture
    #[serde(rename = "size")]
    size: usize,
    /// the key to use when storing the captured bytes in the filter context
    #[serde(rename = "metadataKey")]
    #[serde(default = "default_metadata_key")]
    metadata_key: String,
    /// whether or not to remove the set of the bytes from the packet once captured
    #[serde(default)]
    remove: bool,
}

/// default value for the context key in the Config
fn default_metadata_key() -> String {
    CAPTURED_BYTES.into()
}

impl Default for Strategy {
    fn default() -> Self {
        Strategy::Suffix
    }
}

pub struct CaptureBytesFactory {
    log: Logger,
}

impl CaptureBytesFactory {
    pub fn new(base: &Logger) -> Self {
        CaptureBytesFactory { log: base.clone() }
    }
}

impl FilterFactory for CaptureBytesFactory {
    fn name(&self) -> String {
        "quilkin.extensions.filters.capture_bytes.v1alpha1.CaptureBytes".into()
    }

    fn create_filter(&self, args: CreateFilterArgs) -> Result<Box<dyn Filter>, Error> {
        let config: Config = serde_yaml::to_string(&args.config)
            .and_then(|raw_config| serde_yaml::from_str(raw_config.as_str()))
            .map_err(|err| Error::DeserializeFailed(err.to_string()))?;

        Ok(Box::new(CaptureBytes::new(
            &self.log,
            config,
            Metrics::new(&args.metrics_registry)?,
        )))
    }
}

struct CaptureBytes {
    log: Logger,
    capture: Box<dyn Capture + Sync + Send>,
    /// metrics reporter for this filter.
    metrics: Metrics,
    metadata_key: String,
    size: usize,
    remove: bool,
}

impl CaptureBytes {
    fn new(base: &Logger, config: Config, metrics: Metrics) -> Self {
        let capture: Box<dyn Capture + Sync + Send> = match config.strategy {
            Strategy::Prefix => Box::new(Prefix {}),
            Strategy::Suffix => Box::new(Suffix {}),
        };

        CaptureBytes {
            log: base.new(o!("source" => "extensions::CaptureBytes")),
            capture,
            metrics,
            metadata_key: config.metadata_key,
            size: config.size,
            remove: config.remove,
        }
    }
}

impl Filter for CaptureBytes {
    fn on_downstream_receive(&self, mut ctx: DownstreamContext) -> Option<DownstreamResponse> {
        // if the capture size is bigger than the packet size, then we drop the packet,
        // and occasionally warn
        if ctx.contents.len() < self.size {
            if self.metrics.packets_dropped_total.get() % 1000 == 0 {
                warn!(
                    self.log,
                    "packets are being dropped due to their length being less than {} bytes",
                    self.size; "count" => self.metrics.packets_dropped_total.get()
                );
            }
            self.metrics.packets_dropped_total.inc();
            return None;
        }
        let token = self
            .capture
            .capture(&mut ctx.contents, self.size, self.remove);

        ctx.metadata
            .insert(self.metadata_key.clone(), Box::new(token));

        Some(ctx.into())
    }

    fn on_upstream_receive(&self, ctx: UpstreamContext) -> Option<UpstreamResponse> {
        Some(ctx.into())
    }
}

/// Trait to implement different strategies for capturing packet data
trait Capture {
    /// Capture the packet data from the contents. If remove is true, contents will be altered to
    /// not have the retrieved set of bytes.
    /// Returns the captured bytes.
    fn capture(&self, contents: &mut Vec<u8>, size: usize, remove: bool) -> Vec<u8>;
}

struct Suffix;
impl Capture for Suffix {
    fn capture(&self, contents: &mut Vec<u8>, size: usize, remove: bool) -> Vec<u8> {
        if remove {
            return contents.split_off(contents.len() - size);
        }

        contents
            .iter()
            .skip(contents.len() - size)
            .cloned()
            .collect::<Vec<u8>>()
    }
}

struct Prefix;
impl Capture for Prefix {
    fn capture(&self, contents: &mut Vec<u8>, size: usize, remove: bool) -> Vec<u8> {
        if remove {
            return contents.drain(..size).collect();
        }

        contents.iter().cloned().take(size).collect()
    }
}

#[cfg(test)]
mod tests {
    use prometheus::Registry;
    use serde_yaml::{Mapping, Value};

    use crate::config::{ConnectionConfig, EndPoint};
    use crate::test_utils::{assert_filter_on_downstream_receive_no_change, logger};

    use super::*;

    const TOKEN_KEY: &str = "TOKEN";

    fn capture_bytes(config: Config) -> CaptureBytes {
        CaptureBytes::new(
            &logger(),
            config,
            Metrics::new(&Registry::default()).unwrap(),
        )
    }

    #[test]
    fn factory_valid_config_all() {
        let factory = CaptureBytesFactory::new(&logger());
        let connection = ConnectionConfig::Server { endpoints: vec![] };
        let mut map = Mapping::new();
        map.insert(
            Value::String("strategy".into()),
            Value::String("SUFFIX".into()),
        );
        map.insert(
            Value::String("metadataKey".into()),
            Value::String(TOKEN_KEY.into()),
        );
        map.insert(Value::String("size".into()), Value::Number(3.into()));
        map.insert(Value::String("remove".into()), Value::Bool(true));

        let filter = factory
            .create_filter(CreateFilterArgs::new(
                &connection,
                Some(&Value::Mapping(map)),
            ))
            .unwrap();
        assert_end_strategy(filter.as_ref(), TOKEN_KEY, true);
    }

    #[test]
    fn factory_valid_config_defaults() {
        let factory = CaptureBytesFactory::new(&logger());
        let connection = ConnectionConfig::Server { endpoints: vec![] };
        let mut map = Mapping::new();
        map.insert(Value::String("size".into()), Value::Number(3.into()));
        let filter = factory
            .create_filter(CreateFilterArgs::new(
                &connection,
                Some(&Value::Mapping(map)),
            ))
            .unwrap();
        assert_end_strategy(filter.as_ref(), CAPTURED_BYTES, false);
    }

    #[test]
    fn factory_invalid_config() {
        let factory = CaptureBytesFactory::new(&logger());
        let connection = ConnectionConfig::Server { endpoints: vec![] };
        let mut map = Mapping::new();
        map.insert(Value::String("size".into()), Value::String("WRONG".into()));

        let result = factory.create_filter(CreateFilterArgs::new(
            &connection,
            Some(&Value::Mapping(map)),
        ));
        assert!(result.is_err(), "Should be an error");
    }

    #[test]
    fn on_downstream_receive() {
        let config = Config {
            strategy: Strategy::Suffix,
            metadata_key: TOKEN_KEY.into(),
            size: 3,
            remove: true,
        };
        let filter = capture_bytes(config);
        assert_end_strategy(&filter, TOKEN_KEY, true);
    }

    #[test]
    fn on_downstream_receive_overflow_capture_size() {
        let config = Config {
            strategy: Strategy::Suffix,
            metadata_key: TOKEN_KEY.into(),
            size: 99,
            remove: true,
        };
        let filter = capture_bytes(config);
        let endpoints = vec![EndPoint {
            name: "e1".to_string(),
            address: "127.0.0.1:81".parse().unwrap(),
            connection_ids: vec![],
        }];
        let response = filter.on_downstream_receive(DownstreamContext::new(
            endpoints,
            "127.0.0.1:80".parse().unwrap(),
            "abc".to_string().into_bytes(),
        ));

        assert!(response.is_none());
        let count = filter.metrics.packets_dropped_total.get();
        assert_eq!(1, count);
    }

    #[test]
    fn on_upstream_receive() {
        let config = Config {
            strategy: Strategy::Suffix,
            metadata_key: TOKEN_KEY.into(),
            size: 0,
            remove: false,
        };
        let filter = capture_bytes(config);
        assert_filter_on_downstream_receive_no_change(&filter);
    }

    #[test]
    fn end_capture() {
        let end = Suffix {};
        let mut contents = b"helloabc".to_vec();
        let result = end.capture(&mut contents, 3, false);
        assert_eq!(b"abc".to_vec(), result);
        assert_eq!(b"helloabc".to_vec(), contents);

        let result = end.capture(&mut contents, 3, true);
        assert_eq!(b"abc".to_vec(), result);
        assert_eq!(b"hello".to_vec(), contents);
    }

    #[test]
    fn beginning_capture() {
        let beg = Prefix {};
        let mut contents = b"abchello".to_vec();

        let result = beg.capture(&mut contents, 3, false);
        assert_eq!(b"abc".to_vec(), result);
        assert_eq!(b"abchello".to_vec(), contents);

        let result = beg.capture(&mut contents, 3, true);
        assert_eq!(b"abc".to_vec(), result);
        assert_eq!(b"hello".to_vec(), contents);
    }

    fn assert_end_strategy<F>(filter: &F, key: &str, remove: bool)
    where
        F: Filter + ?Sized,
    {
        let endpoints = vec![EndPoint {
            name: "e1".to_string(),
            address: "127.0.0.1:81".parse().unwrap(),
            connection_ids: vec![],
        }];
        let response = filter
            .on_downstream_receive(DownstreamContext::new(
                endpoints,
                "127.0.0.1:80".parse().unwrap(),
                "helloabc".to_string().into_bytes(),
            ))
            .unwrap();

        if remove {
            assert_eq!(b"hello".to_vec(), response.contents);
        } else {
            assert_eq!(b"helloabc".to_vec(), response.contents);
        }

        let token = response
            .metadata
            .get(key)
            .unwrap()
            .downcast_ref::<Vec<u8>>()
            .unwrap();
        assert_eq!(b"abc", token.as_slice());
    }
}
<commit_msg>Fix unit test for CaptureBytes (#134)<commit_after>/*
 * Copyright 2020 Google LLC All Rights Reserved.
 *
 * 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 serde::{Deserialize, Serialize};
use slog::{o, warn, Logger};

use metrics::Metrics;

use crate::extensions::filters::CAPTURED_BYTES;
use crate::extensions::{
    CreateFilterArgs, DownstreamContext, DownstreamResponse, Error, Filter, FilterFactory,
    UpstreamContext, UpstreamResponse,
};

mod metrics;

#[derive(Serialize, Deserialize, Debug)]
/// Strategy to apply for acquiring a set of bytes in the UDP packet
enum Strategy {
    #[serde(rename = "PREFIX")]
    /// Looks for the set of bytes at the beginning of the packet
    Prefix,
    #[serde(rename = "SUFFIX")]
    /// Look for the set of bytes at the end of the packet
    Suffix,
}

#[derive(Serialize, Deserialize, Debug)]
struct Config {
    #[serde(default)]
    strategy: Strategy,
    /// the number of bytes to capture
    #[serde(rename = "size")]
    size: usize,
    /// the key to use when storing the captured bytes in the filter context
    #[serde(rename = "metadataKey")]
    #[serde(default = "default_metadata_key")]
    metadata_key: String,
    /// whether or not to remove the set of the bytes from the packet once captured
    #[serde(default)]
    remove: bool,
}

/// default value for the context key in the Config
fn default_metadata_key() -> String {
    CAPTURED_BYTES.into()
}

impl Default for Strategy {
    fn default() -> Self {
        Strategy::Suffix
    }
}

pub struct CaptureBytesFactory {
    log: Logger,
}

impl CaptureBytesFactory {
    pub fn new(base: &Logger) -> Self {
        CaptureBytesFactory { log: base.clone() }
    }
}

impl FilterFactory for CaptureBytesFactory {
    fn name(&self) -> String {
        "quilkin.extensions.filters.capture_bytes.v1alpha1.CaptureBytes".into()
    }

    fn create_filter(&self, args: CreateFilterArgs) -> Result<Box<dyn Filter>, Error> {
        let config: Config = serde_yaml::to_string(&args.config)
            .and_then(|raw_config| serde_yaml::from_str(raw_config.as_str()))
            .map_err(|err| Error::DeserializeFailed(err.to_string()))?;

        Ok(Box::new(CaptureBytes::new(
            &self.log,
            config,
            Metrics::new(&args.metrics_registry)?,
        )))
    }
}

struct CaptureBytes {
    log: Logger,
    capture: Box<dyn Capture + Sync + Send>,
    /// metrics reporter for this filter.
    metrics: Metrics,
    metadata_key: String,
    size: usize,
    remove: bool,
}

impl CaptureBytes {
    fn new(base: &Logger, config: Config, metrics: Metrics) -> Self {
        let capture: Box<dyn Capture + Sync + Send> = match config.strategy {
            Strategy::Prefix => Box::new(Prefix {}),
            Strategy::Suffix => Box::new(Suffix {}),
        };

        CaptureBytes {
            log: base.new(o!("source" => "extensions::CaptureBytes")),
            capture,
            metrics,
            metadata_key: config.metadata_key,
            size: config.size,
            remove: config.remove,
        }
    }
}

impl Filter for CaptureBytes {
    fn on_downstream_receive(&self, mut ctx: DownstreamContext) -> Option<DownstreamResponse> {
        // if the capture size is bigger than the packet size, then we drop the packet,
        // and occasionally warn
        if ctx.contents.len() < self.size {
            if self.metrics.packets_dropped_total.get() % 1000 == 0 {
                warn!(
                    self.log,
                    "packets are being dropped due to their length being less than {} bytes",
                    self.size; "count" => self.metrics.packets_dropped_total.get()
                );
            }
            self.metrics.packets_dropped_total.inc();
            return None;
        }
        let token = self
            .capture
            .capture(&mut ctx.contents, self.size, self.remove);

        ctx.metadata
            .insert(self.metadata_key.clone(), Box::new(token));

        Some(ctx.into())
    }

    fn on_upstream_receive(&self, ctx: UpstreamContext) -> Option<UpstreamResponse> {
        Some(ctx.into())
    }
}

/// Trait to implement different strategies for capturing packet data
trait Capture {
    /// Capture the packet data from the contents. If remove is true, contents will be altered to
    /// not have the retrieved set of bytes.
    /// Returns the captured bytes.
    fn capture(&self, contents: &mut Vec<u8>, size: usize, remove: bool) -> Vec<u8>;
}

struct Suffix;
impl Capture for Suffix {
    fn capture(&self, contents: &mut Vec<u8>, size: usize, remove: bool) -> Vec<u8> {
        if remove {
            return contents.split_off(contents.len() - size);
        }

        contents
            .iter()
            .skip(contents.len() - size)
            .cloned()
            .collect::<Vec<u8>>()
    }
}

struct Prefix;
impl Capture for Prefix {
    fn capture(&self, contents: &mut Vec<u8>, size: usize, remove: bool) -> Vec<u8> {
        if remove {
            return contents.drain(..size).collect();
        }

        contents.iter().cloned().take(size).collect()
    }
}

#[cfg(test)]
mod tests {
    use prometheus::Registry;
    use serde_yaml::{Mapping, Value};

    use crate::config::{ConnectionConfig, EndPoint};
    use crate::test_utils::{assert_filter_on_upstream_receive_no_change, logger};

    use super::*;

    const TOKEN_KEY: &str = "TOKEN";

    fn capture_bytes(config: Config) -> CaptureBytes {
        CaptureBytes::new(
            &logger(),
            config,
            Metrics::new(&Registry::default()).unwrap(),
        )
    }

    #[test]
    fn factory_valid_config_all() {
        let factory = CaptureBytesFactory::new(&logger());
        let connection = ConnectionConfig::Server { endpoints: vec![] };
        let mut map = Mapping::new();
        map.insert(
            Value::String("strategy".into()),
            Value::String("SUFFIX".into()),
        );
        map.insert(
            Value::String("metadataKey".into()),
            Value::String(TOKEN_KEY.into()),
        );
        map.insert(Value::String("size".into()), Value::Number(3.into()));
        map.insert(Value::String("remove".into()), Value::Bool(true));

        let filter = factory
            .create_filter(CreateFilterArgs::new(
                &connection,
                Some(&Value::Mapping(map)),
            ))
            .unwrap();
        assert_end_strategy(filter.as_ref(), TOKEN_KEY, true);
    }

    #[test]
    fn factory_valid_config_defaults() {
        let factory = CaptureBytesFactory::new(&logger());
        let connection = ConnectionConfig::Server { endpoints: vec![] };
        let mut map = Mapping::new();
        map.insert(Value::String("size".into()), Value::Number(3.into()));
        let filter = factory
            .create_filter(CreateFilterArgs::new(
                &connection,
                Some(&Value::Mapping(map)),
            ))
            .unwrap();
        assert_end_strategy(filter.as_ref(), CAPTURED_BYTES, false);
    }

    #[test]
    fn factory_invalid_config() {
        let factory = CaptureBytesFactory::new(&logger());
        let connection = ConnectionConfig::Server { endpoints: vec![] };
        let mut map = Mapping::new();
        map.insert(Value::String("size".into()), Value::String("WRONG".into()));

        let result = factory.create_filter(CreateFilterArgs::new(
            &connection,
            Some(&Value::Mapping(map)),
        ));
        assert!(result.is_err(), "Should be an error");
    }

    #[test]
    fn on_downstream_receive() {
        let config = Config {
            strategy: Strategy::Suffix,
            metadata_key: TOKEN_KEY.into(),
            size: 3,
            remove: true,
        };
        let filter = capture_bytes(config);
        assert_end_strategy(&filter, TOKEN_KEY, true);
    }

    #[test]
    fn on_downstream_receive_overflow_capture_size() {
        let config = Config {
            strategy: Strategy::Suffix,
            metadata_key: TOKEN_KEY.into(),
            size: 99,
            remove: true,
        };
        let filter = capture_bytes(config);
        let endpoints = vec![EndPoint {
            name: "e1".to_string(),
            address: "127.0.0.1:81".parse().unwrap(),
            connection_ids: vec![],
        }];
        let response = filter.on_downstream_receive(DownstreamContext::new(
            endpoints,
            "127.0.0.1:80".parse().unwrap(),
            "abc".to_string().into_bytes(),
        ));

        assert!(response.is_none());
        let count = filter.metrics.packets_dropped_total.get();
        assert_eq!(1, count);
    }

    #[test]
    fn on_upstream_receive() {
        let config = Config {
            strategy: Strategy::Suffix,
            metadata_key: TOKEN_KEY.into(),
            size: 0,
            remove: false,
        };
        let filter = capture_bytes(config);
        assert_filter_on_upstream_receive_no_change(&filter);
    }

    #[test]
    fn end_capture() {
        let end = Suffix {};
        let mut contents = b"helloabc".to_vec();
        let result = end.capture(&mut contents, 3, false);
        assert_eq!(b"abc".to_vec(), result);
        assert_eq!(b"helloabc".to_vec(), contents);

        let result = end.capture(&mut contents, 3, true);
        assert_eq!(b"abc".to_vec(), result);
        assert_eq!(b"hello".to_vec(), contents);
    }

    #[test]
    fn beginning_capture() {
        let beg = Prefix {};
        let mut contents = b"abchello".to_vec();

        let result = beg.capture(&mut contents, 3, false);
        assert_eq!(b"abc".to_vec(), result);
        assert_eq!(b"abchello".to_vec(), contents);

        let result = beg.capture(&mut contents, 3, true);
        assert_eq!(b"abc".to_vec(), result);
        assert_eq!(b"hello".to_vec(), contents);
    }

    fn assert_end_strategy<F>(filter: &F, key: &str, remove: bool)
    where
        F: Filter + ?Sized,
    {
        let endpoints = vec![EndPoint {
            name: "e1".to_string(),
            address: "127.0.0.1:81".parse().unwrap(),
            connection_ids: vec![],
        }];
        let response = filter
            .on_downstream_receive(DownstreamContext::new(
                endpoints,
                "127.0.0.1:80".parse().unwrap(),
                "helloabc".to_string().into_bytes(),
            ))
            .unwrap();

        if remove {
            assert_eq!(b"hello".to_vec(), response.contents);
        } else {
            assert_eq!(b"helloabc".to_vec(), response.contents);
        }

        let token = response
            .metadata
            .get(key)
            .unwrap()
            .downcast_ref::<Vec<u8>>()
            .unwrap();
        assert_eq!(b"abc", token.as_slice());
    }
}
<|endoftext|>


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

// This actually tests a lot more than just encodable/decodable, but it gets the
// job done at least

extern mod extra;

use std::io;
use std::rand::{random, Rand};
use extra::serialize::*;
use extra::ebml;
use extra::ebml::writer::Encoder;
use extra::ebml::reader::Decoder;

#[deriving(Encodable, Decodable, Eq, Rand)]
struct A;
#[deriving(Encodable, Decodable, Eq, Rand)]
struct B(int);
#[deriving(Encodable, Decodable, Eq, Rand)]
struct C(int, int, uint);

#[deriving(Encodable, Decodable, Eq, Rand)]
struct D {
    a: int,
    b: uint,
}

#[deriving(Encodable, Decodable, Eq, Rand)]
enum E {
    E1,
    E2(uint),
    E3(D),
    E4{ x: uint },
}

#[deriving(Encodable, Decodable, Eq, Rand)]
enum F { F1 }

#[deriving(Encodable, Decodable, Eq, Rand)]
struct G<T> {
    t: T
}

fn roundtrip<T: Rand + Eq + Encodable<Encoder> + Decodable<Decoder>>() {
    let obj: T = random();
    let bytes = do io::with_bytes_writer |w| {
        let mut e = Encoder(w);
        obj.encode(&mut e);
    };
    let doc = ebml::reader::Doc(@bytes);
    let mut dec = Decoder(doc);
    let obj2 = Decodable::decode(&mut dec);
    assert!(obj == obj2);
}

pub fn main() {
    roundtrip::<A>();
    roundtrip::<B>();
    roundtrip::<C>();
    roundtrip::<D>();

    for 20.times {
        roundtrip::<E>();
        roundtrip::<F>();
        roundtrip::<G<int>>();
    }
}
<commit_msg>xfail-fast the deriving test<commit_after>// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// This actually tests a lot more than just encodable/decodable, but it gets the
// job done at least

// xfail-fast

extern mod extra;

use std::io;
use std::rand::{random, Rand};
use extra::serialize::*;
use extra::ebml;
use extra::ebml::writer::Encoder;
use extra::ebml::reader::Decoder;

#[deriving(Encodable, Decodable, Eq, Rand)]
struct A;
#[deriving(Encodable, Decodable, Eq, Rand)]
struct B(int);
#[deriving(Encodable, Decodable, Eq, Rand)]
struct C(int, int, uint);

#[deriving(Encodable, Decodable, Eq, Rand)]
struct D {
    a: int,
    b: uint,
}

#[deriving(Encodable, Decodable, Eq, Rand)]
enum E {
    E1,
    E2(uint),
    E3(D),
    E4{ x: uint },
}

#[deriving(Encodable, Decodable, Eq, Rand)]
enum F { F1 }

#[deriving(Encodable, Decodable, Eq, Rand)]
struct G<T> {
    t: T
}

fn roundtrip<T: Rand + Eq + Encodable<Encoder> + Decodable<Decoder>>() {
    let obj: T = random();
    let bytes = do io::with_bytes_writer |w| {
        let mut e = Encoder(w);
        obj.encode(&mut e);
    };
    let doc = ebml::reader::Doc(@bytes);
    let mut dec = Decoder(doc);
    let obj2 = Decodable::decode(&mut dec);
    assert!(obj == obj2);
}

pub fn main() {
    roundtrip::<A>();
    roundtrip::<B>();
    roundtrip::<C>();
    roundtrip::<D>();

    for 20.times {
        roundtrip::<E>();
        roundtrip::<F>();
        roundtrip::<G<int>>();
    }
}
<|endoftext|>


<commit_before>use std::cmp::min;
use std::iter::IteratorExt;
use std::collections::{BTreeMap, RingBuf};
use std::string::ToString;
use serialize::json::Json;

use self::TemplateElement::{RawString, Expression, HelperExpression,
                            HTMLExpression, HelperBlock, Comment};

#[deriving(PartialEq)]
pub struct Template {
    pub elements: Vec<TemplateElement>
}

#[deriving(PartialEq, Show)]
enum ParserState {
    Text,
    HtmlExpression,
    Comment,
    HelperStart,
    HelperEnd,
    Expression,
    Invalid
}

#[deriving(PartialEq)]
pub struct Helper {
    name: String,
    params: Vec<String>,
    hash: BTreeMap<String, Json>,
    template: Option<Template>,
    inverse: Option<Template>,
    block: bool
}

impl Helper {
    pub fn name(&self) -> &String {
        &self.name
    }

    pub fn params(&self) -> &Vec<String> {
        &self.params
    }

    pub fn hash(&self) -> &BTreeMap<String, Json> {
        &self.hash
    }

    pub fn template(&self) -> Option<&Template> {
        match self.template {
            Some(ref t) => {
                Some(t)
            },
            None => None
        }
    }

    pub fn inverse(&self) -> Option<&Template> {
        match self.inverse {
            Some(ref t) => {
                Some(t)
            },
            None => None
        }
    }

    pub fn is_block(&self) -> bool {
        self.block
    }
}

impl ToString for Helper {
    fn to_string(&self) -> String {

        let mut buf = String::new();

        if self.block {
            buf.push_str(format!("{{{{#{}", self.name).as_slice());
        } else {
            buf.push_str(format!("{{{{{}", self.name).as_slice());
        }

        for p in self.params.iter() {
            buf.push_str(format!(" {}", p).as_slice());
        }

        for k in self.hash.keys() {
            buf.push_str(format!(" {}={}", k, self.hash.get(k).unwrap()).as_slice());
        }

        buf.push_str("}}");

        if self.block {
            let tpl = self.template();
            if tpl.is_some() {
                buf.push_str(tpl.unwrap().to_string().as_slice());
            }
            let ivs = self.inverse();
            if ivs.is_some() {
                buf.push_str("{{else}}");
                buf.push_str(ivs.unwrap().to_string().as_slice());
            }
            buf.push_str(format!("{{{{/{}}}}}", self.name).as_slice());
        }
        buf
    }
}

#[deriving(Show, Copy)]
pub struct TemplateError;

impl Helper {
    fn parse(source: String, block: bool) -> Result<Helper, TemplateError> {
        let mut tokens = source.split(|&: c: char| c.is_whitespace())
            .filter(|s: &&str| !(*s).is_empty());

        let name = tokens.next();
        match name {
            Some(n) => {
                let mut params: Vec<String> = Vec::new();
                let mut hash: BTreeMap<String, Json> = BTreeMap::new();

                for t in tokens {
                    if t.contains_char('=') {
                        let kv = t.split('=').collect::<Vec<&str>>();
                        hash.insert(kv.get(0).unwrap().to_string(),
                                    kv.get(1).unwrap().parse::<Json>().unwrap());
                    } else {
                        params.push(t.to_string());
                    }
                }

                Ok(Helper{
                    name: n.to_string(),
                    params: params,
                    hash: hash,
                    template: Option::None,
                    inverse: Option::None,
                    block: block
                })
            },
            None =>
                Err(TemplateError)
        }
    }
}

impl Template {
    pub fn compile(source: String) -> Result<Template, TemplateError> {
        let mut helper_stack: RingBuf<Helper> = RingBuf::new();
        let mut template_stack: RingBuf<Template> = RingBuf::new();
        template_stack.push_front(Template{ elements: Vec::new() });

        let mut buffer: String = String::new();
        let mut state = ParserState::Text;

        let mut c:uint = 0;
        let source_len = source.chars().count();
        while c < source_len {
            let slice = source.slice_chars(c, min(c+3, source_len));

            state = match slice {
                "{{{" | "{{!" | "{{#" | "{{/" => {
                    c += 2;
                    if !buffer.is_empty() {
                        let mut t = template_stack.front_mut().unwrap();
                        t.elements.push(RawString(buffer.clone()));
                        buffer.clear();
                    }
                    match slice {
                        "{{{" => ParserState::HtmlExpression,
                        "{{!" => ParserState::Comment,
                        "{{#" => ParserState::HelperStart,
                        "{{/" => {
                            let t = template_stack.pop_front().unwrap();
                            let h = helper_stack.front_mut().unwrap();
                            if h.template.is_some() {
                                h.inverse = Some(t);
                            } else {
                                h.template = Some(t);
                            }
                            ParserState::HelperEnd
                        },
                        _ => ParserState::Invalid
                    }
                },
                "}}}" => {
                    c += 2;
                    let mut t = template_stack.front_mut().unwrap();
                    t.elements.push(HTMLExpression(
                        buffer.clone().trim_chars(' ').to_string()));
                    buffer.clear();
                    ParserState::Text
                },
                _ => {
                    match if slice.chars().count() > 2 { slice.slice_chars(0, 2) } else { slice } {
                        "{{" => {
                            c += 1;
                            if !buffer.is_empty() {
                                let mut t = template_stack.front_mut().unwrap();
                                t.elements.push(RawString(buffer.clone()));
                                buffer.clear();
                            }
                            ParserState::Expression
                        },
                        "}}" => {
                            c += 1;
                            match state {
                                ParserState::Expression => {
                                    if !buffer.is_empty() {
                                        // {{else}} within a helper block
                                        if buffer.trim_chars(' ') == "else" {
                                            buffer.clear(); // drop else
                                            let t = template_stack.pop_front().unwrap();
                                            let h = helper_stack.front_mut().unwrap();
                                            h.template = Some(t);
                                            template_stack.push_front(Template{ elements: Vec::new() });
                                            ParserState::Text
                                        } else if buffer.contains_char(' ') {
                                            //inline helper
                                            match Helper::parse(buffer.clone(), false){
                                                Ok(helper) => {
                                                    let mut t = template_stack.front_mut().unwrap();
                                                    t.elements.push(HelperExpression(helper));
                                                    buffer.clear();
                                                    ParserState::Text
                                                },
                                                Err(_) => ParserState::Invalid
                                            }
                                        } else {
                                            let mut t = template_stack.front_mut().unwrap();
                                            t.elements.push(Expression(
                                                buffer.clone().trim_chars(' ').to_string()));
                                            buffer.clear();
                                            ParserState::Text
                                        }

                                    } else {
                                        ParserState::Invalid
                                    }
                                },
                                ParserState::Comment => {
                                    let mut t = template_stack.front_mut().unwrap();
                                    t.elements.push(Comment(buffer.clone()));
                                    buffer.clear();
                                    ParserState::Text
                                },
                                ParserState::HelperStart => {
                                    match Helper::parse(buffer.clone(), true) {
                                        Ok(helper) => {
                                            helper_stack.push_front(helper);
                                            template_stack.push_front(Template{ elements: Vec::new() });

                                            buffer.clear();
                                            ParserState::Text
                                        },
                                        Err(_) => ParserState::Invalid
                                    }
                                },
                                ParserState::HelperEnd => {
                                    let name = buffer.trim_chars(' ').to_string();
                                    if name == helper_stack.front().unwrap().name {
                                        let h = helper_stack.pop_front().unwrap();
                                        let mut t = template_stack.front_mut().unwrap();
                                        t.elements.push(HelperBlock(h));
                                        buffer.clear();
                                        ParserState::Text
                                    } else {
                                        ParserState::Invalid
                                    }
                                },
                                _ => ParserState::Invalid
                            }
                        },
                        _ => {
                            buffer.push(source.char_at(c));
                            state
                        }
                    }
                }
            };
            if state == ParserState::Invalid {
                return Err(TemplateError);
            }
            c += 1;
        }

        if !buffer.is_empty() {
            let mut t = template_stack.front_mut().unwrap();
            t.elements.push(TemplateElement::RawString(buffer.clone()));
        }

        return Ok(template_stack.pop_front().unwrap());
    }
}

impl ToString for Template {
    fn to_string(&self) -> String {
        let mut buf = String::new();
        for v in self.elements.iter() {
            buf.push_str(v.to_string().as_slice());
        }
        buf
    }
}

#[deriving(PartialEq)]
pub enum TemplateElement {
    RawString(String),
    Expression(String),
    HTMLExpression(String),
    HelperExpression(Helper),
    HelperBlock(Helper),
    Comment(String),
}

impl ToString for TemplateElement {
    fn to_string(&self) -> String {
        match *self {
            RawString(ref v) => {
                v.clone()
            },
            Expression(ref v) => {
                // {{ escape to {
                format!("{{{{{}}}}}", v)
            },
            HTMLExpression(ref v) => {
                format!("{{{{{{{}}}}}}}", v)
            },
            HelperExpression(ref helper) => {
                helper.to_string()
            }
            HelperBlock(ref helper) => {
                helper.to_string()
            }
            Comment(ref v) => {
                format!("{{!{}}}", v)
            }
        }
    }
}

#[test]
fn test_parse_helper_start_tag() {
    let source = "if not name compare=1".to_string();
    let h = Helper::parse(source, true).ok().unwrap();

    assert_eq!(h.name, "if".to_string());
    assert_eq!(h.params, vec::<String>!["not".to_string(), "name".to_string()]);

    let key = "compare".to_string();
    let value = h.hash.get(&key).unwrap();
    assert_eq!(value.as_u64().unwrap(), 1u64);
}

#[test]
fn test_parse_template() {
    let source = "
{{title}}
 {{{content}}}
{{#if date}}
good
{{else}}
bad
{{/if}}{{foo bar}}";
    let t = Template::compile(source.to_string()).ok().unwrap();

    assert_eq!(t.elements.len(), 8);
    assert_eq!((*t.elements.get(0).unwrap()).to_string(), "
".to_string());
    assert_eq!((*t.elements.get(1).unwrap()).to_string(),
               Expression("title".to_string()).to_string());

    assert_eq!((*t.elements.get(3).unwrap()).to_string(), "{{{content}}}".to_string());

    match *t.elements.get(5).unwrap() {
        HelperBlock(ref h) => {
            assert_eq!(h.name, "if".to_string());
            assert_eq!(h.params.len(), 1);
            assert_eq!(h.template.as_ref().unwrap().elements.len(), 1);
        },
        _ => {
            panic!("Helper expected here.");
        }
    };

    match *t.elements.get(7).unwrap() {
        HelperExpression(ref h) => {
            assert_eq!(h.name, "foo".to_string());
            assert_eq!(h.params.len(), 1);
            assert_eq!(*(h.params.get(0).unwrap()), "bar".to_string());
        },
        _ => {
            panic!("Helper expression here");
        }
    };
}

#[test]
fn test_helper_to_string() {
    let source = "{{#ifequals name compare=\"hello\"}}hello{{else}}good{{/ifequals}}".to_string();

    let t = Template::compile(source.to_string()).ok().unwrap();

    assert_eq!(t.elements.len(), 1);
    assert_eq!(t.elements.get(0).unwrap().to_string(), source);
}
<commit_msg>Added error when block helper is not closed.<commit_after>use std::cmp::min;
use std::iter::IteratorExt;
use std::collections::{BTreeMap, RingBuf};
use std::string::ToString;
use serialize::json::Json;

use self::TemplateElement::{RawString, Expression, HelperExpression,
                            HTMLExpression, HelperBlock, Comment};

#[deriving(PartialEq)]
pub struct Template {
    pub elements: Vec<TemplateElement>
}

#[deriving(PartialEq, Show)]
enum ParserState {
    Text,
    HtmlExpression,
    Comment,
    HelperStart,
    HelperEnd,
    Expression,
    Invalid
}

#[deriving(PartialEq)]
pub struct Helper {
    name: String,
    params: Vec<String>,
    hash: BTreeMap<String, Json>,
    template: Option<Template>,
    inverse: Option<Template>,
    block: bool
}

impl Helper {
    pub fn name(&self) -> &String {
        &self.name
    }

    pub fn params(&self) -> &Vec<String> {
        &self.params
    }

    pub fn hash(&self) -> &BTreeMap<String, Json> {
        &self.hash
    }

    pub fn template(&self) -> Option<&Template> {
        match self.template {
            Some(ref t) => {
                Some(t)
            },
            None => None
        }
    }

    pub fn inverse(&self) -> Option<&Template> {
        match self.inverse {
            Some(ref t) => {
                Some(t)
            },
            None => None
        }
    }

    pub fn is_block(&self) -> bool {
        self.block
    }
}

impl ToString for Helper {
    fn to_string(&self) -> String {

        let mut buf = String::new();

        if self.block {
            buf.push_str(format!("{{{{#{}", self.name).as_slice());
        } else {
            buf.push_str(format!("{{{{{}", self.name).as_slice());
        }

        for p in self.params.iter() {
            buf.push_str(format!(" {}", p).as_slice());
        }

        for k in self.hash.keys() {
            buf.push_str(format!(" {}={}", k, self.hash.get(k).unwrap()).as_slice());
        }

        buf.push_str("}}");

        if self.block {
            let tpl = self.template();
            if tpl.is_some() {
                buf.push_str(tpl.unwrap().to_string().as_slice());
            }
            let ivs = self.inverse();
            if ivs.is_some() {
                buf.push_str("{{else}}");
                buf.push_str(ivs.unwrap().to_string().as_slice());
            }
            buf.push_str(format!("{{{{/{}}}}}", self.name).as_slice());
        }
        buf
    }
}

#[deriving(Show, Copy)]
pub struct TemplateError;

impl Helper {
    fn parse(source: String, block: bool) -> Result<Helper, TemplateError> {
        let mut tokens = source.split(|&: c: char| c.is_whitespace())
            .filter(|s: &&str| !(*s).is_empty());

        let name = tokens.next();
        match name {
            Some(n) => {
                let mut params: Vec<String> = Vec::new();
                let mut hash: BTreeMap<String, Json> = BTreeMap::new();

                for t in tokens {
                    if t.contains_char('=') {
                        let kv = t.split('=').collect::<Vec<&str>>();
                        hash.insert(kv.get(0).unwrap().to_string(),
                                    kv.get(1).unwrap().parse::<Json>().unwrap());
                    } else {
                        params.push(t.to_string());
                    }
                }

                Ok(Helper{
                    name: n.to_string(),
                    params: params,
                    hash: hash,
                    template: Option::None,
                    inverse: Option::None,
                    block: block
                })
            },
            None =>
                Err(TemplateError)
        }
    }
}

impl Template {
    pub fn compile(source: String) -> Result<Template, TemplateError> {
        let mut helper_stack: RingBuf<Helper> = RingBuf::new();
        let mut template_stack: RingBuf<Template> = RingBuf::new();
        template_stack.push_front(Template{ elements: Vec::new() });

        let mut buffer: String = String::new();
        let mut state = ParserState::Text;

        let mut c:uint = 0;
        let source_len = source.chars().count();
        while c < source_len {
            let slice = source.slice_chars(c, min(c+3, source_len));

            state = match slice {
                "{{{" | "{{!" | "{{#" | "{{/" => {
                    c += 2;
                    if !buffer.is_empty() {
                        let mut t = template_stack.front_mut().unwrap();
                        t.elements.push(RawString(buffer.clone()));
                        buffer.clear();
                    }
                    match slice {
                        "{{{" => ParserState::HtmlExpression,
                        "{{!" => ParserState::Comment,
                        "{{#" => ParserState::HelperStart,
                        "{{/" => {
                            let t = template_stack.pop_front().unwrap();
                            let h = helper_stack.front_mut().unwrap();
                            if h.template.is_some() {
                                h.inverse = Some(t);
                            } else {
                                h.template = Some(t);
                            }
                            ParserState::HelperEnd
                        },
                        _ => ParserState::Invalid
                    }
                },
                "}}}" => {
                    c += 2;
                    let mut t = template_stack.front_mut().unwrap();
                    t.elements.push(HTMLExpression(
                        buffer.clone().trim_chars(' ').to_string()));
                    buffer.clear();
                    ParserState::Text
                },
                _ => {
                    match if slice.chars().count() > 2 { slice.slice_chars(0, 2) } else { slice } {
                        "{{" => {
                            c += 1;
                            if !buffer.is_empty() {
                                let mut t = template_stack.front_mut().unwrap();
                                t.elements.push(RawString(buffer.clone()));
                                buffer.clear();
                            }
                            ParserState::Expression
                        },
                        "}}" => {
                            c += 1;
                            match state {
                                ParserState::Expression => {
                                    if !buffer.is_empty() {
                                        // {{else}} within a helper block
                                        if buffer.trim_chars(' ') == "else" {
                                            buffer.clear(); // drop else
                                            let t = template_stack.pop_front().unwrap();
                                            let h = helper_stack.front_mut().unwrap();
                                            h.template = Some(t);
                                            template_stack.push_front(Template{ elements: Vec::new() });
                                            ParserState::Text
                                        } else if buffer.contains_char(' ') {
                                            //inline helper
                                            match Helper::parse(buffer.clone(), false){
                                                Ok(helper) => {
                                                    let mut t = template_stack.front_mut().unwrap();
                                                    t.elements.push(HelperExpression(helper));
                                                    buffer.clear();
                                                    ParserState::Text
                                                },
                                                Err(_) => ParserState::Invalid
                                            }
                                        } else {
                                            let mut t = template_stack.front_mut().unwrap();
                                            t.elements.push(Expression(
                                                buffer.clone().trim_chars(' ').to_string()));
                                            buffer.clear();
                                            ParserState::Text
                                        }

                                    } else {
                                        ParserState::Invalid
                                    }
                                },
                                ParserState::Comment => {
                                    let mut t = template_stack.front_mut().unwrap();
                                    t.elements.push(Comment(buffer.clone()));
                                    buffer.clear();
                                    ParserState::Text
                                },
                                ParserState::HelperStart => {
                                    match Helper::parse(buffer.clone(), true) {
                                        Ok(helper) => {
                                            helper_stack.push_front(helper);
                                            template_stack.push_front(Template{ elements: Vec::new() });

                                            buffer.clear();
                                            ParserState::Text
                                        },
                                        Err(_) => ParserState::Invalid
                                    }
                                },
                                ParserState::HelperEnd => {
                                    let name = buffer.trim_chars(' ').to_string();
                                    if name == helper_stack.front().unwrap().name {
                                        let h = helper_stack.pop_front().unwrap();
                                        let mut t = template_stack.front_mut().unwrap();
                                        t.elements.push(HelperBlock(h));
                                        buffer.clear();
                                        ParserState::Text
                                    } else {
                                        ParserState::Invalid
                                    }
                                },
                                _ => ParserState::Invalid
                            }
                        },
                        _ => {
                            buffer.push(source.char_at(c));
                            state
                        }
                    }
                }
            };
            if state == ParserState::Invalid {
                return Err(TemplateError);
            }
            c += 1;
        }

        if !buffer.is_empty() {
            let mut t = template_stack.front_mut().unwrap();
            t.elements.push(TemplateElement::RawString(buffer.clone()));
        }

        if !helper_stack.is_empty() {
            return Err(TemplateError);
        }

        return Ok(template_stack.pop_front().unwrap());
    }
}

impl ToString for Template {
    fn to_string(&self) -> String {
        let mut buf = String::new();
        for v in self.elements.iter() {
            buf.push_str(v.to_string().as_slice());
        }
        buf
    }
}

#[deriving(PartialEq)]
pub enum TemplateElement {
    RawString(String),
    Expression(String),
    HTMLExpression(String),
    HelperExpression(Helper),
    HelperBlock(Helper),
    Comment(String),
}

impl ToString for TemplateElement {
    fn to_string(&self) -> String {
        match *self {
            RawString(ref v) => {
                v.clone()
            },
            Expression(ref v) => {
                // {{ escape to {
                format!("{{{{{}}}}}", v)
            },
            HTMLExpression(ref v) => {
                format!("{{{{{{{}}}}}}}", v)
            },
            HelperExpression(ref helper) => {
                helper.to_string()
            }
            HelperBlock(ref helper) => {
                helper.to_string()
            }
            Comment(ref v) => {
                format!("{{!{}}}", v)
            }
        }
    }
}

#[test]
fn test_parse_helper_start_tag() {
    let source = "if not name compare=1".to_string();
    let h = Helper::parse(source, true).ok().unwrap();

    assert_eq!(h.name, "if".to_string());
    assert_eq!(h.params, vec::<String>!["not".to_string(), "name".to_string()]);

    let key = "compare".to_string();
    let value = h.hash.get(&key).unwrap();
    assert_eq!(value.as_u64().unwrap(), 1u64);
}

#[test]
fn test_parse_template() {
    let source = "
{{title}}
 {{{content}}}
{{#if date}}
good
{{else}}
bad
{{/if}}{{foo bar}}";
    let t = Template::compile(source.to_string()).ok().unwrap();

    assert_eq!(t.elements.len(), 8);
    assert_eq!((*t.elements.get(0).unwrap()).to_string(), "
".to_string());
    assert_eq!((*t.elements.get(1).unwrap()).to_string(),
               Expression("title".to_string()).to_string());

    assert_eq!((*t.elements.get(3).unwrap()).to_string(), "{{{content}}}".to_string());

    match *t.elements.get(5).unwrap() {
        HelperBlock(ref h) => {
            assert_eq!(h.name, "if".to_string());
            assert_eq!(h.params.len(), 1);
            assert_eq!(h.template.as_ref().unwrap().elements.len(), 1);
        },
        _ => {
            panic!("Helper expected here.");
        }
    };

    match *t.elements.get(7).unwrap() {
        HelperExpression(ref h) => {
            assert_eq!(h.name, "foo".to_string());
            assert_eq!(h.params.len(), 1);
            assert_eq!(*(h.params.get(0).unwrap()), "bar".to_string());
        },
        _ => {
            panic!("Helper expression here");
        }
    };
}

#[test]
fn test_helper_to_string() {
    let source = "{{#ifequals name compare=\"hello\"}}hello{{else}}good{{/ifequals}}".to_string();

    let t = Template::compile(source.to_string()).ok().unwrap();

    assert_eq!(t.elements.len(), 1);
    assert_eq!(t.elements.get(0).unwrap().to_string(), source);
}

#[test]
fn test_parse_error() {
    let source = "{{#ifequals name compare=\"hello\"}}hello{{else}}good".to_string();

    let t = Template::compile(source.to_string());

    assert!(t.is_err());
}
<|endoftext|>


<commit_before>/// Asserts that the type implements _all_ of the given traits.
///
/// This is an alias for [`assert_impl_all!`](macro.assert_impl_all.html).
#[deprecated(since = "0.3.4", note = "please use `assert_impl_all!` instead")]
#[macro_export(local_inner_macros)]
macro_rules! assert_impl {
    ($($xs:tt)+) => { _assert_impl_all!($($xs)+); };
}

/// Asserts that the type implements _all_ of the given traits.
///
/// This can be used to ensure types implement auto traits such as [`Send`] and
/// [`Sync`], as well as traits with [blanket `impl`s][blanket].
///
/// # Examples
///
/// On stable Rust, using the macro requires a unique “label” when used in a
/// module scope:
///
#[cfg_attr(feature = "nightly", doc = "```ignore")]
#[cfg_attr(not(feature = "nightly"), doc = "```")]
/// # #[macro_use] extern crate static_assertions;
/// # fn main() {}
/// assert_impl_all!(str; String, Send, Sync, From<&'static str>);
/// assert_impl_all!(vec; &'static [u8], Into<Vec<u8>>);
/// ```
///
/// The [labeling limitation](index.html#limitations) is not necessary if
/// compiling on nightly Rust with the `nightly` feature enabled:
///
#[cfg_attr(feature = "nightly", doc = "```")]
#[cfg_attr(not(feature = "nightly"), doc = "```ignore")]
/// #![feature(underscore_const_names)]
/// # #[macro_use] extern crate static_assertions;
///
/// assert_impl_all!(u32, Copy, Send);
///
/// fn main() {
///     assert_impl_all!(&str, Into<String>);
/// }
/// ```
///
/// Raw pointers cannot be sent between threads safely:
///
/// ```compile_fail
/// # #[macro_use] extern crate static_assertions;
/// # fn main() {
/// assert_impl_all!(*const u8, Send);
/// # }
/// ```
///
/// [`Send`]: https://doc.rust-lang.org/std/marker/trait.Send.html
/// [`Sync`]: https://doc.rust-lang.org/std/marker/trait.Sync.html
/// [blanket]: https://doc.rust-lang.org/book/second-edition/ch10-02-traits.html#using-trait-bounds-to-conditionally-implement-methods
#[macro_export(local_inner_macros)]
macro_rules! assert_impl_all {
    ($($xs:tt)+) => { _assert_impl_all!($($xs)+); };
}

#[doc(hidden)]
#[cfg(feature = "nightly")]
#[macro_export(local_inner_macros)]
macro_rules! _assert_impl_all {
    ($x:ty, $($t:path),+ $(,)*) => {
        const _: fn() -> () = || {
            fn assert_impl_all<T>() where T: ?Sized $(+ $t)+ {}
            assert_impl_all::<$x>();
        };
    };
}

#[doc(hidden)]
#[cfg(not(feature = "nightly"))]
#[macro_export(local_inner_macros)]
macro_rules! _assert_impl_all {
    ($x:ty, $($t:path),+ $(,)*) => {
        {
            fn assert_impl_all<T>() where T: ?Sized $(+ $t)+ {}
            assert_impl_all::<$x>();
        }
    };
    ($label:ident; $($xs:tt)+) => {
        #[allow(dead_code, non_snake_case)]
        fn $label() { assert_impl_all!($($xs)+); }
    };
}
<commit_msg>Remove `assert_impl!` macro<commit_after>/// Asserts that the type implements _all_ of the given traits.
///
/// This can be used to ensure types implement auto traits such as [`Send`] and
/// [`Sync`], as well as traits with [blanket `impl`s][blanket].
///
/// # Examples
///
/// On stable Rust, using the macro requires a unique “label” when used in a
/// module scope:
///
#[cfg_attr(feature = "nightly", doc = "```ignore")]
#[cfg_attr(not(feature = "nightly"), doc = "```")]
/// # #[macro_use] extern crate static_assertions;
/// # fn main() {}
/// assert_impl_all!(str; String, Send, Sync, From<&'static str>);
/// assert_impl_all!(vec; &'static [u8], Into<Vec<u8>>);
/// ```
///
/// The [labeling limitation](index.html#limitations) is not necessary if
/// compiling on nightly Rust with the `nightly` feature enabled:
///
#[cfg_attr(feature = "nightly", doc = "```")]
#[cfg_attr(not(feature = "nightly"), doc = "```ignore")]
/// #![feature(underscore_const_names)]
/// # #[macro_use] extern crate static_assertions;
///
/// assert_impl_all!(u32, Copy, Send);
///
/// fn main() {
///     assert_impl_all!(&str, Into<String>);
/// }
/// ```
///
/// Raw pointers cannot be sent between threads safely:
///
/// ```compile_fail
/// # #[macro_use] extern crate static_assertions;
/// # fn main() {
/// assert_impl_all!(*const u8, Send);
/// # }
/// ```
///
/// [`Send`]: https://doc.rust-lang.org/std/marker/trait.Send.html
/// [`Sync`]: https://doc.rust-lang.org/std/marker/trait.Sync.html
/// [blanket]: https://doc.rust-lang.org/book/second-edition/ch10-02-traits.html#using-trait-bounds-to-conditionally-implement-methods
#[macro_export(local_inner_macros)]
macro_rules! assert_impl_all {
    ($($xs:tt)+) => { _assert_impl_all!($($xs)+); };
}

#[doc(hidden)]
#[cfg(feature = "nightly")]
#[macro_export(local_inner_macros)]
macro_rules! _assert_impl_all {
    ($x:ty, $($t:path),+ $(,)*) => {
        const _: fn() -> () = || {
            fn assert_impl_all<T>() where T: ?Sized $(+ $t)+ {}
            assert_impl_all::<$x>();
        };
    };
}

#[doc(hidden)]
#[cfg(not(feature = "nightly"))]
#[macro_export(local_inner_macros)]
macro_rules! _assert_impl_all {
    ($x:ty, $($t:path),+ $(,)*) => {
        {
            fn assert_impl_all<T>() where T: ?Sized $(+ $t)+ {}
            assert_impl_all::<$x>();
        }
    };
    ($label:ident; $($xs:tt)+) => {
        #[allow(dead_code, non_snake_case)]
        fn $label() { assert_impl_all!($($xs)+); }
    };
}
<|endoftext|>


<commit_before>extern crate hlua;

#[test]
fn basic() {
    let mut lua = hlua::Lua::new();

    let mut f = hlua::LuaFunction::load(&mut lua, "return 5;").unwrap();

    let val: i32 = f.call().unwrap();
    assert_eq!(val, 5);
}

#[test]
fn syntax_error() {
    let mut lua = hlua::Lua::new();

    assert!(hlua::LuaFunction::load(&mut lua, "azerazer").is_err());
}

#[test]
fn execution_error() {
    let mut lua = hlua::Lua::new();

    let mut f = hlua::LuaFunction::load(&mut lua, "return a:hello()").unwrap();

    let val: Result<i32, hlua::LuaError> = f.call();
    assert!(val.is_err());
}

#[test]
fn call_and_read_table() {
    let mut lua = hlua::Lua::new();

    let mut f = hlua::LuaFunction::load(&mut lua, "return {1, 2, 3};").unwrap();

    let val: hlua::LuaTable<_> = f.call().unwrap();

}
<commit_msg>Fix test<commit_after>extern crate hlua;

#[test]
fn basic() {
    let mut lua = hlua::Lua::new();

    let mut f = hlua::LuaFunction::load(&mut lua, "return 5;").unwrap();

    let val: i32 = f.call().unwrap();
    assert_eq!(val, 5);
}

#[test]
fn syntax_error() {
    let mut lua = hlua::Lua::new();

    assert!(hlua::LuaFunction::load(&mut lua, "azerazer").is_err());
}

#[test]
fn execution_error() {
    let mut lua = hlua::Lua::new();

    let mut f = hlua::LuaFunction::load(&mut lua, "return a:hello()").unwrap();

    let val: Result<i32, hlua::LuaError> = f.call();
    assert!(val.is_err());
}

#[test]
fn call_and_read_table() {
    let mut lua = hlua::Lua::new();

    let mut f = hlua::LuaFunction::load(&mut lua, "return {1, 2, 3};").unwrap();

    let mut val: hlua::LuaTable<_> = f.call().unwrap();
    assert_eq!(val.get::<u8, _>(2).unwrap(), 2);
}
<|endoftext|>


<commit_before>/**********************************************************\
|                                                          |
|                          hprose                          |
|                                                          |
| Official WebSite: http://www.hprose.com/                 |
|                   http://www.hprose.org/                 |
|                                                          |
\**********************************************************/
/**********************************************************\
 *                                                        *
 * io/byte_reader.rs                                      *
 *                                                        *
 * byte reader for Rust.                                  *
 *                                                        *
 * LastModified: Sep 25, 2016                             *
 * Author: Chen Fei <cf@hprose.com>                       *
 *                                                        *
\**********************************************************/

use super::tags::*;
use super::util::utf8_slice_to_str;

use self::ParserError::*;

use std::{io, num, f64};

#[derive(Clone, PartialEq, Debug)]
pub enum ParserError {
    BadUTF8Encode,
    ParseBoolError,
    ParseIntError(num::ParseIntError),
    ParseFloatError(num::ParseFloatError),
    IoError(io::ErrorKind),
}

pub struct ByteReader<'a> {
    pub buf: &'a [u8],
    pub off: usize
}

impl<'a> ByteReader<'a> {
    #[inline]
    pub fn new(buf: &'a [u8]) -> ByteReader<'a> {
        ByteReader {
            buf: buf,
            off: 0
        }
    }

    pub fn next(&mut self, count: usize) -> Result<&[u8], ParserError> {
        let p = self.off + count;
        if p <= self.buf.len() {
            let b = &self.buf[self.off..p];
            self.off = p;
            Ok(b)
        } else {
            Err(IoError(io::ErrorKind::UnexpectedEof))
        }
    }

    pub fn read_byte(&mut self) -> Result<u8, ParserError> {
        if self.off < self.buf.len() {
            let b = self.buf[self.off];
            self.off += 1;
            Ok(b)
        } else {
            Err(IoError(io::ErrorKind::UnexpectedEof))
        }
    }

    pub fn read_i64_with_tag(&mut self, tag: u8) -> Result<i64, ParserError> {
        let mut i: i64 = 0;
        self.read_byte().and_then(|b| {
            if b == tag {
                Ok(i)
            } else {
                let mut neg = false;
                let next = match b {
                    TAG_NEG => {
                        neg = true;
                        self.read_byte()
                    },
                    TAG_POS => self.read_byte(),
                    _ => Ok(b)
                };
                if neg {
                    next.and_then(|mut b| {
                        while b != tag {
                            i = i * 10 - (b as i64 - b'0' as i64);
                            b = match self.read_byte() {
                                Ok(b) => b,
                                Err(e) => return Err(e)
                            }
                        }
                        Ok(i)
                    })
                } else {
                    next.and_then(|mut b| {
                        while b != tag {
                            i = i * 10 + (b as i64 - b'0' as i64);
                            b = match self.read_byte() {
                                Ok(b) => b,
                                Err(e) => return Err(e)
                            }
                        }
                        Ok(i)
                    })
                }
            }
        })
    }

    #[inline]
    pub fn read_u64_with_tag(&mut self, tag: u8) -> Result<u64, ParserError> {
        self.read_i64_with_tag(tag).map(|i| i as u64)
    }

    #[inline]
    pub fn read_i64(&mut self) -> Result<i64, ParserError> {
        self.read_i64_with_tag(TAG_SEMICOLON)
    }

    #[inline]
    pub fn read_u64(&mut self) -> Result<u64, ParserError> {
        self.read_u64_with_tag(TAG_SEMICOLON)
    }

    #[inline]
    fn read_length(&mut self) -> Result<usize, ParserError> {
        self.read_i64_with_tag(TAG_QUOTE).map(|i| i as usize)
    }

    #[inline]
    pub fn read_count(&mut self) -> Result<usize, ParserError> {
        self.read_i64_with_tag(TAG_OPENBRACE).map(|i| i as usize)
    }

    pub fn read_until(&mut self, tag: u8) -> Result<&[u8], ParserError> {
        let result = &self.buf[self.off..];
        match result.iter().position(|x| *x == tag) {
            Some(idx) => {
                self.off += idx + 1;
                Ok(&result[..idx])
            },
            None => {
                self.off = self.buf.len();
                Ok(result)
            }
        }
    }

    pub fn read_f32(&mut self) -> Result<f32, ParserError> {
        self.read_until(TAG_SEMICOLON)
            .and_then(|v| utf8_slice_to_str(v).parse::<f32>().map_err(|e| ParseFloatError(e)))
    }

    pub fn read_f64(&mut self) -> Result<f64, ParserError> {
        self.read_until(TAG_SEMICOLON)
            .and_then(|v| utf8_slice_to_str(v).parse::<f64>().map_err(|e| ParseFloatError(e)))
    }

    pub fn read_utf8_slice(&mut self, length: usize) -> Result<&[u8], ParserError> {
        if length == 0 {
            return Ok(&[])
        }
        let p = self.off;
        let mut i: usize = 0;
        while i < length {
            let b = self.buf[self.off];
            match b >> 4 {
                0...7 => self.off += 1,
                12 | 13 => self.off += 2,
                14 => self.off += 3,
                15 => {
                    if b & 8 == 8 {
                        return Err(BadUTF8Encode)
                    }
                    self.off += 4;
                    i += 1
                },
                _ => return Err(BadUTF8Encode)
            }
            i += 1;
        }
        Ok(&self.buf[p..self.off])
    }

    pub fn read_utf8_string(&mut self, length: usize) -> Result<String, ParserError> {
        self.read_utf8_slice(length).map(|s| unsafe { String::from_utf8_unchecked(s.to_owned()) })
    }

    pub fn read_string(&mut self) -> Result<String, ParserError> {
        self.read_length()
            .and_then(|len| self.read_utf8_string(len))
            .and_then(|s| self.read_byte().map(|_| s))
    }

    pub fn read_inf(&mut self) -> Result<f64, ParserError> {
        self.read_byte().map(|sign| if sign == TAG_POS { f64::INFINITY } else { f64::NEG_INFINITY })
    }
}
<commit_msg>Use wrapping_mul wrapping_sub wrapping_add instead of * - + while read i64.<commit_after>/**********************************************************\
|                                                          |
|                          hprose                          |
|                                                          |
| Official WebSite: http://www.hprose.com/                 |
|                   http://www.hprose.org/                 |
|                                                          |
\**********************************************************/
/**********************************************************\
 *                                                        *
 * io/byte_reader.rs                                      *
 *                                                        *
 * byte reader for Rust.                                  *
 *                                                        *
 * LastModified: Sep 25, 2016                             *
 * Author: Chen Fei <cf@hprose.com>                       *
 *                                                        *
\**********************************************************/

use super::tags::*;
use super::util::utf8_slice_to_str;

use self::ParserError::*;

use std::{io, num, f64};

#[derive(Clone, PartialEq, Debug)]
pub enum ParserError {
    BadUTF8Encode,
    ParseBoolError,
    ParseIntError(num::ParseIntError),
    ParseFloatError(num::ParseFloatError),
    IoError(io::ErrorKind),
}

pub struct ByteReader<'a> {
    pub buf: &'a [u8],
    pub off: usize
}

impl<'a> ByteReader<'a> {
    #[inline]
    pub fn new(buf: &'a [u8]) -> ByteReader<'a> {
        ByteReader {
            buf: buf,
            off: 0
        }
    }

    pub fn next(&mut self, count: usize) -> Result<&[u8], ParserError> {
        let p = self.off + count;
        if p <= self.buf.len() {
            let b = &self.buf[self.off..p];
            self.off = p;
            Ok(b)
        } else {
            Err(IoError(io::ErrorKind::UnexpectedEof))
        }
    }

    pub fn read_byte(&mut self) -> Result<u8, ParserError> {
        if self.off < self.buf.len() {
            let b = self.buf[self.off];
            self.off += 1;
            Ok(b)
        } else {
            Err(IoError(io::ErrorKind::UnexpectedEof))
        }
    }

    pub fn read_i64_with_tag(&mut self, tag: u8) -> Result<i64, ParserError> {
        let mut i: i64 = 0;
        self.read_byte().and_then(|b| {
            if b == tag {
                Ok(i)
            } else {
                let mut neg = false;
                let next = match b {
                    TAG_NEG => {
                        neg = true;
                        self.read_byte()
                    },
                    TAG_POS => self.read_byte(),
                    _ => Ok(b)
                };
                if neg {
                    next.and_then(|mut b| {
                        while b != tag {
                            i = i.wrapping_mul(10).wrapping_sub(b as i64 - b'0' as i64);
                            b = match self.read_byte() {
                                Ok(b) => b,
                                Err(e) => return Err(e)
                            }
                        }
                        Ok(i)
                    })
                } else {
                    next.and_then(|mut b| {
                        while b != tag {
                            i = i.wrapping_mul(10).wrapping_add(b as i64 - b'0' as i64);
                            b = match self.read_byte() {
                                Ok(b) => b,
                                Err(e) => return Err(e)
                            }
                        }
                        Ok(i)
                    })
                }
            }
        })
    }

    #[inline]
    pub fn read_u64_with_tag(&mut self, tag: u8) -> Result<u64, ParserError> {
        self.read_i64_with_tag(tag).map(|i| i as u64)
    }

    #[inline]
    pub fn read_i64(&mut self) -> Result<i64, ParserError> {
        self.read_i64_with_tag(TAG_SEMICOLON)
    }

    #[inline]
    pub fn read_u64(&mut self) -> Result<u64, ParserError> {
        self.read_u64_with_tag(TAG_SEMICOLON)
    }

    #[inline]
    fn read_length(&mut self) -> Result<usize, ParserError> {
        self.read_i64_with_tag(TAG_QUOTE).map(|i| i as usize)
    }

    #[inline]
    pub fn read_count(&mut self) -> Result<usize, ParserError> {
        self.read_i64_with_tag(TAG_OPENBRACE).map(|i| i as usize)
    }

    pub fn read_until(&mut self, tag: u8) -> Result<&[u8], ParserError> {
        let result = &self.buf[self.off..];
        match result.iter().position(|x| *x == tag) {
            Some(idx) => {
                self.off += idx + 1;
                Ok(&result[..idx])
            },
            None => {
                self.off = self.buf.len();
                Ok(result)
            }
        }
    }

    pub fn read_f32(&mut self) -> Result<f32, ParserError> {
        self.read_until(TAG_SEMICOLON)
            .and_then(|v| utf8_slice_to_str(v).parse::<f32>().map_err(|e| ParseFloatError(e)))
    }

    pub fn read_f64(&mut self) -> Result<f64, ParserError> {
        self.read_until(TAG_SEMICOLON)
            .and_then(|v| utf8_slice_to_str(v).parse::<f64>().map_err(|e| ParseFloatError(e)))
    }

    pub fn read_utf8_slice(&mut self, length: usize) -> Result<&[u8], ParserError> {
        if length == 0 {
            return Ok(&[])
        }
        let p = self.off;
        let mut i: usize = 0;
        while i < length {
            let b = self.buf[self.off];
            match b >> 4 {
                0...7 => self.off += 1,
                12 | 13 => self.off += 2,
                14 => self.off += 3,
                15 => {
                    if b & 8 == 8 {
                        return Err(BadUTF8Encode)
                    }
                    self.off += 4;
                    i += 1
                },
                _ => return Err(BadUTF8Encode)
            }
            i += 1;
        }
        Ok(&self.buf[p..self.off])
    }

    pub fn read_utf8_string(&mut self, length: usize) -> Result<String, ParserError> {
        self.read_utf8_slice(length).map(|s| unsafe { String::from_utf8_unchecked(s.to_owned()) })
    }

    pub fn read_string(&mut self) -> Result<String, ParserError> {
        self.read_length()
            .and_then(|len| self.read_utf8_string(len))
            .and_then(|s| self.read_byte().map(|_| s))
    }

    pub fn read_inf(&mut self) -> Result<f64, ParserError> {
        self.read_byte().map(|sign| if sign == TAG_POS { f64::INFINITY } else { f64::NEG_INFINITY })
    }
}
<|endoftext|>


<commit_before>use crate::{
    channel::Channel,
    consumer::Consumer,
    exchange::ExchangeKind,
    options::{BasicConsumeOptions, ExchangeDeclareOptions, QueueDeclareOptions},
    queue::Queue,
    types::{FieldTable, ShortString},
};
use serde::{Deserialize, Serialize};
use std::ops::Deref;

#[derive(Clone, Debug, Default, Deserialize, Serialize)]
pub struct TopologyDefinition {
    #[serde(default)]
    pub exchanges: Vec<ExchangeDefinition>,
    #[serde(default)]
    pub queues: Vec<QueueDefinition>,
    #[serde(default)]
    pub channels: Vec<ChannelDefinition>,
}

#[derive(Clone, Debug, Default, Deserialize, Serialize)]
pub struct ExchangeDefinition {
    pub name: ShortString,
    pub kind: Option<ExchangeKind>,
    pub options: Option<ExchangeDeclareOptions>,
    pub arguments: Option<FieldTable>,
    #[serde(default)]
    pub bindings: Vec<BindingDefinition>,
}

#[derive(Clone, Debug, Default, Deserialize, Serialize)]
pub struct QueueDefinition {
    pub name: ShortString,
    pub options: Option<QueueDeclareOptions>,
    pub arguments: Option<FieldTable>,
    #[serde(default)]
    pub bindings: Vec<BindingDefinition>,
}

impl QueueDefinition {
    pub(crate) fn is_exclusive(&self) -> bool {
        self.options.map_or(false, |o| o.exclusive)
    }
}

#[derive(Clone, Debug, Default, Deserialize, Serialize)]
pub struct BindingDefinition {
    pub source: ShortString,
    pub routing_key: ShortString,
    #[serde(default)]
    pub arguments: FieldTable,
}

#[derive(Clone, Debug, Default, Deserialize, Serialize)]
pub struct ChannelDefinition {
    #[serde(default)]
    pub queues: Vec<QueueDefinition>,
    pub consumers: Vec<ConsumerDefinition>,
}

#[derive(Clone, Debug, Default, Deserialize, Serialize)]
pub struct ConsumerDefinition {
    pub queue: ShortString,
    #[serde(default)]
    pub tag: ShortString,
    #[serde(default)]
    pub options: BasicConsumeOptions,
    #[serde(default)]
    pub arguments: FieldTable,
}

#[derive(Default)]
pub struct RestoredTopology {
    pub(crate) queues: Vec<Queue>,
    pub(crate) channels: Vec<RestoredChannel>,
}

impl RestoredTopology {
    pub fn queue(&self, index: usize) -> Queue {
        self.queues[index].clone()
    }

    pub fn channel(&self, index: usize) -> RestoredChannel {
        self.channels[index].clone()
    }
}

#[derive(Clone)]
pub struct RestoredChannel {
    pub(crate) channel: Channel,
    pub(crate) queues: Vec<Queue>,
    pub(crate) consumers: Vec<Consumer>,
}

impl Deref for RestoredChannel {
    type Target = Channel;

    fn deref(&self) -> &Self::Target {
        &self.channel
    }
}

impl RestoredChannel {
    pub(crate) fn new(channel: Channel) -> Self {
        Self {
            channel,
            queues: Vec::new(),
            consumers: Vec::new(),
        }
    }

    pub fn into_inner(self) -> Channel {
        self.channel
    }

    pub fn queue(&self, index: usize) -> Queue {
        self.queues[index].clone()
    }

    pub fn consumer(&self, index: usize) -> Consumer {
        self.consumers[index].clone()
    }
}
<commit_msg>some comments<commit_after>use crate::{
    channel::Channel,
    consumer::Consumer,
    exchange::ExchangeKind,
    options::{BasicConsumeOptions, ExchangeDeclareOptions, QueueDeclareOptions},
    queue::Queue,
    types::{FieldTable, ShortString},
};
use serde::{Deserialize, Serialize};
use std::ops::Deref;

/// The current topology definition
///
/// This contains the list of exhanges, queues, bindings, channels and consumers
/// declared on the current Connection.
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
pub struct TopologyDefinition {
    /// The exchanges declared in this topology.
    #[serde(default)]
    pub exchanges: Vec<ExchangeDefinition>,
    /// The "global" (not exclusive) declared in this topology.
    #[serde(default)]
    pub queues: Vec<QueueDefinition>,
    /// The channels declares in this topology
    #[serde(default)]
    pub channels: Vec<ChannelDefinition>,
}

#[derive(Clone, Debug, Default, Deserialize, Serialize)]
pub struct ExchangeDefinition {
    pub name: ShortString,
    pub kind: Option<ExchangeKind>,
    pub options: Option<ExchangeDeclareOptions>,
    pub arguments: Option<FieldTable>,
    #[serde(default)]
    pub bindings: Vec<BindingDefinition>,
}

#[derive(Clone, Debug, Default, Deserialize, Serialize)]
pub struct QueueDefinition {
    pub name: ShortString,
    pub options: Option<QueueDeclareOptions>,
    pub arguments: Option<FieldTable>,
    #[serde(default)]
    pub bindings: Vec<BindingDefinition>,
}

impl QueueDefinition {
    pub(crate) fn is_exclusive(&self) -> bool {
        self.options.map_or(false, |o| o.exclusive)
    }
}

#[derive(Clone, Debug, Default, Deserialize, Serialize)]
pub struct BindingDefinition {
    pub source: ShortString,
    pub routing_key: ShortString,
    #[serde(default)]
    pub arguments: FieldTable,
}

#[derive(Clone, Debug, Default, Deserialize, Serialize)]
pub struct ChannelDefinition {
    /// Exclusive queues need to be declared in a Channel.
    /// This is the list of exclusive queues for this one.
    #[serde(default)]
    pub queues: Vec<QueueDefinition>,
    pub consumers: Vec<ConsumerDefinition>,
}

#[derive(Clone, Debug, Default, Deserialize, Serialize)]
pub struct ConsumerDefinition {
    pub queue: ShortString,
    #[serde(default)]
    pub tag: ShortString,
    #[serde(default)]
    pub options: BasicConsumeOptions,
    #[serde(default)]
    pub arguments: FieldTable,
}

#[derive(Default)]
pub struct RestoredTopology {
    pub(crate) queues: Vec<Queue>,
    pub(crate) channels: Vec<RestoredChannel>,
}

impl RestoredTopology {
    pub fn queue(&self, index: usize) -> Queue {
        self.queues[index].clone()
    }

    pub fn channel(&self, index: usize) -> RestoredChannel {
        self.channels[index].clone()
    }
}

#[derive(Clone)]
pub struct RestoredChannel {
    pub(crate) channel: Channel,
    pub(crate) queues: Vec<Queue>,
    pub(crate) consumers: Vec<Consumer>,
}

impl Deref for RestoredChannel {
    type Target = Channel;

    fn deref(&self) -> &Self::Target {
        &self.channel
    }
}

impl RestoredChannel {
    pub(crate) fn new(channel: Channel) -> Self {
        Self {
            channel,
            queues: Vec::new(),
            consumers: Vec::new(),
        }
    }

    pub fn into_inner(self) -> Channel {
        self.channel
    }

    pub fn queue(&self, index: usize) -> Queue {
        self.queues[index].clone()
    }

    pub fn consumer(&self, index: usize) -> Consumer {
        self.consumers[index].clone()
    }
}
<|endoftext|>