<commit_before>//! Error types.

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

use cairo;
use cssparser::{BasicParseError, BasicParseErrorKind};
use glib;
use markup5ever::QualName;

use crate::allowed_url::Fragment;
use crate::node::RsvgNode;

/// A simple error which refers to an attribute's value
#[derive(Debug, Clone, PartialEq)]
pub enum ValueErrorKind {
    /// A property with the specified name was not found
    UnknownProperty,

    /// The value could not be parsed
    Parse(String),

    // The value could be parsed, but is invalid
    Value(String),
}

impl ValueErrorKind {
    pub fn parse_error(s: &str) -> ValueErrorKind {
        ValueErrorKind::Parse(s.to_string())
    }

    pub fn value_error(s: &str) -> ValueErrorKind {
        ValueErrorKind::Value(s.to_string())
    }
}

impl fmt::Display for ValueErrorKind {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match *self {
            ValueErrorKind::UnknownProperty => write!(f, "unknown property name"),

            ValueErrorKind::Parse(ref s) => write!(
                f,
                "parse error: {}",
                s
            ),

            ValueErrorKind::Value(ref s) => write!(
                f,
                "invalid value: {}",
                s
            ),
        }
    }
}

/// A complete error for an attribute and its erroneous value
#[derive(Debug, Clone, PartialEq)]
pub struct NodeError {
    pub attr: QualName,
    pub err: ValueErrorKind,
}

impl NodeError {
    pub fn new(attr: QualName, error: ValueErrorKind) -> NodeError {
        NodeError { attr, err: error }
    }

    pub fn parse_error(attr: QualName, error: &str) -> NodeError {
        NodeError {
            attr,
            err: ValueErrorKind::Parse(error.to_string()),
        }
    }

    pub fn value_error(attr: QualName, description: &str) -> NodeError {
        NodeError {
            attr,
            err: ValueErrorKind::Value(description.to_string()),
        }
    }
}

impl error::Error for NodeError {
    fn description(&self) -> &str {
        match self.err {
            ValueErrorKind::UnknownProperty => "unknown property",
            ValueErrorKind::Parse(_) => "parse error",
            ValueErrorKind::Value(_) => "invalid attribute value",
        }
    }
}

impl fmt::Display for NodeError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{:?}: {}", self.attr.expanded(), self.err)
    }
}

impl<'a> From<BasicParseError<'a>> for ValueErrorKind {
    fn from(e: BasicParseError<'_>) -> ValueErrorKind {
        let BasicParseError { kind, location: _ } =  e;

        let msg = match kind {
            BasicParseErrorKind::UnexpectedToken(_) => "unexpected token",
            BasicParseErrorKind::EndOfInput => "unexpected end of input",
            BasicParseErrorKind::AtRuleInvalid(_) => "invalid @-rule",
            BasicParseErrorKind::AtRuleBodyInvalid => "invalid @-rule body",
            BasicParseErrorKind::QualifiedRuleInvalid => "invalid qualified rule",
        };

        ValueErrorKind::parse_error(msg)
    }
}

#[derive(Debug, Clone, PartialEq)]
pub enum DefsLookupErrorKind {
    HrefError(HrefError),
    CannotLookupExternalReferences,
    NotFound,
}

#[derive(Debug, Clone, PartialEq)]
pub enum RenderingError {
    Cairo(cairo::Status),
    CircularReference,
    InstancingLimit,
    InvalidId(DefsLookupErrorKind),
    InvalidHref,
    OutOfMemory,
    HandleIsNotLoaded,
}

impl From<cairo::Status> for RenderingError {
    fn from(e: cairo::Status) -> RenderingError {
        assert!(e != cairo::Status::Success);

        RenderingError::Cairo(e)
    }
}

pub enum AcquireError {
    LinkNotFound(Fragment),
    InvalidLinkType(Fragment),
    CircularReference(RsvgNode),
    MaxReferencesExceeded,
}

impl fmt::Display for AcquireError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match *self {
            AcquireError::LinkNotFound(ref frag) =>
                write!(f, "link not found: {}", frag),

            AcquireError::InvalidLinkType(ref frag) =>
                write!(f, "link {} is to object of invalid type", frag),

            AcquireError::CircularReference(ref node) =>
                write!(f, "circular reference in node {}", node),

            AcquireError::MaxReferencesExceeded =>
                write!(f, "maximum number of references exceeded"),
        }
    }
}

/// Helper for converting `Result<O, E>` into `Result<O, NodeError>`
///
/// A `NodeError` requires a `QualName` that corresponds to the attribute to which the
/// error refers, plus the actual `ValueErrorKind` that describes the error.  However,
/// parsing functions for attribute value types will want to return their own kind of
/// error, instead of `ValueErrorKind`.  If that particular error type has an `impl
/// From<FooError> for ValueErrorKind`, then this trait helps assign attribute values in
/// `set_atts()` methods as follows:
///
/// ```ignore
/// use error::AttributeResultExt;
///
/// // fn parse_foo(...) -> Result<Foo, FooError>
///
/// // It is assumed that there is an impl From<FooError> for ValueErrorKind
///
/// self.foo = parse_foo(value).attribute(local_name!("foo"))?;
/// ```
///
/// The call to `.attribute(attr)` converts the `Result` from `parse_foo()` into a full
/// `NodeError` with the provided `attr`.
pub trait AttributeResultExt<O, E> {
    fn attribute(self, attr: QualName) -> Result<O, NodeError>;
}

impl<O, E: Into<ValueErrorKind>> AttributeResultExt<O, E> for Result<O, E> {
    fn attribute(self, attr: QualName) -> Result<O, NodeError> {
        self.map_err(|e| e.into())
            .map_err(|e| NodeError::new(attr, e))
    }
}

/// Errors returned when creating an `Href` out of a string
#[derive(Debug, Clone, PartialEq)]
pub enum HrefError {
    /// The href is an invalid URI or has empty components.
    ParseError,

    /// A fragment identifier ("`#foo`") is not allowed here
    ///
    /// For example, the SVG `<image>` element only allows referencing
    /// resources without fragment identifiers like
    /// `xlink:href="foo.png"`.
    FragmentForbidden,

    /// A fragment identifier ("`#foo`") was required but not found.  For example,
    /// the SVG `<use>` element requires one, as in `<use xlink:href="foo.svg#bar">`.
    FragmentRequired,
}

impl From<HrefError> for ValueErrorKind {
    fn from(e: HrefError) -> ValueErrorKind {
        match e {
            HrefError::ParseError => ValueErrorKind::parse_error("url parse error"),
            HrefError::FragmentForbidden => {
                ValueErrorKind::value_error("fragment identifier not allowed")
            }
            HrefError::FragmentRequired => {
                ValueErrorKind::value_error("fragment identifier required")
            }
        }
    }
}

#[derive(Debug, Clone)]
pub enum LoadingError {
    NoDataPassedToParser,
    XmlParseError(String),
    // Could not parse data: URL
    CouldNotCreateXmlParser,
    BadUrl,
    BadDataUrl,
    BadStylesheet,
    BadCss,
    Cairo(cairo::Status),
    EmptyData,
    SvgHasNoElements,
    RootElementIsNotSvg,
    Glib(glib::Error),
    Unknown,
}

impl error::Error for LoadingError {
    fn description(&self) -> &str {
        match *self {
            LoadingError::NoDataPassedToParser => "no data passed to parser",
            LoadingError::CouldNotCreateXmlParser => "could not create XML parser",
            LoadingError::XmlParseError(_) => "XML parse error",
            LoadingError::BadUrl => "invalid URL",
            LoadingError::BadDataUrl => "invalid data: URL",
            LoadingError::BadStylesheet => "invalid stylesheet",
            LoadingError::BadCss => "invalid CSS",
            LoadingError::Cairo(_) => "cairo error",
            LoadingError::EmptyData => "empty data",
            LoadingError::SvgHasNoElements => "SVG has no elements",
            LoadingError::RootElementIsNotSvg => "root element is not <svg>",
            LoadingError::Glib(ref e) => e.description(),
            LoadingError::Unknown => "unknown error",
        }
    }
}

impl fmt::Display for LoadingError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match *self {
            LoadingError::Cairo(status) => write!(f, "cairo error: {:?}", status),
            LoadingError::XmlParseError(ref s) => write!(f, "XML parse error: {}", s),
            LoadingError::NoDataPassedToParser
            | LoadingError::CouldNotCreateXmlParser
            | LoadingError::BadUrl
            | LoadingError::BadDataUrl
            | LoadingError::BadStylesheet
            | LoadingError::BadCss
            | LoadingError::EmptyData
            | LoadingError::SvgHasNoElements
            | LoadingError::RootElementIsNotSvg
            | LoadingError::Glib(_)
            | LoadingError::Unknown => write!(f, "{}", self.description()),
        }
    }
}

impl error::Error for RenderingError {
    fn description(&self) -> &str {
        match *self {
            RenderingError::Cairo(_) => "cairo error",
            RenderingError::CircularReference => "circular reference",
            RenderingError::InstancingLimit => "instancing limit",
            RenderingError::InvalidId(_) => "invalid id",
            RenderingError::InvalidHref => "invalid href",
            RenderingError::OutOfMemory => "out of memory",
            RenderingError::HandleIsNotLoaded => "SVG data is not loaded into handle",
        }
    }
}

impl fmt::Display for RenderingError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match *self {
            RenderingError::Cairo(ref status) => write!(f, "cairo error: {:?}", status),
            RenderingError::InvalidId(ref id) => write!(f, "invalid id: {:?}", id),
            RenderingError::CircularReference
            | RenderingError::InstancingLimit
            | RenderingError::InvalidHref
            | RenderingError::OutOfMemory
            | RenderingError::HandleIsNotLoaded => write!(f, "{}", self.description()),
        }
    }
}

impl From<cairo::Status> for LoadingError {
    fn from(e: cairo::Status) -> LoadingError {
        assert!(e != cairo::Status::Success);

        LoadingError::Cairo(e)
    }
}

impl From<glib::Error> for LoadingError {
    fn from(e: glib::Error) -> LoadingError {
        LoadingError::Glib(e)
    }
}

#[cfg(test)]
pub fn is_parse_error<T>(r: &Result<T, ValueErrorKind>) -> bool {
    match *r {
        Err(ValueErrorKind::Parse(_)) => true,
        _ => false,
    }
}

#[cfg(test)]
pub fn is_value_error<T>(r: &Result<T, ValueErrorKind>) -> bool {
    match *r {
        Err(ValueErrorKind::Value(_)) => true,
        _ => false,
    }
}
<commit_msg>NodeError: remove unused method value_error()<commit_after>//! Error types.

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

use cairo;
use cssparser::{BasicParseError, BasicParseErrorKind};
use glib;
use markup5ever::QualName;

use crate::allowed_url::Fragment;
use crate::node::RsvgNode;

/// A simple error which refers to an attribute's value
#[derive(Debug, Clone, PartialEq)]
pub enum ValueErrorKind {
    /// A property with the specified name was not found
    UnknownProperty,

    /// The value could not be parsed
    Parse(String),

    // The value could be parsed, but is invalid
    Value(String),
}

impl ValueErrorKind {
    pub fn parse_error(s: &str) -> ValueErrorKind {
        ValueErrorKind::Parse(s.to_string())
    }

    pub fn value_error(s: &str) -> ValueErrorKind {
        ValueErrorKind::Value(s.to_string())
    }
}

impl fmt::Display for ValueErrorKind {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match *self {
            ValueErrorKind::UnknownProperty => write!(f, "unknown property name"),

            ValueErrorKind::Parse(ref s) => write!(
                f,
                "parse error: {}",
                s
            ),

            ValueErrorKind::Value(ref s) => write!(
                f,
                "invalid value: {}",
                s
            ),
        }
    }
}

/// A complete error for an attribute and its erroneous value
#[derive(Debug, Clone, PartialEq)]
pub struct NodeError {
    pub attr: QualName,
    pub err: ValueErrorKind,
}

impl NodeError {
    pub fn new(attr: QualName, error: ValueErrorKind) -> NodeError {
        NodeError { attr, err: error }
    }

    pub fn parse_error(attr: QualName, error: &str) -> NodeError {
        NodeError {
            attr,
            err: ValueErrorKind::Parse(error.to_string()),
        }
    }
}

impl error::Error for NodeError {
    fn description(&self) -> &str {
        match self.err {
            ValueErrorKind::UnknownProperty => "unknown property",
            ValueErrorKind::Parse(_) => "parse error",
            ValueErrorKind::Value(_) => "invalid attribute value",
        }
    }
}

impl fmt::Display for NodeError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{:?}: {}", self.attr.expanded(), self.err)
    }
}

impl<'a> From<BasicParseError<'a>> for ValueErrorKind {
    fn from(e: BasicParseError<'_>) -> ValueErrorKind {
        let BasicParseError { kind, location: _ } =  e;

        let msg = match kind {
            BasicParseErrorKind::UnexpectedToken(_) => "unexpected token",
            BasicParseErrorKind::EndOfInput => "unexpected end of input",
            BasicParseErrorKind::AtRuleInvalid(_) => "invalid @-rule",
            BasicParseErrorKind::AtRuleBodyInvalid => "invalid @-rule body",
            BasicParseErrorKind::QualifiedRuleInvalid => "invalid qualified rule",
        };

        ValueErrorKind::parse_error(msg)
    }
}

#[derive(Debug, Clone, PartialEq)]
pub enum DefsLookupErrorKind {
    HrefError(HrefError),
    CannotLookupExternalReferences,
    NotFound,
}

#[derive(Debug, Clone, PartialEq)]
pub enum RenderingError {
    Cairo(cairo::Status),
    CircularReference,
    InstancingLimit,
    InvalidId(DefsLookupErrorKind),
    InvalidHref,
    OutOfMemory,
    HandleIsNotLoaded,
}

impl From<cairo::Status> for RenderingError {
    fn from(e: cairo::Status) -> RenderingError {
        assert!(e != cairo::Status::Success);

        RenderingError::Cairo(e)
    }
}

pub enum AcquireError {
    LinkNotFound(Fragment),
    InvalidLinkType(Fragment),
    CircularReference(RsvgNode),
    MaxReferencesExceeded,
}

impl fmt::Display for AcquireError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match *self {
            AcquireError::LinkNotFound(ref frag) =>
                write!(f, "link not found: {}", frag),

            AcquireError::InvalidLinkType(ref frag) =>
                write!(f, "link {} is to object of invalid type", frag),

            AcquireError::CircularReference(ref node) =>
                write!(f, "circular reference in node {}", node),

            AcquireError::MaxReferencesExceeded =>
                write!(f, "maximum number of references exceeded"),
        }
    }
}

/// Helper for converting `Result<O, E>` into `Result<O, NodeError>`
///
/// A `NodeError` requires a `QualName` that corresponds to the attribute to which the
/// error refers, plus the actual `ValueErrorKind` that describes the error.  However,
/// parsing functions for attribute value types will want to return their own kind of
/// error, instead of `ValueErrorKind`.  If that particular error type has an `impl
/// From<FooError> for ValueErrorKind`, then this trait helps assign attribute values in
/// `set_atts()` methods as follows:
///
/// ```ignore
/// use error::AttributeResultExt;
///
/// // fn parse_foo(...) -> Result<Foo, FooError>
///
/// // It is assumed that there is an impl From<FooError> for ValueErrorKind
///
/// self.foo = parse_foo(value).attribute(local_name!("foo"))?;
/// ```
///
/// The call to `.attribute(attr)` converts the `Result` from `parse_foo()` into a full
/// `NodeError` with the provided `attr`.
pub trait AttributeResultExt<O, E> {
    fn attribute(self, attr: QualName) -> Result<O, NodeError>;
}

impl<O, E: Into<ValueErrorKind>> AttributeResultExt<O, E> for Result<O, E> {
    fn attribute(self, attr: QualName) -> Result<O, NodeError> {
        self.map_err(|e| e.into())
            .map_err(|e| NodeError::new(attr, e))
    }
}

/// Errors returned when creating an `Href` out of a string
#[derive(Debug, Clone, PartialEq)]
pub enum HrefError {
    /// The href is an invalid URI or has empty components.
    ParseError,

    /// A fragment identifier ("`#foo`") is not allowed here
    ///
    /// For example, the SVG `<image>` element only allows referencing
    /// resources without fragment identifiers like
    /// `xlink:href="foo.png"`.
    FragmentForbidden,

    /// A fragment identifier ("`#foo`") was required but not found.  For example,
    /// the SVG `<use>` element requires one, as in `<use xlink:href="foo.svg#bar">`.
    FragmentRequired,
}

impl From<HrefError> for ValueErrorKind {
    fn from(e: HrefError) -> ValueErrorKind {
        match e {
            HrefError::ParseError => ValueErrorKind::parse_error("url parse error"),
            HrefError::FragmentForbidden => {
                ValueErrorKind::value_error("fragment identifier not allowed")
            }
            HrefError::FragmentRequired => {
                ValueErrorKind::value_error("fragment identifier required")
            }
        }
    }
}

#[derive(Debug, Clone)]
pub enum LoadingError {
    NoDataPassedToParser,
    XmlParseError(String),
    // Could not parse data: URL
    CouldNotCreateXmlParser,
    BadUrl,
    BadDataUrl,
    BadStylesheet,
    BadCss,
    Cairo(cairo::Status),
    EmptyData,
    SvgHasNoElements,
    RootElementIsNotSvg,
    Glib(glib::Error),
    Unknown,
}

impl error::Error for LoadingError {
    fn description(&self) -> &str {
        match *self {
            LoadingError::NoDataPassedToParser => "no data passed to parser",
            LoadingError::CouldNotCreateXmlParser => "could not create XML parser",
            LoadingError::XmlParseError(_) => "XML parse error",
            LoadingError::BadUrl => "invalid URL",
            LoadingError::BadDataUrl => "invalid data: URL",
            LoadingError::BadStylesheet => "invalid stylesheet",
            LoadingError::BadCss => "invalid CSS",
            LoadingError::Cairo(_) => "cairo error",
            LoadingError::EmptyData => "empty data",
            LoadingError::SvgHasNoElements => "SVG has no elements",
            LoadingError::RootElementIsNotSvg => "root element is not <svg>",
            LoadingError::Glib(ref e) => e.description(),
            LoadingError::Unknown => "unknown error",
        }
    }
}

impl fmt::Display for LoadingError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match *self {
            LoadingError::Cairo(status) => write!(f, "cairo error: {:?}", status),
            LoadingError::XmlParseError(ref s) => write!(f, "XML parse error: {}", s),
            LoadingError::NoDataPassedToParser
            | LoadingError::CouldNotCreateXmlParser
            | LoadingError::BadUrl
            | LoadingError::BadDataUrl
            | LoadingError::BadStylesheet
            | LoadingError::BadCss
            | LoadingError::EmptyData
            | LoadingError::SvgHasNoElements
            | LoadingError::RootElementIsNotSvg
            | LoadingError::Glib(_)
            | LoadingError::Unknown => write!(f, "{}", self.description()),
        }
    }
}

impl error::Error for RenderingError {
    fn description(&self) -> &str {
        match *self {
            RenderingError::Cairo(_) => "cairo error",
            RenderingError::CircularReference => "circular reference",
            RenderingError::InstancingLimit => "instancing limit",
            RenderingError::InvalidId(_) => "invalid id",
            RenderingError::InvalidHref => "invalid href",
            RenderingError::OutOfMemory => "out of memory",
            RenderingError::HandleIsNotLoaded => "SVG data is not loaded into handle",
        }
    }
}

impl fmt::Display for RenderingError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match *self {
            RenderingError::Cairo(ref status) => write!(f, "cairo error: {:?}", status),
            RenderingError::InvalidId(ref id) => write!(f, "invalid id: {:?}", id),
            RenderingError::CircularReference
            | RenderingError::InstancingLimit
            | RenderingError::InvalidHref
            | RenderingError::OutOfMemory
            | RenderingError::HandleIsNotLoaded => write!(f, "{}", self.description()),
        }
    }
}

impl From<cairo::Status> for LoadingError {
    fn from(e: cairo::Status) -> LoadingError {
        assert!(e != cairo::Status::Success);

        LoadingError::Cairo(e)
    }
}

impl From<glib::Error> for LoadingError {
    fn from(e: glib::Error) -> LoadingError {
        LoadingError::Glib(e)
    }
}

#[cfg(test)]
pub fn is_parse_error<T>(r: &Result<T, ValueErrorKind>) -> bool {
    match *r {
        Err(ValueErrorKind::Parse(_)) => true,
        _ => false,
    }
}

#[cfg(test)]
pub fn is_value_error<T>(r: &Result<T, ValueErrorKind>) -> bool {
    match *r {
        Err(ValueErrorKind::Value(_)) => true,
        _ => false,
    }
}
<|endoftext|>


<commit_before>extern crate tokio_core;
extern crate trust_dns;
extern crate trust_dns_server;
extern crate trust_dns_resolver;
extern crate trust_dns_integration;

use std::net::*;
use std::str::FromStr;

use tokio_core::reactor::Core;

use trust_dns::client::ClientFuture;
use trust_dns::op::Query;
use trust_dns::rr::domain;
use trust_dns::rr::{RData, RecordType};
use trust_dns_server::authority::Catalog;
use trust_dns_resolver::lookup::InnerLookupFuture;
use trust_dns_resolver::lookup_state::CachingClient;

use trust_dns_integration::TestClientStream;
use trust_dns_integration::authority::create_example;
use trust_dns_integration::mock_client::*;

#[test]
fn test_lookup() {
    let authority = create_example();
    let mut catalog = Catalog::new();
    catalog.upsert(authority.origin().clone(), authority);

    let mut io_loop = Core::new().unwrap();
    let (stream, sender) = TestClientStream::new(catalog);
    let client = ClientFuture::new(stream, sender, &io_loop.handle(), None);

    let lookup = InnerLookupFuture::lookup(
        vec![domain::Name::from_str("www.example.com.").unwrap()],
        RecordType::A,
        CachingClient::new(0, client),
    );
    let lookup = io_loop.run(lookup).unwrap();

    assert_eq!(
        *lookup.iter().next().unwrap(),
        RData::A(Ipv4Addr::new(93, 184, 216, 34))
    );
}

#[test]
fn test_mock_lookup() {
    let resp_query = Query::query(
        domain::Name::from_str("www.example.com.").unwrap(),
        RecordType::A,
    );
    let v4_record = v4_record(
        domain::Name::from_str("www.example.com.").unwrap(),
        Ipv4Addr::new(93, 184, 216, 34),
    );
    let message = message(resp_query, vec![v4_record], vec![], vec![]);
    let client = MockClientHandle::mock(vec![message]);

    let lookup = InnerLookupFuture::lookup(
        vec![domain::Name::from_str("www.example.com.").unwrap()],
        RecordType::A,
        CachingClient::new(0, client),
    );

    let mut io_loop = Core::new().unwrap();
    let lookup = io_loop.run(lookup).unwrap();

    assert_eq!(
        *lookup.iter().next().unwrap(),
        RData::A(Ipv4Addr::new(93, 184, 216, 34))
    );
}

#[test]
fn test_cname_lookup() {
    let resp_query = Query::query(
        domain::Name::from_str("www.example.com.").unwrap(),
        RecordType::A,
    );
    let cname_record = cname_record(
        domain::Name::from_str("www.example.com.").unwrap(),
        domain::Name::from_str("v4.example.com.").unwrap(),
    );
    let v4_record = v4_record(
        domain::Name::from_str("v4.example.com.").unwrap(),
        Ipv4Addr::new(93, 184, 216, 34),
    );
    let message = message(resp_query, vec![cname_record, v4_record], vec![], vec![]);
    let client = MockClientHandle::mock(vec![message]);

    let lookup = InnerLookupFuture::lookup(
        vec![domain::Name::from_str("www.example.com.").unwrap()],
        RecordType::A,
        CachingClient::new(0, client),
    );

    let mut io_loop = Core::new().unwrap();
    let lookup = io_loop.run(lookup).unwrap();

    assert_eq!(
        *lookup.iter().next().unwrap(),
        RData::A(Ipv4Addr::new(93, 184, 216, 34))
    );
}

#[test]
fn test_chained_cname_lookup() {
    let resp_query = Query::query(
        domain::Name::from_str("www.example.com.").unwrap(),
        RecordType::A,
    );
    let cname_record = cname_record(
        domain::Name::from_str("www.example.com.").unwrap(),
        domain::Name::from_str("v4.example.com.").unwrap(),
    );
    let v4_record = v4_record(
        domain::Name::from_str("v4.example.com.").unwrap(),
        Ipv4Addr::new(93, 184, 216, 34),
    );

    // The first response should be a cname, the second will be the actual record
    let message1 = message(resp_query.clone(), vec![cname_record], vec![], vec![]);
    let message2 = message(resp_query, vec![v4_record], vec![], vec![]);

    // the mock pops messages...
    let client = MockClientHandle::mock(vec![message2, message1]);

    let lookup = InnerLookupFuture::lookup(
        vec![domain::Name::from_str("www.example.com.").unwrap()],
        RecordType::A,
        CachingClient::new(0, client),
    );

    let mut io_loop = Core::new().unwrap();
    let lookup = io_loop.run(lookup).unwrap();

    assert_eq!(
        *lookup.iter().next().unwrap(),
        RData::A(Ipv4Addr::new(93, 184, 216, 34))
    );
}

#[test]
fn test_max_chained_lookup_depth() {
    let resp_query = Query::query(
        domain::Name::from_str("www.example.com.").unwrap(),
        RecordType::A,
    );
    let cname_record1 = cname_record(
        domain::Name::from_str("www.example.com.").unwrap(),
        domain::Name::from_str("cname2.example.com.").unwrap(),
    );
    let cname_record2 = cname_record(
        domain::Name::from_str("cname2.example.com.").unwrap(),
        domain::Name::from_str("cname3.example.com.").unwrap(),
    );
    let cname_record3 = cname_record(
        domain::Name::from_str("cname3.example.com.").unwrap(),
        domain::Name::from_str("cname4.example.com.").unwrap(),
    );
    let cname_record4 = cname_record(
        domain::Name::from_str("cname4.example.com.").unwrap(),
        domain::Name::from_str("cname5.example.com.").unwrap(),
    );
    let cname_record5 = cname_record(
        domain::Name::from_str("cname5.example.com.").unwrap(),
        domain::Name::from_str("cname6.example.com.").unwrap(),
    );
    let cname_record6 = cname_record(
        domain::Name::from_str("cname6.example.com.").unwrap(),
        domain::Name::from_str("cname7.example.com.").unwrap(),
    );
    let cname_record7 = cname_record(
        domain::Name::from_str("cname7.example.com.").unwrap(),
        domain::Name::from_str("cname8.example.com.").unwrap(),
    );
    let cname_record8 = cname_record(
        domain::Name::from_str("cname8.example.com.").unwrap(),
        domain::Name::from_str("cname9.example.com.").unwrap(),
    );
    let cname_record9 = cname_record(
        domain::Name::from_str("cname9.example.com.").unwrap(),
        domain::Name::from_str("v4.example.com.").unwrap(),
    );
    let v4_record = v4_record(
        domain::Name::from_str("v4.example.com.").unwrap(),
        Ipv4Addr::new(93, 184, 216, 34),
    );

    // The first response should be a cname, the second will be the actual record
    let message1 = message(resp_query.clone(), vec![cname_record1], vec![], vec![]);
    let message2 = message(resp_query.clone(), vec![cname_record2], vec![], vec![]);
    let message3 = message(resp_query.clone(), vec![cname_record3], vec![], vec![]);
    let message4 = message(resp_query.clone(), vec![cname_record4], vec![], vec![]);
    let message5 = message(resp_query.clone(), vec![cname_record5], vec![], vec![]);
    let message6 = message(resp_query.clone(), vec![cname_record6], vec![], vec![]);
    let message7 = message(resp_query.clone(), vec![cname_record7], vec![], vec![]);
    let message8 = message(resp_query.clone(), vec![cname_record8], vec![], vec![]);
    let message9 = message(resp_query.clone(), vec![cname_record9], vec![], vec![]);
    let message10 = message(resp_query, vec![v4_record], vec![], vec![]);

    // the mock pops messages...
    let client = MockClientHandle::mock(vec![
        message10,
        message9,
        message8,
        message7,
        message6,
        message5,
        message4,
        message3,
        message2,
        message1,
    ]);

    let client = CachingClient::new(0, client);
    let lookup = InnerLookupFuture::lookup(
        vec![domain::Name::from_str("www.example.com.").unwrap()],
        RecordType::A,
        client.clone(),
    );

    let mut io_loop = Core::new().unwrap();

    println!("performing max cname validation");
    // TODO: validate exact error
    assert!(io_loop.run(lookup).is_err());

    // This query should succeed, as the queue depth should reset to 0 on a failed request
    let lookup = InnerLookupFuture::lookup(
        vec![domain::Name::from_str("cname9.example.com.").unwrap()],
        RecordType::A,
        client,
    );

    println!("performing followup resolve, should work");
    let lookup = io_loop.run(lookup).unwrap();

    assert_eq!(
        *lookup.iter().next().unwrap(),
        RData::A(Ipv4Addr::new(93, 184, 216, 34))
    );
}<commit_msg>add integration test for lookup hosts<commit_after>extern crate tokio_core;
extern crate trust_dns;
extern crate trust_dns_server;
extern crate trust_dns_resolver;
extern crate trust_dns_integration;

use std::net::*;
use std::str::FromStr;
use std::sync::Arc;

use tokio_core::reactor::Core;

use trust_dns::client::ClientFuture;
use trust_dns::op::Query;
use trust_dns::rr::domain;
use trust_dns::rr::{RData, RecordType};
use trust_dns_server::authority::Catalog;
use trust_dns_resolver::lookup::InnerLookupFuture;
use trust_dns_resolver::lookup_ip::InnerLookupIpFuture;
use trust_dns_resolver::lookup_state::CachingClient;
use trust_dns_resolver::config::LookupIpStrategy;
use trust_dns_resolver::Hosts;

use trust_dns_integration::TestClientStream;
use trust_dns_integration::authority::create_example;
use trust_dns_integration::mock_client::*;

#[test]
fn test_lookup() {
    let authority = create_example();
    let mut catalog = Catalog::new();
    catalog.upsert(authority.origin().clone(), authority);

    let mut io_loop = Core::new().unwrap();
    let (stream, sender) = TestClientStream::new(catalog);
    let client = ClientFuture::new(stream, sender, &io_loop.handle(), None);

    let lookup = InnerLookupFuture::lookup(
        vec![domain::Name::from_str("www.example.com.").unwrap()],
        RecordType::A,
        CachingClient::new(0, client),
    );
    let lookup = io_loop.run(lookup).unwrap();

    assert_eq!(
        *lookup.iter().next().unwrap(),
        RData::A(Ipv4Addr::new(93, 184, 216, 34))
    );
}

#[test]
fn test_lookup_hosts() {
    let authority = create_example();
    let mut catalog = Catalog::new();
    catalog.upsert(authority.origin().clone(), authority);

    let mut io_loop = Core::new().unwrap();
    let (stream, sender) = TestClientStream::new(catalog);
    let client = ClientFuture::new(stream, sender, &io_loop.handle(), None);

    let mut hosts = Hosts::default();

    hosts
        .by_name
        .entry(domain::Name::from_str("www.example.com.").unwrap())
        .or_insert(vec![])
        .push(RData::A(Ipv4Addr::new(10, 0, 1, 104)));

    let lookup = InnerLookupIpFuture::lookup(
        vec![domain::Name::from_str("www.example.com.").unwrap()],
        LookupIpStrategy::default(),
        CachingClient::new(0, client),
        Arc::new(hosts),
    );
    let lookup = io_loop.run(lookup).unwrap();

    assert_eq!(lookup.iter().next().unwrap(), Ipv4Addr::new(10, 0, 1, 104));
}

#[test]
fn test_mock_lookup() {
    let resp_query = Query::query(
        domain::Name::from_str("www.example.com.").unwrap(),
        RecordType::A,
    );
    let v4_record = v4_record(
        domain::Name::from_str("www.example.com.").unwrap(),
        Ipv4Addr::new(93, 184, 216, 34),
    );
    let message = message(resp_query, vec![v4_record], vec![], vec![]);
    let client = MockClientHandle::mock(vec![message]);

    let lookup = InnerLookupFuture::lookup(
        vec![domain::Name::from_str("www.example.com.").unwrap()],
        RecordType::A,
        CachingClient::new(0, client),
    );

    let mut io_loop = Core::new().unwrap();
    let lookup = io_loop.run(lookup).unwrap();

    assert_eq!(
        *lookup.iter().next().unwrap(),
        RData::A(Ipv4Addr::new(93, 184, 216, 34))
    );
}

#[test]
fn test_cname_lookup() {
    let resp_query = Query::query(
        domain::Name::from_str("www.example.com.").unwrap(),
        RecordType::A,
    );
    let cname_record = cname_record(
        domain::Name::from_str("www.example.com.").unwrap(),
        domain::Name::from_str("v4.example.com.").unwrap(),
    );
    let v4_record = v4_record(
        domain::Name::from_str("v4.example.com.").unwrap(),
        Ipv4Addr::new(93, 184, 216, 34),
    );
    let message = message(resp_query, vec![cname_record, v4_record], vec![], vec![]);
    let client = MockClientHandle::mock(vec![message]);

    let lookup = InnerLookupFuture::lookup(
        vec![domain::Name::from_str("www.example.com.").unwrap()],
        RecordType::A,
        CachingClient::new(0, client),
    );

    let mut io_loop = Core::new().unwrap();
    let lookup = io_loop.run(lookup).unwrap();

    assert_eq!(
        *lookup.iter().next().unwrap(),
        RData::A(Ipv4Addr::new(93, 184, 216, 34))
    );
}

#[test]
fn test_chained_cname_lookup() {
    let resp_query = Query::query(
        domain::Name::from_str("www.example.com.").unwrap(),
        RecordType::A,
    );
    let cname_record = cname_record(
        domain::Name::from_str("www.example.com.").unwrap(),
        domain::Name::from_str("v4.example.com.").unwrap(),
    );
    let v4_record = v4_record(
        domain::Name::from_str("v4.example.com.").unwrap(),
        Ipv4Addr::new(93, 184, 216, 34),
    );

    // The first response should be a cname, the second will be the actual record
    let message1 = message(resp_query.clone(), vec![cname_record], vec![], vec![]);
    let message2 = message(resp_query, vec![v4_record], vec![], vec![]);

    // the mock pops messages...
    let client = MockClientHandle::mock(vec![message2, message1]);

    let lookup = InnerLookupFuture::lookup(
        vec![domain::Name::from_str("www.example.com.").unwrap()],
        RecordType::A,
        CachingClient::new(0, client),
    );

    let mut io_loop = Core::new().unwrap();
    let lookup = io_loop.run(lookup).unwrap();

    assert_eq!(
        *lookup.iter().next().unwrap(),
        RData::A(Ipv4Addr::new(93, 184, 216, 34))
    );
}

#[test]
fn test_max_chained_lookup_depth() {
    let resp_query = Query::query(
        domain::Name::from_str("www.example.com.").unwrap(),
        RecordType::A,
    );
    let cname_record1 = cname_record(
        domain::Name::from_str("www.example.com.").unwrap(),
        domain::Name::from_str("cname2.example.com.").unwrap(),
    );
    let cname_record2 = cname_record(
        domain::Name::from_str("cname2.example.com.").unwrap(),
        domain::Name::from_str("cname3.example.com.").unwrap(),
    );
    let cname_record3 = cname_record(
        domain::Name::from_str("cname3.example.com.").unwrap(),
        domain::Name::from_str("cname4.example.com.").unwrap(),
    );
    let cname_record4 = cname_record(
        domain::Name::from_str("cname4.example.com.").unwrap(),
        domain::Name::from_str("cname5.example.com.").unwrap(),
    );
    let cname_record5 = cname_record(
        domain::Name::from_str("cname5.example.com.").unwrap(),
        domain::Name::from_str("cname6.example.com.").unwrap(),
    );
    let cname_record6 = cname_record(
        domain::Name::from_str("cname6.example.com.").unwrap(),
        domain::Name::from_str("cname7.example.com.").unwrap(),
    );
    let cname_record7 = cname_record(
        domain::Name::from_str("cname7.example.com.").unwrap(),
        domain::Name::from_str("cname8.example.com.").unwrap(),
    );
    let cname_record8 = cname_record(
        domain::Name::from_str("cname8.example.com.").unwrap(),
        domain::Name::from_str("cname9.example.com.").unwrap(),
    );
    let cname_record9 = cname_record(
        domain::Name::from_str("cname9.example.com.").unwrap(),
        domain::Name::from_str("v4.example.com.").unwrap(),
    );
    let v4_record = v4_record(
        domain::Name::from_str("v4.example.com.").unwrap(),
        Ipv4Addr::new(93, 184, 216, 34),
    );

    // The first response should be a cname, the second will be the actual record
    let message1 = message(resp_query.clone(), vec![cname_record1], vec![], vec![]);
    let message2 = message(resp_query.clone(), vec![cname_record2], vec![], vec![]);
    let message3 = message(resp_query.clone(), vec![cname_record3], vec![], vec![]);
    let message4 = message(resp_query.clone(), vec![cname_record4], vec![], vec![]);
    let message5 = message(resp_query.clone(), vec![cname_record5], vec![], vec![]);
    let message6 = message(resp_query.clone(), vec![cname_record6], vec![], vec![]);
    let message7 = message(resp_query.clone(), vec![cname_record7], vec![], vec![]);
    let message8 = message(resp_query.clone(), vec![cname_record8], vec![], vec![]);
    let message9 = message(resp_query.clone(), vec![cname_record9], vec![], vec![]);
    let message10 = message(resp_query, vec![v4_record], vec![], vec![]);

    // the mock pops messages...
    let client = MockClientHandle::mock(vec![
        message10,
        message9,
        message8,
        message7,
        message6,
        message5,
        message4,
        message3,
        message2,
        message1,
    ]);

    let client = CachingClient::new(0, client);
    let lookup = InnerLookupFuture::lookup(
        vec![domain::Name::from_str("www.example.com.").unwrap()],
        RecordType::A,
        client.clone(),
    );

    let mut io_loop = Core::new().unwrap();

    println!("performing max cname validation");
    // TODO: validate exact error
    assert!(io_loop.run(lookup).is_err());

    // This query should succeed, as the queue depth should reset to 0 on a failed request
    let lookup = InnerLookupFuture::lookup(
        vec![domain::Name::from_str("cname9.example.com.").unwrap()],
        RecordType::A,
        client,
    );

    println!("performing followup resolve, should work");
    let lookup = io_loop.run(lookup).unwrap();

    assert_eq!(
        *lookup.iter().next().unwrap(),
        RData::A(Ipv4Addr::new(93, 184, 216, 34))
    );
}
<|endoftext|>


<commit_before>extern crate "rust-crypto" as rust_crypto;
extern crate serialize;

use std::io::{TcpListener, TcpStream};
use std::io::{Acceptor, Listener};
use std::io::{File, BufferedStream};
use std::string::String;
use std::os;

use rust_crypto::sha1::Sha1;
use rust_crypto::digest::Digest;
use serialize::base64::{ToBase64, STANDARD};

pub const CR: u8 = b'\r';
pub const LF: u8 = b'\n';
pub const SP: u8 = b' ';
pub const COLON: u8 = b':';
pub const CRLF: &'static [u8] = &[CR,LF];

struct RequestedRoute {
    method: String,
    pathname: String
}

struct ClientHeader {
    key: String,
    value: String
}

enum Payload {
    Text(String),
    Binary(Vec<u8>),
    Empty
}

enum Opcode {
    TextOp = 0x1,
    BinaryOp = 0x2
}

struct Message {
    payload: Payload,
    opcode: Opcode
 }

impl Message {

    fn from_payload (payload: Payload) -> Message {
        let opcode: Opcode = match payload {
            Payload::Text(_) => Opcode::TextOp,
            Payload::Binary(_) => Opcode::BinaryOp,
            _ => panic!("unsupported payload")
        };
        return Message {payload: payload, opcode: opcode};
    }

    fn send (&self,
            mut stream: &mut BufferedStream<TcpStream>,
            mut headers: &mut Vec<ClientHeader>) {
        let msg = match self.payload {
            Payload::Text(ref s) => s.as_bytes(),
            Payload::Binary(ref s) => s.as_slice(),
            Payload::Empty => unimplemented!()
        };

        let length = msg.len() as u8;

        println!("msg: {}, opcode: {}", msg, self.opcode as u8);

        stream.write_u8(0b1000_0000 | self.opcode as u8).unwrap();
        stream.write_u8(length).unwrap();
        stream.write(msg).unwrap();
    }
}

fn get_header_by_name (header: &[u8], headers: &Vec<ClientHeader>) -> String {
    let mut result = String::new();

    for h in headers.iter() {
        if h.key.as_bytes() == header {
            result = h.value.clone();
            break;
        }
    }

    return result;
}

fn parse_request_line (header: &str) -> RequestedRoute {
    let mut pathname = String::new();
    let mut method = String::new();
    let mut starting = true;
    let mut found_method = false;

    for ch in header.graphemes(true) {
        let cbyte = ch.as_bytes()[0];

        if starting && cbyte != SP {
            method.push_str(ch);
        }
        if starting && cbyte == SP {
            starting = false;
            found_method = true;
            continue;
        }
        if found_method && cbyte != SP {
            pathname.push_str(ch);
        }
        if found_method && cbyte == SP {
            break;
        }

    }

    return RequestedRoute {method: method, pathname: pathname};
}

fn parse_normal_header (header: &str) -> ClientHeader {
    let mut lhs = String::new(); // lhs of ':' in header
    let mut rhs = String::new(); // rhs of ':' in header
    let mut found_colon = false;

    for ch in header.graphemes(true) {
        let cbyte = ch.as_bytes()[0];
        if !found_colon && cbyte == COLON {
            found_colon = true;
            continue;
        }

        if cbyte == CR || cbyte == LF {
            continue;
        }

        if !found_colon {
            lhs.push_str(ch);
        } else {
            rhs.push_str(ch);
        }
    }

    return ClientHeader {
        key:lhs.trim().to_string(),
        value:rhs.trim().to_string()
    };
}

fn get_normal_body (path_on_disk: &str) -> String {
    let path = Path::new(path_on_disk);
    let display = path.display();
    let mut file = match File::open(&path) {
        Ok(f) => f,
        Err(err) => panic!("file error: {}", err)
    };

    let content = match file.read_to_end() {
        Ok(c) => c,
        Err(err) => panic!("{}", err)
    };

    let s = String::from_utf8(content).unwrap();
    return s;
}

fn sec_handshake (from_server: &[u8]) -> String {

    // from rfc6455 [page 6]
    let guid = b"258EAFA5-E914-47DA-95CA-C5AB0DC85B11";

    let mut sha = Sha1::new();

    sha.input(from_server);
    sha.input(guid);
    let mut out = [0u8, ..20];
    sha.result(out.as_mut_slice());

    return out.to_base64(STANDARD);
}

fn ws_handshake (mut stream: BufferedStream<TcpStream>,
                 headers: Vec<ClientHeader>) {

    let header_sec_key = b"Sec-WebSocket-Key";

    let from_server = get_header_by_name(header_sec_key, &headers);
    let accept = sec_handshake(from_server.as_bytes());

    let sec_header = format!("Sec-WebSocket-Accept: {}\r\n", accept);

    stream.write(b"HTTP/1.1 101 Switching Protocols\r\n").unwrap();
    stream.write(b"Upgrade: websocket\r\n").unwrap();
    stream.write(b"Connection: Upgrade\r\n").unwrap();
    stream.write(b"Sec-WebSocket-Version: 13\r\n").unwrap();
    stream.write(b"Sec-WebSocket-Protocol: protocolOne\r\n").unwrap();
    stream.write(sec_header.as_bytes()).unwrap();
    stream.write(b"\r\n");

    ws_listen(stream, headers);
}

fn ws_listen(mut stream: BufferedStream<TcpStream>,
             mut headers: Vec<ClientHeader>) {

    /*
    let string_msg = "this is a message".to_string();
    let msg = box Message::new(&string_msg);
    msg.send(stream, headers);
    */

    let mut bin = Vec::new();
    bin.push('a' as u8);

    let payload = Payload::Text("text here".to_string());
    let msg = Message::from_payload(payload);
    msg.send(&mut stream, &mut headers);
    let mut stream2 = stream;

    msg.send(&mut stream2, &mut headers);
}

fn main () {

    for argument in os::args().iter() {
        println!("arg: {}", argument);
    }

    let addr = "127.0.0.1:8099";

    let listener = TcpListener::bind(addr).unwrap();
    let mut acceptor = listener.listen().unwrap();
    println!("Listening on {}", addr);

    fn handle_client(mut stream: BufferedStream<TcpStream>) {
        let mut body: String = "
You sent it!
".to_string();

        let mut cur_line: String;
        let mut ii = 0u;
        let mut req: RequestedRoute = RequestedRoute {
            pathname: String::new(),
            method: String::new()
        };

        let mut headers: Vec<ClientHeader> = Vec::new();
        loop {
            match stream.read_line() { // XXX: strange unwrap like thing
                Ok(line) => cur_line = line,
                Err(_) => break
            }

            body = body + cur_line + "
";
            if cur_line.as_bytes() == CRLF {
                break;
            }

            if ii == 0u { // the Request-Line is always the first line
                req = parse_request_line(cur_line.as_slice());
                println!("{}", req.pathname);
                ii += 1;
            } else { // this should be  "normal" key-value type header
                headers.push(parse_normal_header(cur_line.as_slice()));
            }

        }

        println!("pathname {} method {}", req.pathname, req.method);

        if req.pathname.as_bytes() == b"/" {
            body = get_normal_body("../html/ws1.html");
        } else if req.pathname.as_bytes() == b"/ws1.js" {
            body = get_normal_body("../html/ws1.js");
        } else if req.pathname.as_bytes() == b"/ws" {
            ws_handshake(stream, headers);
            return;
        }

        let body_length = format!("Content-length: {}", body.len());

        stream.write(b"HTTP/1.1 200 OK\r\n").unwrap();
        stream.write(b"Content-type: text/html\r\n").unwrap();
        stream.write(b"X-header: from bytes\r\n").unwrap();
        stream.write(body_length.as_bytes()).unwrap();
        stream.write(b"\r\n\r\n").unwrap();
        stream.write(body.as_bytes()).unwrap();
        stream.flush().unwrap();

        println!("Done handling acceptor");
    }

    for stream in acceptor.incoming() {
        match stream {
            Err(e) => { println!("connection failed: {}", e) }
            Ok(stream) => spawn(proc() {
                let bs = BufferedStream::new(stream);
                handle_client(bs)
            })
        }
    }

    drop(acceptor);
}
<commit_msg>remove unused header param<commit_after>extern crate "rust-crypto" as rust_crypto;
extern crate serialize;

use std::io::{TcpListener, TcpStream};
use std::io::{Acceptor, Listener};
use std::io::{File, BufferedStream};
use std::string::String;
use std::os;

use rust_crypto::sha1::Sha1;
use rust_crypto::digest::Digest;
use serialize::base64::{ToBase64, STANDARD};

pub const CR: u8 = b'\r';
pub const LF: u8 = b'\n';
pub const SP: u8 = b' ';
pub const COLON: u8 = b':';
pub const CRLF: &'static [u8] = &[CR,LF];

struct RequestedRoute {
    method: String,
    pathname: String
}

struct ClientHeader {
    key: String,
    value: String
}

enum Payload {
    Text(String),
    Binary(Vec<u8>),
    Empty
}

enum Opcode {
    TextOp = 0x1,
    BinaryOp = 0x2
}

struct Message {
    payload: Payload,
    opcode: Opcode
 }

impl Message {

    fn from_payload (payload: Payload) -> Message {
        let opcode: Opcode = match payload {
            Payload::Text(_) => Opcode::TextOp,
            Payload::Binary(_) => Opcode::BinaryOp,
            _ => panic!("unsupported payload")
        };
        return Message {payload: payload, opcode: opcode};
    }

    fn send (&self,
            mut stream: &mut BufferedStream<TcpStream>) {
        let msg = match self.payload {
            Payload::Text(ref s) => s.as_bytes(),
            Payload::Binary(ref s) => s.as_slice(),
            Payload::Empty => unimplemented!()
        };

        let length = msg.len() as u8;

        println!("msg: {}, opcode: {}", msg, self.opcode as u8);

        stream.write_u8(0b1000_0000 | self.opcode as u8).unwrap();
        stream.write_u8(length).unwrap();
        stream.write(msg).unwrap();
    }
}

fn get_header_by_name (header: &[u8], headers: &Vec<ClientHeader>) -> String {
    let mut result = String::new();

    for h in headers.iter() {
        if h.key.as_bytes() == header {
            result = h.value.clone();
            break;
        }
    }

    return result;
}

fn parse_request_line (header: &str) -> RequestedRoute {
    let mut pathname = String::new();
    let mut method = String::new();
    let mut starting = true;
    let mut found_method = false;

    for ch in header.graphemes(true) {
        let cbyte = ch.as_bytes()[0];

        if starting && cbyte != SP {
            method.push_str(ch);
        }
        if starting && cbyte == SP {
            starting = false;
            found_method = true;
            continue;
        }
        if found_method && cbyte != SP {
            pathname.push_str(ch);
        }
        if found_method && cbyte == SP {
            break;
        }

    }

    return RequestedRoute {method: method, pathname: pathname};
}

fn parse_normal_header (header: &str) -> ClientHeader {
    let mut lhs = String::new(); // lhs of ':' in header
    let mut rhs = String::new(); // rhs of ':' in header
    let mut found_colon = false;

    for ch in header.graphemes(true) {
        let cbyte = ch.as_bytes()[0];
        if !found_colon && cbyte == COLON {
            found_colon = true;
            continue;
        }

        if cbyte == CR || cbyte == LF {
            continue;
        }

        if !found_colon {
            lhs.push_str(ch);
        } else {
            rhs.push_str(ch);
        }
    }

    return ClientHeader {
        key:lhs.trim().to_string(),
        value:rhs.trim().to_string()
    };
}

fn get_normal_body (path_on_disk: &str) -> String {
    let path = Path::new(path_on_disk);
    let display = path.display();
    let mut file = match File::open(&path) {
        Ok(f) => f,
        Err(err) => panic!("file error: {}", err)
    };

    let content = match file.read_to_end() {
        Ok(c) => c,
        Err(err) => panic!("{}", err)
    };

    let s = String::from_utf8(content).unwrap();
    return s;
}

fn sec_handshake (from_server: &[u8]) -> String {

    // from rfc6455 [page 6]
    let guid = b"258EAFA5-E914-47DA-95CA-C5AB0DC85B11";

    let mut sha = Sha1::new();

    sha.input(from_server);
    sha.input(guid);
    let mut out = [0u8, ..20];
    sha.result(out.as_mut_slice());

    return out.to_base64(STANDARD);
}

fn ws_handshake (mut stream: BufferedStream<TcpStream>,
                 headers: Vec<ClientHeader>) {

    let header_sec_key = b"Sec-WebSocket-Key";

    let from_server = get_header_by_name(header_sec_key, &headers);
    let accept = sec_handshake(from_server.as_bytes());

    let sec_header = format!("Sec-WebSocket-Accept: {}\r\n", accept);

    stream.write(b"HTTP/1.1 101 Switching Protocols\r\n").unwrap();
    stream.write(b"Upgrade: websocket\r\n").unwrap();
    stream.write(b"Connection: Upgrade\r\n").unwrap();
    stream.write(b"Sec-WebSocket-Version: 13\r\n").unwrap();
    stream.write(b"Sec-WebSocket-Protocol: protocolOne\r\n").unwrap();
    stream.write(sec_header.as_bytes()).unwrap();
    stream.write(b"\r\n");

    ws_listen(stream, headers);
}

fn ws_listen(mut stream: BufferedStream<TcpStream>,
             mut headers: Vec<ClientHeader>) {

    /*
    let string_msg = "this is a message".to_string();
    let msg = box Message::new(&string_msg);
    msg.send(stream, headers);
    */

    let mut bin = Vec::new();
    bin.push('a' as u8);

    let payload = Payload::Text("text here".to_string());
    let msg = Message::from_payload(payload);
    msg.send(&mut stream);
    let mut stream2 = stream;
    msg.send(&mut stream2);
}

fn main () {

    for argument in os::args().iter() {
        println!("arg: {}", argument);
    }

    let addr = "127.0.0.1:8099";

    let listener = TcpListener::bind(addr).unwrap();
    let mut acceptor = listener.listen().unwrap();
    println!("Listening on {}", addr);

    fn handle_client(mut stream: BufferedStream<TcpStream>) {
        let mut body: String = "
You sent it!
".to_string();

        let mut cur_line: String;
        let mut ii = 0u;
        let mut req: RequestedRoute = RequestedRoute {
            pathname: String::new(),
            method: String::new()
        };

        let mut headers: Vec<ClientHeader> = Vec::new();
        loop {
            match stream.read_line() { // XXX: strange unwrap like thing
                Ok(line) => cur_line = line,
                Err(_) => break
            }

            body = body + cur_line + "
";
            if cur_line.as_bytes() == CRLF {
                break;
            }

            if ii == 0u { // the Request-Line is always the first line
                req = parse_request_line(cur_line.as_slice());
                println!("{}", req.pathname);
                ii += 1;
            } else { // this should be  "normal" key-value type header
                headers.push(parse_normal_header(cur_line.as_slice()));
            }

        }

        println!("pathname {} method {}", req.pathname, req.method);

        if req.pathname.as_bytes() == b"/" {
            body = get_normal_body("../html/ws1.html");
        } else if req.pathname.as_bytes() == b"/ws1.js" {
            body = get_normal_body("../html/ws1.js");
        } else if req.pathname.as_bytes() == b"/ws" {
            ws_handshake(stream, headers);
            return;
        }

        let body_length = format!("Content-length: {}", body.len());

        stream.write(b"HTTP/1.1 200 OK\r\n").unwrap();
        stream.write(b"Content-type: text/html\r\n").unwrap();
        stream.write(b"X-header: from bytes\r\n").unwrap();
        stream.write(body_length.as_bytes()).unwrap();
        stream.write(b"\r\n\r\n").unwrap();
        stream.write(body.as_bytes()).unwrap();
        stream.flush().unwrap();

        println!("Done handling acceptor");
    }

    for stream in acceptor.incoming() {
        match stream {
            Err(e) => { println!("connection failed: {}", e) }
            Ok(stream) => spawn(proc() {
                let bs = BufferedStream::new(stream);
                handle_client(bs)
            })
        }
    }

    drop(acceptor);
}
<|endoftext|>


<commit_before>// Copyright (C) 2019 Sebastian Dröge <sebastian@centricular.com>
//
// 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.

use glib;
use glib::gobject_sys;
use glib::prelude::*;
use glib::subclass;
use glib::subclass::prelude::*;
use gst;
use gst::prelude::*;
use gst::subclass::prelude::*;
use std::sync::Mutex;

use once_cell::sync::Lazy;

// This enum may be used to control what type of output the progressbin should produce.
// It also serves the secondary purpose of illustrating how to add enum-type properties
// to a plugin written in rust.
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy, GEnum)]
#[repr(u32)]
#[genum(type_name = "GstProgressBinOutput")]
pub(crate) enum ProgressBinOutput {
    #[genum(
        name = "Println: Outputs the progress using a println! macro.",
        nick = "println"
    )]
    Println = 0,
    #[genum(
        name = "Debug Category: Outputs the progress as info logs under the element's debug category.",
        nick = "debug-category"
    )]
    DebugCategory = 1,
}

const DEFAULT_OUTPUT_TYPE: ProgressBinOutput = ProgressBinOutput::Println;

static CAT: Lazy<gst::DebugCategory> = Lazy::new(|| {
    gst::DebugCategory::new(
        "progressbin",
        gst::DebugColorFlags::empty(),
        Some("Rust Progress Reporter"),
    )
});

// Struct containing all the element data
struct ProgressBin {
    progress: gst::Element,
    srcpad: gst::GhostPad,
    sinkpad: gst::GhostPad,
    // We put the output_type property behind a mutex, as we want
    // change it in the set_property function, which can be called
    // from any thread.
    output_type: Mutex<ProgressBinOutput>,
}

// Metadata for the element's properties
static PROPERTIES: [subclass::Property; 1] = [subclass::Property("output", |name| {
    glib::ParamSpec::enum_(
        name,
        "Output",
        "Defines the output type of the progressbin",
        ProgressBinOutput::static_type(),
        DEFAULT_OUTPUT_TYPE as i32,
        glib::ParamFlags::READWRITE,
    )
})];

// This trait registers our type with the GObject object system and
// provides the entry points for creating a new instance and setting
// up the class data
impl ObjectSubclass for ProgressBin {
    const NAME: &'static str = "RsProgressBin";
    type ParentType = gst::Bin;
    type Instance = gst::subclass::ElementInstanceStruct<Self>;
    type Class = subclass::simple::ClassStruct<Self>;

    // This macro provides some boilerplate.
    glib_object_subclass!();

    // Called when a new instance is to be created. We need to return an instance
    // of our struct here and also get the class struct passed in case it's needed
    fn new_with_class(klass: &subclass::simple::ClassStruct<Self>) -> Self {
        // Create our two ghostpads from the templates that were registered with
        // the class. We don't provide a target for them yet because we can only
        // do so after the progressreport element was added to the bin.
        //
        // We do that and adding the pads inside glib::Object::constructed() later.
        let templ = klass.get_pad_template("sink").unwrap();
        let sinkpad = gst::GhostPad::new_no_target_from_template(Some("sink"), &templ).unwrap();
        let templ = klass.get_pad_template("src").unwrap();
        let srcpad = gst::GhostPad::new_no_target_from_template(Some("src"), &templ).unwrap();

        // Create the progressreport element.
        let progress = gst::ElementFactory::make("progressreport", Some("progress")).unwrap();
        // Don't let progressreport print to stdout itself
        progress.set_property("silent", &true).unwrap();

        // Return an instance of our struct
        Self {
            progress,
            srcpad,
            sinkpad,
            output_type: Mutex::new(ProgressBinOutput::Println),
        }
    }

    // Called exactly once when registering the type. Used for
    // setting up metadata for all instances, e.g. the name and
    // classification and the pad templates with their caps.
    //
    // Actual instances can create pads based on those pad templates
    // with a subset of the caps given here.
    fn class_init(klass: &mut subclass::simple::ClassStruct<Self>) {
        // Set the element specific metadata. This information is what
        // is visible from gst-inspect-1.0 and can also be programatically
        // retrieved from the gst::Registry after initial registration
        // without having to load the plugin in memory.
        klass.set_metadata(
            "ProgressBin",
            "Generic",
            "Prints progress information to stdout",
            "Sebastian Dröge <sebastian@centricular.com>",
        );

        // Create and add pad templates for our sink and source pad. These
        // are later used for actually creating the pads and beforehand
        // already provide information to GStreamer about all possible
        // pads that could exist for this type.

        // Our element can accept any possible caps on both pads
        let caps = gst::Caps::new_any();
        let src_pad_template = gst::PadTemplate::new(
            "src",
            gst::PadDirection::Src,
            gst::PadPresence::Always,
            &caps,
        )
        .unwrap();
        klass.add_pad_template(src_pad_template);

        let sink_pad_template = gst::PadTemplate::new(
            "sink",
            gst::PadDirection::Sink,
            gst::PadPresence::Always,
            &caps,
        )
        .unwrap();
        klass.add_pad_template(sink_pad_template);

        // Install all our properties
        klass.install_properties(&PROPERTIES);
    }
}

// Implementation of glib::Object virtual methods
impl ObjectImpl for ProgressBin {
    // This macro provides some boilerplate
    glib_object_impl!();

    // Called whenever a value of a property is changed. It can be called
    // at any time from any thread.
    fn set_property(&self, obj: &glib::Object, id: usize, value: &glib::Value) {
        let prop = &PROPERTIES[id];
        let element = obj.downcast_ref::<gst_base::BaseTransform>().unwrap();

        match *prop {
            subclass::Property("output", ..) => {
                let mut output_type = self.output_type.lock().unwrap();
                let new_output_type = value
                    .get_some::<ProgressBinOutput>()
                    .expect("type checked upstream");
                gst_info!(
                    CAT,
                    obj: element,
                    "Changing output from {:?} to {:?}",
                    output_type,
                    new_output_type
                );
                *output_type = new_output_type;
            }
            _ => unimplemented!(),
        }
    }

    // Called whenever a value of a property is read. It can be called
    // at any time from any thread.
    fn get_property(&self, _obj: &glib::Object, id: usize) -> Result<glib::Value, ()> {
        let prop = &PROPERTIES[id];

        match *prop {
            subclass::Property("output", ..) => {
                let output_type = self.output_type.lock().unwrap();
                Ok(output_type.to_value())
            }
            _ => unimplemented!(),
        }
    }

    // Called right after construction of a new instance
    fn constructed(&self, obj: &glib::Object) {
        // Call the parent class' ::constructed() implementation first
        self.parent_constructed(obj);

        // Here we actually add the pads we created in ProgressBin::new() to the
        // element so that GStreamer is aware of their existence.
        let bin = obj.downcast_ref::<gst::Bin>().unwrap();

        // Add the progressreport element to the bin.
        bin.add(&self.progress).unwrap();

        // Then set the ghost pad targets to the corresponding pads of the progressreport element.
        self.sinkpad
            .set_target(Some(&self.progress.get_static_pad("sink").unwrap()))
            .unwrap();
        self.srcpad
            .set_target(Some(&self.progress.get_static_pad("src").unwrap()))
            .unwrap();

        // And finally add the two ghostpads to the bin.
        bin.add_pad(&self.sinkpad).unwrap();
        bin.add_pad(&self.srcpad).unwrap();
    }
}

// Implementation of gst::Element virtual methods
impl ElementImpl for ProgressBin {}

// Implementation of gst::Bin virtual methods
impl BinImpl for ProgressBin {
    fn handle_message(&self, bin: &gst::Bin, msg: gst::Message) {
        use gst::MessageView;

        match msg.view() {
            // If this is the progressreport message, we print the status
            // to stdout. Otherwise we pass through to the default message
            // handling of the parent class, i.e. forwarding to the parent
            // bins and the application.
            MessageView::Element(ref msg)
                if msg.get_src().as_ref() == Some(self.progress.upcast_ref())
                    && msg
                        .get_structure()
                        .map(|s| s.get_name() == "progress")
                        .unwrap_or(false) =>
            {
                let s = msg.get_structure().unwrap();
                if let Ok(percent) = s.get_some::<f64>("percent-double") {
                    let output_type = self.output_type.lock().unwrap();
                    match *output_type {
                        ProgressBinOutput::Println => println!("progress: {:5.1}%", percent),
                        ProgressBinOutput::DebugCategory => {
                            gst_info!(CAT, "progress: {:5.1}%", percent);
                        }
                    };
                }
            }
            _ => self.parent_handle_message(bin, msg),
        }
    }
}

// Registers the type for our element, and then registers in GStreamer under
// the name "rsprogressbin" for being able to instantiate it via e.g.
// gst::ElementFactory::make().
pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
    gst::Element::register(
        Some(plugin),
        "rsprogressbin",
        gst::Rank::None,
        ProgressBin::get_type(),
    )
}
<commit_msg>tutorial/progressbin: Fix downcast type<commit_after>// Copyright (C) 2019 Sebastian Dröge <sebastian@centricular.com>
//
// 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.

use glib;
use glib::gobject_sys;
use glib::prelude::*;
use glib::subclass;
use glib::subclass::prelude::*;
use gst;
use gst::prelude::*;
use gst::subclass::prelude::*;
use std::sync::Mutex;

use once_cell::sync::Lazy;

// This enum may be used to control what type of output the progressbin should produce.
// It also serves the secondary purpose of illustrating how to add enum-type properties
// to a plugin written in rust.
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy, GEnum)]
#[repr(u32)]
#[genum(type_name = "GstProgressBinOutput")]
pub(crate) enum ProgressBinOutput {
    #[genum(
        name = "Println: Outputs the progress using a println! macro.",
        nick = "println"
    )]
    Println = 0,
    #[genum(
        name = "Debug Category: Outputs the progress as info logs under the element's debug category.",
        nick = "debug-category"
    )]
    DebugCategory = 1,
}

const DEFAULT_OUTPUT_TYPE: ProgressBinOutput = ProgressBinOutput::Println;

static CAT: Lazy<gst::DebugCategory> = Lazy::new(|| {
    gst::DebugCategory::new(
        "progressbin",
        gst::DebugColorFlags::empty(),
        Some("Rust Progress Reporter"),
    )
});

// Struct containing all the element data
struct ProgressBin {
    progress: gst::Element,
    srcpad: gst::GhostPad,
    sinkpad: gst::GhostPad,
    // We put the output_type property behind a mutex, as we want
    // change it in the set_property function, which can be called
    // from any thread.
    output_type: Mutex<ProgressBinOutput>,
}

// Metadata for the element's properties
static PROPERTIES: [subclass::Property; 1] = [subclass::Property("output", |name| {
    glib::ParamSpec::enum_(
        name,
        "Output",
        "Defines the output type of the progressbin",
        ProgressBinOutput::static_type(),
        DEFAULT_OUTPUT_TYPE as i32,
        glib::ParamFlags::READWRITE,
    )
})];

// This trait registers our type with the GObject object system and
// provides the entry points for creating a new instance and setting
// up the class data
impl ObjectSubclass for ProgressBin {
    const NAME: &'static str = "RsProgressBin";
    type ParentType = gst::Bin;
    type Instance = gst::subclass::ElementInstanceStruct<Self>;
    type Class = subclass::simple::ClassStruct<Self>;

    // This macro provides some boilerplate.
    glib_object_subclass!();

    // Called when a new instance is to be created. We need to return an instance
    // of our struct here and also get the class struct passed in case it's needed
    fn new_with_class(klass: &subclass::simple::ClassStruct<Self>) -> Self {
        // Create our two ghostpads from the templates that were registered with
        // the class. We don't provide a target for them yet because we can only
        // do so after the progressreport element was added to the bin.
        //
        // We do that and adding the pads inside glib::Object::constructed() later.
        let templ = klass.get_pad_template("sink").unwrap();
        let sinkpad = gst::GhostPad::new_no_target_from_template(Some("sink"), &templ).unwrap();
        let templ = klass.get_pad_template("src").unwrap();
        let srcpad = gst::GhostPad::new_no_target_from_template(Some("src"), &templ).unwrap();

        // Create the progressreport element.
        let progress = gst::ElementFactory::make("progressreport", Some("progress")).unwrap();
        // Don't let progressreport print to stdout itself
        progress.set_property("silent", &true).unwrap();

        // Return an instance of our struct
        Self {
            progress,
            srcpad,
            sinkpad,
            output_type: Mutex::new(ProgressBinOutput::Println),
        }
    }

    // Called exactly once when registering the type. Used for
    // setting up metadata for all instances, e.g. the name and
    // classification and the pad templates with their caps.
    //
    // Actual instances can create pads based on those pad templates
    // with a subset of the caps given here.
    fn class_init(klass: &mut subclass::simple::ClassStruct<Self>) {
        // Set the element specific metadata. This information is what
        // is visible from gst-inspect-1.0 and can also be programatically
        // retrieved from the gst::Registry after initial registration
        // without having to load the plugin in memory.
        klass.set_metadata(
            "ProgressBin",
            "Generic",
            "Prints progress information to stdout",
            "Sebastian Dröge <sebastian@centricular.com>",
        );

        // Create and add pad templates for our sink and source pad. These
        // are later used for actually creating the pads and beforehand
        // already provide information to GStreamer about all possible
        // pads that could exist for this type.

        // Our element can accept any possible caps on both pads
        let caps = gst::Caps::new_any();
        let src_pad_template = gst::PadTemplate::new(
            "src",
            gst::PadDirection::Src,
            gst::PadPresence::Always,
            &caps,
        )
        .unwrap();
        klass.add_pad_template(src_pad_template);

        let sink_pad_template = gst::PadTemplate::new(
            "sink",
            gst::PadDirection::Sink,
            gst::PadPresence::Always,
            &caps,
        )
        .unwrap();
        klass.add_pad_template(sink_pad_template);

        // Install all our properties
        klass.install_properties(&PROPERTIES);
    }
}

// Implementation of glib::Object virtual methods
impl ObjectImpl for ProgressBin {
    // This macro provides some boilerplate
    glib_object_impl!();

    // Called whenever a value of a property is changed. It can be called
    // at any time from any thread.
    fn set_property(&self, obj: &glib::Object, id: usize, value: &glib::Value) {
        let prop = &PROPERTIES[id];
        let element = obj.downcast_ref::<gst::Bin>().unwrap();

        match *prop {
            subclass::Property("output", ..) => {
                let mut output_type = self.output_type.lock().unwrap();
                let new_output_type = value
                    .get_some::<ProgressBinOutput>()
                    .expect("type checked upstream");
                gst_info!(
                    CAT,
                    obj: element,
                    "Changing output from {:?} to {:?}",
                    output_type,
                    new_output_type
                );
                *output_type = new_output_type;
            }
            _ => unimplemented!(),
        }
    }

    // Called whenever a value of a property is read. It can be called
    // at any time from any thread.
    fn get_property(&self, _obj: &glib::Object, id: usize) -> Result<glib::Value, ()> {
        let prop = &PROPERTIES[id];

        match *prop {
            subclass::Property("output", ..) => {
                let output_type = self.output_type.lock().unwrap();
                Ok(output_type.to_value())
            }
            _ => unimplemented!(),
        }
    }

    // Called right after construction of a new instance
    fn constructed(&self, obj: &glib::Object) {
        // Call the parent class' ::constructed() implementation first
        self.parent_constructed(obj);

        // Here we actually add the pads we created in ProgressBin::new() to the
        // element so that GStreamer is aware of their existence.
        let bin = obj.downcast_ref::<gst::Bin>().unwrap();

        // Add the progressreport element to the bin.
        bin.add(&self.progress).unwrap();

        // Then set the ghost pad targets to the corresponding pads of the progressreport element.
        self.sinkpad
            .set_target(Some(&self.progress.get_static_pad("sink").unwrap()))
            .unwrap();
        self.srcpad
            .set_target(Some(&self.progress.get_static_pad("src").unwrap()))
            .unwrap();

        // And finally add the two ghostpads to the bin.
        bin.add_pad(&self.sinkpad).unwrap();
        bin.add_pad(&self.srcpad).unwrap();
    }
}

// Implementation of gst::Element virtual methods
impl ElementImpl for ProgressBin {}

// Implementation of gst::Bin virtual methods
impl BinImpl for ProgressBin {
    fn handle_message(&self, bin: &gst::Bin, msg: gst::Message) {
        use gst::MessageView;

        match msg.view() {
            // If this is the progressreport message, we print the status
            // to stdout. Otherwise we pass through to the default message
            // handling of the parent class, i.e. forwarding to the parent
            // bins and the application.
            MessageView::Element(ref msg)
                if msg.get_src().as_ref() == Some(self.progress.upcast_ref())
                    && msg
                        .get_structure()
                        .map(|s| s.get_name() == "progress")
                        .unwrap_or(false) =>
            {
                let s = msg.get_structure().unwrap();
                if let Ok(percent) = s.get_some::<f64>("percent-double") {
                    let output_type = self.output_type.lock().unwrap();
                    match *output_type {
                        ProgressBinOutput::Println => println!("progress: {:5.1}%", percent),
                        ProgressBinOutput::DebugCategory => {
                            gst_info!(CAT, "progress: {:5.1}%", percent);
                        }
                    };
                }
            }
            _ => self.parent_handle_message(bin, msg),
        }
    }
}

// Registers the type for our element, and then registers in GStreamer under
// the name "rsprogressbin" for being able to instantiate it via e.g.
// gst::ElementFactory::make().
pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
    gst::Element::register(
        Some(plugin),
        "rsprogressbin",
        gst::Rank::None,
        ProgressBin::get_type(),
    )
}
<|endoftext|>


<commit_before>// Working with flag subcommands allows behavior similar to the popular Archlinux package manager Pacman.
// Man page: https://jlk.fjfi.cvut.cz/arch/manpages/man/pacman.8
//
// It's suggested that you read examples/20_subcommands.rs prior to learning about `FlagSubCommand`s
//
// This differs from normal subcommands because it allows passing subcommands in the same fashion as `clap::Arg` in short or long args.
//
//            Top Level App (pacman)                              TOP
//                           |
//    ---------------------------------------------------
//   /     |        |        |         |        \        \
// sync  database remove    files      query   deptest   upgrade  LEVEL 1
//
// Given the above hierachy, valid runtime uses would be (not an all inclusive list):
//
// $ pacman -Ss
//           ^--- subcommand followed by an arg in its scope.
//
// $ pacman -Qs
//
// $ pacman -Rns
//
// NOTE: Subcommands short flags can be uppercase or lowercase.
//
// $ pacman --sync --search
//            ^--- subcommand
//
// $ pacman sync -s
//          ^--- subcommand
//
// NOTE: this isn't valid for pacman, but is done implicitly by Clap which
// adds support for both flags and standard subcommands out of the box.
// Allowing your users to make the choice of what feels more intuitive for them.
//
// Notice only one command per "level" may be used. You could not, for example, do:
//
// $ pacman -SQR
//
// It's also important to know that subcommands each have their own set of matches and may have args
// with the same name as other subcommands in a different part of the tree heirachy (i.e. the arg
// names aren't in a flat namespace).
//
// In order to use subcommands in clap, you only need to know which subcommand you're at in your
// tree, and which args are defined on that subcommand.
//
// Let's make a quick program to illustrate. We'll be using the same example as above but for
// brevity sake we won't implement all of the subcommands, only a few.
use clap::{App, AppSettings, Arg};

fn main() {
    let matches = App::new("pacman")
        .about("package manager utility")
        .version("5.2.1")
        .setting(AppSettings::SubcommandRequiredElseHelp)
        .author("Pacman Development Team")
        // Query subcommand
        //
        // Only a few of its arguments are implemented below.
        .subcommand(
            App::new("query")
                .short_flag('Q')
                .long_flag("query")
                .about("Query the package database.")
                .arg(
                    Arg::new("search")
                        .short('s')
                        .long("search")
                        .about("search locally-installed packages for matching strings")
                        .multiple(true)
                        .takes_value(true),
                )
                .arg(
                    Arg::new("info")
                        .long("info")
                        .short('i')
                        .about("view package information (-ii for backup files)")
                        .multiple(true),
                ),
        )
        // Sync subcommand
        //
        // Only a few of its arguments are implemented below.
        .subcommand(
            App::new("sync")
                .short_flag('S')
                .long_flag("sync")
                .about("Synchronize packages.")
                .arg(
                    Arg::new("search")
                        .short('s')
                        .long("search")
                        .about("search remote repositories for matching strings")
                        .multiple(true)
                        .takes_value(true),
                )
                .arg(
                    Arg::new("info")
                        .long("info")
                        .short('i')
                        .about("view package information (-ii for extended information)")
                        .multiple(true),
                )
                .arg(
                    Arg::new("package")
                        .about("package")
                        .multiple(true)
                        .takes_value(true),
                ),
        )
        .get_matches();

    match matches.subcommand() {
        ("sync", Some(sync_matches)) => {
            if sync_matches.is_present("info") {
                // Values required here, so it's safe to unwrap
                let packages: Vec<_> = sync_matches.values_of("info").unwrap().collect();
                let comma_sep = packages.join(", ");
                println!("Retrieving info for {}...", comma_sep);
            } else if sync_matches.is_present("search") {
                // Values required here, so it's safe to unwrap
                let queries: Vec<_> = sync_matches.values_of("search").unwrap().collect();
                let comma_sep = queries.join(", ");
                println!("Searching for {}...", comma_sep);
            } else {
                // Sync was called without any arguments
                match sync_matches.values_of("package") {
                    Some(packages) => {
                        let pkgs: Vec<_> = packages.collect();
                        let comma_sep = pkgs.join(", ");
                        println!("Installing {}...", comma_sep);
                    }
                    None => panic!("No targets specified (use -h for help)"),
                }
            }
        }
        ("query", Some(query_matches)) => {
            if query_matches.is_present("info") {
                // Values required here, so it's safe to unwrap
                let packages: Vec<_> = query_matches.values_of("info").unwrap().collect();
                let comma_sep = packages.join(", ");
                println!("Retrieving info for {}...", comma_sep);
            } else if query_matches.is_present("search") {
                // Values required here, so it's safe to unwrap
                let queries: Vec<_> = query_matches.values_of("search").unwrap().collect();
                let comma_sep = queries.join(", ");
                println!("Searching Locally for {}...", comma_sep);
            } else {
                // Query was called without any arguments
                println!("Displaying all locally installed packages...");
            }
        }
        ("", None) => panic!("error: no operation specified (use -h for help)"), // If no subcommand was used
        _ => unreachable!(), // If all subcommands are defined above, anything else is unreachable
    }
}
<commit_msg>example: more pacman-like and refractor<commit_after>// Working with flag subcommands allows behavior similar to the popular Archlinux package manager Pacman.
// Man page: https://jlk.fjfi.cvut.cz/arch/manpages/man/pacman.8
//
// It's suggested that you read examples/20_subcommands.rs prior to learning about `FlagSubCommand`s
//
// This differs from normal subcommands because it allows passing subcommands in the same fashion as `clap::Arg` in short or long args.
//
//            Top Level App (pacman)                              TOP
//                           |
//    ---------------------------------------------------
//   /     |        |        |         |        \        \
// sync  database remove    files      query   deptest   upgrade  LEVEL 1
//
// Given the above hierachy, valid runtime uses would be (not an all inclusive list):
//
// $ pacman -Ss
//           ^--- subcommand followed by an arg in its scope.
//
// $ pacman -Qs
//
// $ pacman -Rns
//
// NOTE: Subcommands short flags can be uppercase or lowercase.
//
// $ pacman --sync --search
//            ^--- subcommand
//
// $ pacman sync -s
//          ^--- subcommand
//
// NOTE: this isn't valid for pacman, but is done implicitly by Clap which
// adds support for both flags and standard subcommands out of the box.
// Allowing your users to make the choice of what feels more intuitive for them.
//
// Notice only one command per "level" may be used. You could not, for example, do:
//
// $ pacman -SQR
//
// It's also important to know that subcommands each have their own set of matches and may have args
// with the same name as other subcommands in a different part of the tree heirachy (i.e. the arg
// names aren't in a flat namespace).
//
// In order to use subcommands in clap, you only need to know which subcommand you're at in your
// tree, and which args are defined on that subcommand.
//
// Let's make a quick program to illustrate. We'll be using the same example as above but for
// brevity sake we won't implement all of the subcommands, only a few.
use clap::{App, AppSettings, Arg};

fn main() {
    let matches = App::new("pacman")
        .about("package manager utility")
        .version("5.2.1")
        .setting(AppSettings::SubcommandRequiredElseHelp)
        .author("Pacman Development Team")
        // Query subcommand
        //
        // Only a few of its arguments are implemented below.
        .subcommand(
            App::new("query")
                .short_flag('Q')
                .long_flag("query")
                .about("Query the package database.")
                .arg(
                    Arg::new("search")
                        .short('s')
                        .long("search")
                        .about("search locally-installed packages for matching strings")
                        .conflicts_with("info")
                        .multiple_values(true),
                )
                .arg(
                    Arg::new("info")
                        .long("info")
                        .short('i')
                        .conflicts_with("search")
                        .about("view package information")
                        .multiple_values(true),
                ),
        )
        // Sync subcommand
        //
        // Only a few of its arguments are implemented below.
        .subcommand(
            App::new("sync")
                .short_flag('S')
                .long_flag("sync")
                .about("Synchronize packages.")
                .arg(
                    Arg::new("search")
                        .short('s')
                        .long("search")
                        .conflicts_with("info")
                        .takes_value(true)
                        .multiple_values(true)
                        .about("search remote repositories for matching strings"),
                )
                .arg(
                    Arg::new("info")
                        .long("info")
                        .conflicts_with("search")
                        .short('i')
                        .about("view package information"),
                )
                .arg(
                    Arg::new("package")
                        .about("packages")
                        .multiple(true)
                        .required_unless_one(&["search"])
                        .takes_value(true),
                ),
        )
        .get_matches();

    match matches.subcommand() {
        ("sync", Some(sync_matches)) => {
            if sync_matches.is_present("search") {
                let packages: Vec<_> = sync_matches.values_of("search").unwrap().collect();
                let values = packages.join(", ");
                println!("Searching for {}...", values);
                return;
            }

            let packages: Vec<_> = sync_matches.values_of("package").unwrap().collect();
            let values = packages.join(", ");

            if sync_matches.is_present("info") {
                println!("Retrieving info for {}...", values);
            } else {
                println!("Installing {}...", values);
            }
        }
        ("query", Some(query_matches)) => {
            if let Some(packages) = query_matches.values_of("info") {
                let comma_sep = packages.collect::<Vec<_>>().join(", ");
                println!("Retrieving info for {}...", comma_sep);
            } else if let Some(queries) = query_matches.values_of("search") {
                let comma_sep = queries.collect::<Vec<_>>().join(", ");
                println!("Searching Locally for {}...", comma_sep);
            } else {
                println!("Displaying all locally installed packages...");
            }
        }
        _ => unreachable!(), // If all subcommands are defined above, anything else is unreachable
    }
}
<|endoftext|>