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

use cssparser::{self, SourceLocation};
use html5ever::{Namespace as NsAtom};
use media_queries::CSSErrorReporterTest;
use parking_lot::RwLock;
use selectors::attr::*;
use selectors::parser::*;
use servo_arc::Arc;
use servo_atoms::Atom;
use servo_url::ServoUrl;
use std::borrow::ToOwned;
use std::sync::Mutex;
use std::sync::atomic::AtomicBool;
use style::context::QuirksMode;
use style::error_reporting::{ParseErrorReporter, ContextualParseError};
use style::media_queries::MediaList;
use style::properties::Importance;
use style::properties::{CSSWideKeyword, DeclaredValueOwned, PropertyDeclaration, PropertyDeclarationBlock};
use style::properties::longhands;
use style::properties::longhands::animation_timing_function;
use style::shared_lock::SharedRwLock;
use style::stylesheets::{Origin, Namespaces};
use style::stylesheets::{Stylesheet, StylesheetContents, NamespaceRule, CssRule, CssRules, StyleRule, KeyframesRule};
use style::stylesheets::keyframes_rule::{Keyframe, KeyframeSelector, KeyframePercentage};
use style::values::{KeyframesName, CustomIdent};
use style::values::computed::Percentage;
use style::values::specified::{LengthOrPercentageOrAuto, PositionComponent};
use style::values::specified::transform::TimingFunction;

pub fn block_from<I>(iterable: I) -> PropertyDeclarationBlock
where I: IntoIterator<Item=(PropertyDeclaration, Importance)> {
    let mut block = PropertyDeclarationBlock::new();
    for (d, i) in iterable {
        block.push(d, i)
    }
    block
}

#[test]
fn test_parse_stylesheet() {
    let css = r"
        @namespace url(http://www.w3.org/1999/xhtml);
        /* FIXME: only if scripting is enabled */
        input[type=hidden i] {
            display: block !important;
            display: none !important;
            display: inline;
            --a: b !important;
            --a: inherit !important;
            --a: c;
        }
        html , body /**/ {
            display: none;
            display: block;
        }
        #d1 > .ok { background: blue; }
        @keyframes foo {
            from { width: 0% }
            to {
                width: 100%;
                width: 50% !important; /* !important not allowed here */
                animation-name: 'foo'; /* animation properties not allowed here */
                animation-timing-function: ease; /* … except animation-timing-function */
            }
        }";
    let url = ServoUrl::parse("about::test").unwrap();
    let lock = SharedRwLock::new();
    let media = Arc::new(lock.wrap(MediaList::empty()));
    let stylesheet = Stylesheet::from_str(css, url.clone(), Origin::UserAgent, media, lock,
                                          None, &CSSErrorReporterTest, QuirksMode::NoQuirks, 0);
    let mut namespaces = Namespaces::default();
    namespaces.default = Some((ns!(html), ()));
    let expected = Stylesheet {
        contents: StylesheetContents {
            origin: Origin::UserAgent,
            namespaces: RwLock::new(namespaces),
            url_data: RwLock::new(url),
            quirks_mode: QuirksMode::NoQuirks,
            rules: CssRules::new(vec![
                CssRule::Namespace(Arc::new(stylesheet.shared_lock.wrap(NamespaceRule {
                    prefix: None,
                    url: NsAtom::from("http://www.w3.org/1999/xhtml"),
                    source_location: SourceLocation {
                        line: 1,
                        column: 19,
                    },
                }))),
                CssRule::Style(Arc::new(stylesheet.shared_lock.wrap(StyleRule {
                    selectors: SelectorList::from_vec(vec!(
                        Selector::from_vec(vec!(
                            Component::DefaultNamespace(NsAtom::from("http://www.w3.org/1999/xhtml")),
                            Component::LocalName(LocalName {
                                name: local_name!("input"),
                                lower_name: local_name!("input"),
                            }),
                            Component::AttributeInNoNamespace {
                                local_name: local_name!("type"),
                                local_name_lower: local_name!("type"),
                                operator: AttrSelectorOperator::Equal,
                                value: "hidden".to_owned(),
                                case_sensitivity: ParsedCaseSensitivity::AsciiCaseInsensitive,
                                never_matches: false,
                            }
                        ), (0 << 20) + (1 << 10) + (1 << 0))
                    )),
                    block: Arc::new(stylesheet.shared_lock.wrap(block_from(vec![
                        (PropertyDeclaration::Display(longhands::display::SpecifiedValue::none),
                         Importance::Important),
                        (PropertyDeclaration::Custom(Atom::from("a"),
                         DeclaredValueOwned::CSSWideKeyword(CSSWideKeyword::Inherit)),
                         Importance::Important),
                    ]))),
                    source_location: SourceLocation {
                        line: 3,
                        column: 9,
                    },
                }))),
                CssRule::Style(Arc::new(stylesheet.shared_lock.wrap(StyleRule {
                    selectors: SelectorList::from_vec(vec!(
                        Selector::from_vec(vec!(
                                Component::DefaultNamespace(NsAtom::from("http://www.w3.org/1999/xhtml")),
                                Component::LocalName(LocalName {
                                    name: local_name!("html"),
                                    lower_name: local_name!("html"),
                                }),
                            ), (0 << 20) + (0 << 10) + (1 << 0)),
                        Selector::from_vec(vec!(
                            Component::DefaultNamespace(NsAtom::from("http://www.w3.org/1999/xhtml")),
                            Component::LocalName(LocalName {
                                name: local_name!("body"),
                                lower_name: local_name!("body"),
                            })
                            ), (0 << 20) + (0 << 10) + (1 << 0)
                        ),
                    )),
                    block: Arc::new(stylesheet.shared_lock.wrap(block_from(vec![
                        (PropertyDeclaration::Display(longhands::display::SpecifiedValue::block),
                         Importance::Normal),
                    ]))),
                    source_location: SourceLocation {
                        line: 11,
                        column: 9,
                    },
                }))),
                CssRule::Style(Arc::new(stylesheet.shared_lock.wrap(StyleRule {
                    selectors: SelectorList::from_vec(vec!(
                        Selector::from_vec(vec!(
                            Component::DefaultNamespace(NsAtom::from("http://www.w3.org/1999/xhtml")),
                            Component::ID(Atom::from("d1")),
                            Component::Combinator(Combinator::Child),
                            Component::DefaultNamespace(NsAtom::from("http://www.w3.org/1999/xhtml")),
                            Component::Class(Atom::from("ok"))
                        ), (1 << 20) + (1 << 10) + (0 << 0))
                    )),
                    block: Arc::new(stylesheet.shared_lock.wrap(block_from(vec![
                        (PropertyDeclaration::BackgroundColor(
                            longhands::background_color::SpecifiedValue::Numeric {
                                authored: Some("blue".to_owned().into_boxed_str()),
                                parsed: cssparser::RGBA::new(0, 0, 255, 255),
                            }
                         ),
                         Importance::Normal),
                        (PropertyDeclaration::BackgroundPositionX(
                            longhands::background_position_x::SpecifiedValue(
                            vec![PositionComponent::zero()])),
                         Importance::Normal),
                        (PropertyDeclaration::BackgroundPositionY(
                            longhands::background_position_y::SpecifiedValue(
                            vec![PositionComponent::zero()])),
                         Importance::Normal),
                        (PropertyDeclaration::BackgroundRepeat(
                            longhands::background_repeat::SpecifiedValue(
                            vec![longhands::background_repeat::single_value
                                                       ::get_initial_specified_value()])),
                         Importance::Normal),
                        (PropertyDeclaration::BackgroundAttachment(
                            longhands::background_attachment::SpecifiedValue(
                            vec![longhands::background_attachment::single_value
                                                       ::get_initial_specified_value()])),
                         Importance::Normal),
                        (PropertyDeclaration::BackgroundImage(
                            longhands::background_image::SpecifiedValue(
                            vec![longhands::background_image::single_value
                                                       ::get_initial_specified_value()])),
                         Importance::Normal),
                        (PropertyDeclaration::BackgroundSize(
                            longhands::background_size::SpecifiedValue(
                            vec![longhands::background_size::single_value
                                                       ::get_initial_specified_value()])),
                         Importance::Normal),
                        (PropertyDeclaration::BackgroundOrigin(
                            longhands::background_origin::SpecifiedValue(
                            vec![longhands::background_origin::single_value
                                                       ::get_initial_specified_value()])),
                         Importance::Normal),
                        (PropertyDeclaration::BackgroundClip(
                            longhands::background_clip::SpecifiedValue(
                            vec![longhands::background_clip::single_value
                                                       ::get_initial_specified_value()])),
                         Importance::Normal),
                    ]))),
                    source_location: SourceLocation {
                        line: 15,
                        column: 9,
                    },
                }))),
                CssRule::Keyframes(Arc::new(stylesheet.shared_lock.wrap(KeyframesRule {
                    name: KeyframesName::Ident(CustomIdent("foo".into())),
                    keyframes: vec![
                        Arc::new(stylesheet.shared_lock.wrap(Keyframe {
                            selector: KeyframeSelector::new_for_unit_testing(
                                          vec![KeyframePercentage::new(0.)]),
                            block: Arc::new(stylesheet.shared_lock.wrap(block_from(vec![
                                (PropertyDeclaration::Width(
                                    LengthOrPercentageOrAuto::Percentage(Percentage(0.))),
                                 Importance::Normal),
                            ]))),
                            source_location: SourceLocation {
                                line: 17,
                                column: 13,
                            },
                        })),
                        Arc::new(stylesheet.shared_lock.wrap(Keyframe {
                            selector: KeyframeSelector::new_for_unit_testing(
                                          vec![KeyframePercentage::new(1.)]),
                            block: Arc::new(stylesheet.shared_lock.wrap(block_from(vec![
                                (PropertyDeclaration::Width(
                                    LengthOrPercentageOrAuto::Percentage(Percentage(1.))),
                                 Importance::Normal),
                                (PropertyDeclaration::AnimationTimingFunction(
                                    animation_timing_function::SpecifiedValue(
                                        vec![TimingFunction::ease()])),
                                 Importance::Normal),
                            ]))),
                            source_location: SourceLocation {
                                line: 18,
                                column: 13,
                            },
                        })),
                    ],
                    vendor_prefix: None,
                    source_location: SourceLocation {
                        line: 16,
                        column: 19,
                    },
                })))
            ], &stylesheet.shared_lock),
            source_map_url: RwLock::new(None),
            source_url: RwLock::new(None),
        },
        media: Arc::new(stylesheet.shared_lock.wrap(MediaList::empty())),
        shared_lock: stylesheet.shared_lock.clone(),
        disabled: AtomicBool::new(false),
    };

    assert_eq!(format!("{:#?}", stylesheet), format!("{:#?}", expected));
}

struct CSSError {
    pub url : ServoUrl,
    pub line: u32,
    pub column: u32,
    pub message: String
}

struct CSSInvalidErrorReporterTest {
    pub errors: Arc<Mutex<Vec<CSSError>>>
}

impl CSSInvalidErrorReporterTest {
    pub fn new() -> CSSInvalidErrorReporterTest {
        return CSSInvalidErrorReporterTest{
            errors: Arc::new(Mutex::new(Vec::new()))
        }
    }
}

impl ParseErrorReporter for CSSInvalidErrorReporterTest {
    fn report_error(&self,
                    url: &ServoUrl,
                    location: SourceLocation,
                    error: ContextualParseError) {
        let mut errors = self.errors.lock().unwrap();
        errors.push(
            CSSError{
                url: url.clone(),
                line: location.line,
                column: location.column,
                message: error.to_string(),
            }
        );
    }
}

#[test]
fn test_report_error_stylesheet() {
    let css = r"
    div {
        background-color: red;
        display: invalid;
        invalid: true;
    }
    ";
    let url = ServoUrl::parse("about::test").unwrap();
    let error_reporter = CSSInvalidErrorReporterTest::new();

    let errors = error_reporter.errors.clone();

    let lock = SharedRwLock::new();
    let media = Arc::new(lock.wrap(MediaList::empty()));
    Stylesheet::from_str(css, url.clone(), Origin::UserAgent, media, lock,
                         None, &error_reporter, QuirksMode::NoQuirks, 5);

    let mut errors = errors.lock().unwrap();

    let error = errors.pop().unwrap();
    assert_eq!("Unsupported property declaration: 'invalid: true;', \
                Custom(PropertyDeclaration(UnknownProperty(\"invalid\")))", error.message);
    assert_eq!(9, error.line);
    assert_eq!(9, error.column);

    let error = errors.pop().unwrap();
    assert_eq!("Unsupported property declaration: 'display: invalid;', \
                Custom(PropertyDeclaration(InvalidValue(\"display\", None)))", error.message);
    assert_eq!(8, error.line);
    assert_eq!(9, error.column);

    // testing for the url
    assert_eq!(url, error.url);
}

#[test]
fn test_no_report_unrecognized_vendor_properties() {
    let css = r"
    div {
        -o-background-color: red;
        _background-color: red;
        -moz-background-color: red;
    }
    ";
    let url = ServoUrl::parse("about::test").unwrap();
    let error_reporter = CSSInvalidErrorReporterTest::new();

    let errors = error_reporter.errors.clone();

    let lock = SharedRwLock::new();
    let media = Arc::new(lock.wrap(MediaList::empty()));
    Stylesheet::from_str(css, url, Origin::UserAgent, media, lock,
                         None, &error_reporter, QuirksMode::NoQuirks, 0);

    let mut errors = errors.lock().unwrap();
    let error = errors.pop().unwrap();
    assert_eq!("Unsupported property declaration: '-moz-background-color: red;', \
                Custom(PropertyDeclaration(UnknownProperty(\"-moz-background-color\")))",
               error.message);
    assert!(errors.is_empty());
}

#[test]
fn test_source_map_url() {
    let tests = vec![
        ("", None),
        ("/*# sourceMappingURL=something */", Some("something".to_string())),
    ];

    for test in tests {
        let url = ServoUrl::parse("about::test").unwrap();
        let lock = SharedRwLock::new();
        let media = Arc::new(lock.wrap(MediaList::empty()));
        let stylesheet = Stylesheet::from_str(test.0, url.clone(), Origin::UserAgent, media, lock,
                                              None, &CSSErrorReporterTest, QuirksMode::NoQuirks,
                                              0);
        let url_opt = stylesheet.contents.source_map_url.read();
        assert_eq!(*url_opt, test.1);
    }
}

#[test]
fn test_source_url() {
    let tests = vec![
        ("", None),
        ("/*# sourceURL=something */", Some("something".to_string())),
    ];

    for test in tests {
        let url = ServoUrl::parse("about::test").unwrap();
        let lock = SharedRwLock::new();
        let media = Arc::new(lock.wrap(MediaList::empty()));
        let stylesheet = Stylesheet::from_str(test.0, url.clone(), Origin::UserAgent, media, lock,
                                              None, &CSSErrorReporterTest, QuirksMode::NoQuirks,
                                              0);
        let url_opt = stylesheet.contents.source_url.read();
        assert_eq!(*url_opt, test.1);
    }
}
<commit_msg>Use arrays for error reporting test expected values<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use cssparser::{self, SourceLocation};
use html5ever::{Namespace as NsAtom};
use media_queries::CSSErrorReporterTest;
use parking_lot::RwLock;
use selectors::attr::*;
use selectors::parser::*;
use servo_arc::Arc;
use servo_atoms::Atom;
use servo_url::ServoUrl;
use std::borrow::ToOwned;
use std::cell::RefCell;
use std::sync::atomic::AtomicBool;
use style::context::QuirksMode;
use style::error_reporting::{ParseErrorReporter, ContextualParseError};
use style::media_queries::MediaList;
use style::properties::Importance;
use style::properties::{CSSWideKeyword, DeclaredValueOwned, PropertyDeclaration, PropertyDeclarationBlock};
use style::properties::longhands;
use style::properties::longhands::animation_timing_function;
use style::shared_lock::SharedRwLock;
use style::stylesheets::{Origin, Namespaces};
use style::stylesheets::{Stylesheet, StylesheetContents, NamespaceRule, CssRule, CssRules, StyleRule, KeyframesRule};
use style::stylesheets::keyframes_rule::{Keyframe, KeyframeSelector, KeyframePercentage};
use style::values::{KeyframesName, CustomIdent};
use style::values::computed::Percentage;
use style::values::specified::{LengthOrPercentageOrAuto, PositionComponent};
use style::values::specified::transform::TimingFunction;

pub fn block_from<I>(iterable: I) -> PropertyDeclarationBlock
where I: IntoIterator<Item=(PropertyDeclaration, Importance)> {
    let mut block = PropertyDeclarationBlock::new();
    for (d, i) in iterable {
        block.push(d, i)
    }
    block
}

#[test]
fn test_parse_stylesheet() {
    let css = r"
        @namespace url(http://www.w3.org/1999/xhtml);
        /* FIXME: only if scripting is enabled */
        input[type=hidden i] {
            display: block !important;
            display: none !important;
            display: inline;
            --a: b !important;
            --a: inherit !important;
            --a: c;
        }
        html , body /**/ {
            display: none;
            display: block;
        }
        #d1 > .ok { background: blue; }
        @keyframes foo {
            from { width: 0% }
            to {
                width: 100%;
                width: 50% !important; /* !important not allowed here */
                animation-name: 'foo'; /* animation properties not allowed here */
                animation-timing-function: ease; /* … except animation-timing-function */
            }
        }";
    let url = ServoUrl::parse("about::test").unwrap();
    let lock = SharedRwLock::new();
    let media = Arc::new(lock.wrap(MediaList::empty()));
    let stylesheet = Stylesheet::from_str(css, url.clone(), Origin::UserAgent, media, lock,
                                          None, &CSSErrorReporterTest, QuirksMode::NoQuirks, 0);
    let mut namespaces = Namespaces::default();
    namespaces.default = Some((ns!(html), ()));
    let expected = Stylesheet {
        contents: StylesheetContents {
            origin: Origin::UserAgent,
            namespaces: RwLock::new(namespaces),
            url_data: RwLock::new(url),
            quirks_mode: QuirksMode::NoQuirks,
            rules: CssRules::new(vec![
                CssRule::Namespace(Arc::new(stylesheet.shared_lock.wrap(NamespaceRule {
                    prefix: None,
                    url: NsAtom::from("http://www.w3.org/1999/xhtml"),
                    source_location: SourceLocation {
                        line: 1,
                        column: 19,
                    },
                }))),
                CssRule::Style(Arc::new(stylesheet.shared_lock.wrap(StyleRule {
                    selectors: SelectorList::from_vec(vec!(
                        Selector::from_vec(vec!(
                            Component::DefaultNamespace(NsAtom::from("http://www.w3.org/1999/xhtml")),
                            Component::LocalName(LocalName {
                                name: local_name!("input"),
                                lower_name: local_name!("input"),
                            }),
                            Component::AttributeInNoNamespace {
                                local_name: local_name!("type"),
                                local_name_lower: local_name!("type"),
                                operator: AttrSelectorOperator::Equal,
                                value: "hidden".to_owned(),
                                case_sensitivity: ParsedCaseSensitivity::AsciiCaseInsensitive,
                                never_matches: false,
                            }
                        ), (0 << 20) + (1 << 10) + (1 << 0))
                    )),
                    block: Arc::new(stylesheet.shared_lock.wrap(block_from(vec![
                        (PropertyDeclaration::Display(longhands::display::SpecifiedValue::none),
                         Importance::Important),
                        (PropertyDeclaration::Custom(Atom::from("a"),
                         DeclaredValueOwned::CSSWideKeyword(CSSWideKeyword::Inherit)),
                         Importance::Important),
                    ]))),
                    source_location: SourceLocation {
                        line: 3,
                        column: 9,
                    },
                }))),
                CssRule::Style(Arc::new(stylesheet.shared_lock.wrap(StyleRule {
                    selectors: SelectorList::from_vec(vec!(
                        Selector::from_vec(vec!(
                                Component::DefaultNamespace(NsAtom::from("http://www.w3.org/1999/xhtml")),
                                Component::LocalName(LocalName {
                                    name: local_name!("html"),
                                    lower_name: local_name!("html"),
                                }),
                            ), (0 << 20) + (0 << 10) + (1 << 0)),
                        Selector::from_vec(vec!(
                            Component::DefaultNamespace(NsAtom::from("http://www.w3.org/1999/xhtml")),
                            Component::LocalName(LocalName {
                                name: local_name!("body"),
                                lower_name: local_name!("body"),
                            })
                            ), (0 << 20) + (0 << 10) + (1 << 0)
                        ),
                    )),
                    block: Arc::new(stylesheet.shared_lock.wrap(block_from(vec![
                        (PropertyDeclaration::Display(longhands::display::SpecifiedValue::block),
                         Importance::Normal),
                    ]))),
                    source_location: SourceLocation {
                        line: 11,
                        column: 9,
                    },
                }))),
                CssRule::Style(Arc::new(stylesheet.shared_lock.wrap(StyleRule {
                    selectors: SelectorList::from_vec(vec!(
                        Selector::from_vec(vec!(
                            Component::DefaultNamespace(NsAtom::from("http://www.w3.org/1999/xhtml")),
                            Component::ID(Atom::from("d1")),
                            Component::Combinator(Combinator::Child),
                            Component::DefaultNamespace(NsAtom::from("http://www.w3.org/1999/xhtml")),
                            Component::Class(Atom::from("ok"))
                        ), (1 << 20) + (1 << 10) + (0 << 0))
                    )),
                    block: Arc::new(stylesheet.shared_lock.wrap(block_from(vec![
                        (PropertyDeclaration::BackgroundColor(
                            longhands::background_color::SpecifiedValue::Numeric {
                                authored: Some("blue".to_owned().into_boxed_str()),
                                parsed: cssparser::RGBA::new(0, 0, 255, 255),
                            }
                         ),
                         Importance::Normal),
                        (PropertyDeclaration::BackgroundPositionX(
                            longhands::background_position_x::SpecifiedValue(
                            vec![PositionComponent::zero()])),
                         Importance::Normal),
                        (PropertyDeclaration::BackgroundPositionY(
                            longhands::background_position_y::SpecifiedValue(
                            vec![PositionComponent::zero()])),
                         Importance::Normal),
                        (PropertyDeclaration::BackgroundRepeat(
                            longhands::background_repeat::SpecifiedValue(
                            vec![longhands::background_repeat::single_value
                                                       ::get_initial_specified_value()])),
                         Importance::Normal),
                        (PropertyDeclaration::BackgroundAttachment(
                            longhands::background_attachment::SpecifiedValue(
                            vec![longhands::background_attachment::single_value
                                                       ::get_initial_specified_value()])),
                         Importance::Normal),
                        (PropertyDeclaration::BackgroundImage(
                            longhands::background_image::SpecifiedValue(
                            vec![longhands::background_image::single_value
                                                       ::get_initial_specified_value()])),
                         Importance::Normal),
                        (PropertyDeclaration::BackgroundSize(
                            longhands::background_size::SpecifiedValue(
                            vec![longhands::background_size::single_value
                                                       ::get_initial_specified_value()])),
                         Importance::Normal),
                        (PropertyDeclaration::BackgroundOrigin(
                            longhands::background_origin::SpecifiedValue(
                            vec![longhands::background_origin::single_value
                                                       ::get_initial_specified_value()])),
                         Importance::Normal),
                        (PropertyDeclaration::BackgroundClip(
                            longhands::background_clip::SpecifiedValue(
                            vec![longhands::background_clip::single_value
                                                       ::get_initial_specified_value()])),
                         Importance::Normal),
                    ]))),
                    source_location: SourceLocation {
                        line: 15,
                        column: 9,
                    },
                }))),
                CssRule::Keyframes(Arc::new(stylesheet.shared_lock.wrap(KeyframesRule {
                    name: KeyframesName::Ident(CustomIdent("foo".into())),
                    keyframes: vec![
                        Arc::new(stylesheet.shared_lock.wrap(Keyframe {
                            selector: KeyframeSelector::new_for_unit_testing(
                                          vec![KeyframePercentage::new(0.)]),
                            block: Arc::new(stylesheet.shared_lock.wrap(block_from(vec![
                                (PropertyDeclaration::Width(
                                    LengthOrPercentageOrAuto::Percentage(Percentage(0.))),
                                 Importance::Normal),
                            ]))),
                            source_location: SourceLocation {
                                line: 17,
                                column: 13,
                            },
                        })),
                        Arc::new(stylesheet.shared_lock.wrap(Keyframe {
                            selector: KeyframeSelector::new_for_unit_testing(
                                          vec![KeyframePercentage::new(1.)]),
                            block: Arc::new(stylesheet.shared_lock.wrap(block_from(vec![
                                (PropertyDeclaration::Width(
                                    LengthOrPercentageOrAuto::Percentage(Percentage(1.))),
                                 Importance::Normal),
                                (PropertyDeclaration::AnimationTimingFunction(
                                    animation_timing_function::SpecifiedValue(
                                        vec![TimingFunction::ease()])),
                                 Importance::Normal),
                            ]))),
                            source_location: SourceLocation {
                                line: 18,
                                column: 13,
                            },
                        })),
                    ],
                    vendor_prefix: None,
                    source_location: SourceLocation {
                        line: 16,
                        column: 19,
                    },
                })))
            ], &stylesheet.shared_lock),
            source_map_url: RwLock::new(None),
            source_url: RwLock::new(None),
        },
        media: Arc::new(stylesheet.shared_lock.wrap(MediaList::empty())),
        shared_lock: stylesheet.shared_lock.clone(),
        disabled: AtomicBool::new(false),
    };

    assert_eq!(format!("{:#?}", stylesheet), format!("{:#?}", expected));
}

#[derive(Debug)]
struct CSSError {
    pub url : ServoUrl,
    pub line: u32,
    pub column: u32,
    pub message: String
}

struct TestingErrorReporter {
    errors: RefCell<Vec<CSSError>>,
}

impl TestingErrorReporter {
    pub fn new() -> Self {
        TestingErrorReporter {
            errors: RefCell::new(Vec::new()),
        }
    }

    fn assert_messages_contain(&self, expected_errors: &[(u32, u32, &str)]) {
        let errors = self.errors.borrow();
        for (i, (error, &(line, column, message))) in errors.iter().zip(expected_errors).enumerate() {
            assert_eq!((error.line, error.column), (line, column),
                       "line/column numbers of the {}th error", i + 1);
            assert!(error.message.contains(message),
                    "{:?} does not contain {:?}", error.message, message);
        }
        if errors.len() < expected_errors.len() {
            panic!("Missing errors: {:?}", &expected_errors[errors.len()..]);
        }
        if errors.len() > expected_errors.len() {
            panic!("Extra errors: {:?}", &errors[expected_errors.len()..]);
        }
    }
}

impl ParseErrorReporter for TestingErrorReporter {
    fn report_error(&self,
                    url: &ServoUrl,
                    location: SourceLocation,
                    error: ContextualParseError) {
        self.errors.borrow_mut().push(
            CSSError{
                url: url.clone(),
                line: location.line,
                column: location.column,
                message: error.to_string(),
            }
        )
    }
}

#[test]
fn test_report_error_stylesheet() {
    let css = r"
    div {
        background-color: red;
        display: invalid;
        invalid: true;
    }
    ";
    let url = ServoUrl::parse("about::test").unwrap();
    let error_reporter = TestingErrorReporter::new();

    let lock = SharedRwLock::new();
    let media = Arc::new(lock.wrap(MediaList::empty()));
    Stylesheet::from_str(css, url.clone(), Origin::UserAgent, media, lock,
                         None, &error_reporter, QuirksMode::NoQuirks, 5);

    error_reporter.assert_messages_contain(&[
        (8, 9, "Unsupported property declaration: 'display: invalid;'"),
        (9, 9, "Unsupported property declaration: 'invalid: true;'"),
    ]);

    assert_eq!(error_reporter.errors.borrow()[0].url, url);
}

#[test]
fn test_no_report_unrecognized_vendor_properties() {
    let css = r"
    div {
        -o-background-color: red;
        _background-color: red;
        -moz-background-color: red;
    }
    ";
    let url = ServoUrl::parse("about::test").unwrap();
    let error_reporter = TestingErrorReporter::new();

    let lock = SharedRwLock::new();
    let media = Arc::new(lock.wrap(MediaList::empty()));
    Stylesheet::from_str(css, url, Origin::UserAgent, media, lock,
                         None, &error_reporter, QuirksMode::NoQuirks, 0);

    error_reporter.assert_messages_contain(&[
        (4, 9, "Unsupported property declaration: '-moz-background-color: red;'"),
    ]);
}

#[test]
fn test_source_map_url() {
    let tests = vec![
        ("", None),
        ("/*# sourceMappingURL=something */", Some("something".to_string())),
    ];

    for test in tests {
        let url = ServoUrl::parse("about::test").unwrap();
        let lock = SharedRwLock::new();
        let media = Arc::new(lock.wrap(MediaList::empty()));
        let stylesheet = Stylesheet::from_str(test.0, url.clone(), Origin::UserAgent, media, lock,
                                              None, &CSSErrorReporterTest, QuirksMode::NoQuirks,
                                              0);
        let url_opt = stylesheet.contents.source_map_url.read();
        assert_eq!(*url_opt, test.1);
    }
}

#[test]
fn test_source_url() {
    let tests = vec![
        ("", None),
        ("/*# sourceURL=something */", Some("something".to_string())),
    ];

    for test in tests {
        let url = ServoUrl::parse("about::test").unwrap();
        let lock = SharedRwLock::new();
        let media = Arc::new(lock.wrap(MediaList::empty()));
        let stylesheet = Stylesheet::from_str(test.0, url.clone(), Origin::UserAgent, media, lock,
                                              None, &CSSErrorReporterTest, QuirksMode::NoQuirks,
                                              0);
        let url_opt = stylesheet.contents.source_url.read();
        assert_eq!(*url_opt, test.1);
    }
}
<|endoftext|>


<commit_before>//! Error and result types.

use std::fmt;
use std::error::Error;
use std::io::Error as IoError;

use chrono::format::ParseError as ChronoParseError;
use serde_json::{Value, from_str};

use xmlutil::XmlParseError;

/// An error produced when AWS API calls are unsuccessful.
#[derive(Debug, PartialEq)]
pub struct AwsError {
    pub message: String
}

pub fn parse_json_protocol_error(body: &str) -> AwsError {
    match from_str::<Value>(body) {
        Ok(json) => {
            let error_type: &str = match json.find("__type") {
                Some(error_type) => error_type.as_string().unwrap_or("Unknown error"),
                None => "Unknown error",
            };

            let error_message: &str = match json.find("message") {
                Some(error_message) => error_message.as_string().unwrap_or(body),
                None => body,
            };

            AwsError::new(format!("{}: {}", error_type, error_message))
        }
        Err(err) => AwsError::new(
            format!("Failed to parse error as JSON: {}.\nRaw error:\n{}", err, body)
        ),
    }
}

impl AwsError {
    /// Create a new error with the given message.
    pub fn new<S>(message: S) -> AwsError where S: Into<String> {
        AwsError {
            message: message.into(),
        }
    }
}

impl Error for AwsError {
    fn description(&self) -> &str {
        &self.message
    }
}

impl From<ChronoParseError> for AwsError {
    fn from(err: ChronoParseError) -> AwsError {
        AwsError::new(format!("{}", err))
    }
}

impl From<IoError> for AwsError {
    fn from(err: IoError) -> AwsError {
        AwsError::new(format!("{}", err))
    }
}

impl From<XmlParseError> for AwsError {
    fn from(err: XmlParseError) -> AwsError {
        AwsError::new(format!("{:?}", err))
    }
}

impl fmt::Display for AwsError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.message)
    }
}

/// The result type produced by AWS API calls.
pub type AwsResult<T> = Result<T, AwsError>;
<commit_msg>Added missing From<...> impls for AwsError<commit_after>//! Error and result types.

use std::fmt;
use std::error::Error;
use std::io::Error as IoError;
use std::str::ParseBoolError;
use std::num::ParseIntError;

use chrono::format::ParseError as ChronoParseError;
use serde_json::{Value, from_str};

use xmlutil::XmlParseError;

/// An error produced when AWS API calls are unsuccessful.
#[derive(Debug, PartialEq)]
pub struct AwsError {
    pub message: String
}

pub fn parse_json_protocol_error(body: &str) -> AwsError {
    match from_str::<Value>(body) {
        Ok(json) => {
            let error_type: &str = match json.find("__type") {
                Some(error_type) => error_type.as_string().unwrap_or("Unknown error"),
                None => "Unknown error",
            };

            let error_message: &str = match json.find("message") {
                Some(error_message) => error_message.as_string().unwrap_or(body),
                None => body,
            };

            AwsError::new(format!("{}: {}", error_type, error_message))
        }
        Err(err) => AwsError::new(
            format!("Failed to parse error as JSON: {}.\nRaw error:\n{}", err, body)
        ),
    }
}

impl AwsError {
    /// Create a new error with the given message.
    pub fn new<S>(message: S) -> AwsError where S: Into<String> {
        AwsError {
            message: message.into(),
        }
    }
}

impl Error for AwsError {
    fn description(&self) -> &str {
        &self.message
    }
}

impl From<ParseBoolError> for AwsError {
    fn from(err: ParseBoolError) -> AwsError {
        AwsError::new(format!("{}", err))
    }
}

impl From<ParseIntError> for AwsError {
    fn from(err: ParseIntError) -> AwsError {
        AwsError::new(format!("{}", err))
    }
}

impl From<ChronoParseError> for AwsError {
    fn from(err: ChronoParseError) -> AwsError {
        AwsError::new(format!("{}", err))
    }
}

impl From<IoError> for AwsError {
    fn from(err: IoError) -> AwsError {
        AwsError::new(format!("{}", err))
    }
}

impl From<XmlParseError> for AwsError {
    fn from(err: XmlParseError) -> AwsError {
        AwsError::new(format!("{:?}", err))
    }
}

impl fmt::Display for AwsError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.message)
    }
}

/// The result type produced by AWS API calls.
pub type AwsResult<T> = Result<T, AwsError>;
<|endoftext|>


<commit_before>//! Abstractions for the events passed back from i3.

use common;
use reply;
use serde_json as json;
use std::str::FromStr;

use event::inner::*;

/// An event passed back from i3.
#[derive(Debug)]
pub enum Event {
    WorkspaceEvent(WorkspaceEventInfo),
    OutputEvent(OutputEventInfo),
    ModeEvent(ModeEventInfo),
    WindowEvent(WindowEventInfo),
    BarConfigEvent(BarConfigEventInfo),
    BindingEvent(BindingEventInfo),

    #[cfg(feature = "i3-4-14")]
    #[cfg_attr(feature = "dox", doc(cfg(feature = "i3-4-14")))]
    ShutdownEvent(ShutdownEventInfo),
}

/// Data for `WorkspaceEvent`.
#[derive(Debug)]
pub struct WorkspaceEventInfo {
    /// The type of change.
    pub change: WorkspaceChange,
    /// Will be `Some` if the type of event affects the workspace.
    pub current: Option<reply::Node>,
    /// Will be `Some` only when `change == Focus` *and* there was a previous workspace.
    /// Note that if the previous workspace was empty it will get destroyed when switching, but
    /// will still appear here.
    pub old: Option<reply::Node>,
}

impl FromStr for WorkspaceEventInfo {
    type Err = json::error::Error;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let val: json::Value = try!(json::from_str(s));
        Ok(WorkspaceEventInfo {
            change: match val.get("change").unwrap().as_str().unwrap() {
                "focus" => WorkspaceChange::Focus,
                "init" => WorkspaceChange::Init,
                "empty" => WorkspaceChange::Empty,
                "urgent" => WorkspaceChange::Urgent,
                "rename" => WorkspaceChange::Rename,
                "reload" => WorkspaceChange::Reload,
                "move" => WorkspaceChange::Move,
                "restored" => WorkspaceChange::Restored,
                other => {
                    warn!(target: "i3ipc", "Unknown WorkspaceChange {}", other);
                    WorkspaceChange::Unknown
                }
            },
            current: match val.get("current").unwrap().clone() {
                json::Value::Null => None,
                val => Some(common::build_tree(&val)),
            },
            old: match val.get("old") {
                Some(o) => match o.clone() {
                    json::Value::Null => None,
                    val => Some(common::build_tree(&val)),
                },
                None => None,
            },
        })
    }
}

/// Data for `OutputEvent`.
#[derive(Debug)]
pub struct OutputEventInfo {
    /// The type of change.
    pub change: OutputChange,
}

impl FromStr for OutputEventInfo {
    type Err = json::error::Error;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let val: json::Value = try!(json::from_str(s));
        Ok(OutputEventInfo {
            change: match val.get("change").unwrap().as_str().unwrap() {
                "unspecified" => OutputChange::Unspecified,
                other => {
                    warn!(target: "i3ipc", "Unknown OutputChange {}", other);
                    OutputChange::Unknown
                }
            },
        })
    }
}

/// Data for `ModeEvent`.
#[derive(Debug)]
pub struct ModeEventInfo {
    /// The name of current mode in use. It is the same as specified in config when creating a
    /// mode. The default mode is simply named default.
    pub change: String,
}

impl FromStr for ModeEventInfo {
    type Err = json::error::Error;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let val: json::Value = try!(json::from_str(s));
        Ok(ModeEventInfo {
            change: val.get("change").unwrap().as_str().unwrap().to_owned(),
        })
    }
}

/// Data for `WindowEvent`.
#[derive(Debug)]
pub struct WindowEventInfo {
    /// Indicates the type of change
    pub change: WindowChange,
    /// The window's parent container. Be aware that for the "new" event, the container will hold
    /// the initial name of the newly reparented window (e.g. if you run urxvt with a shell that
    /// changes the title, you will still at this point get the window title as "urxvt").
    pub container: reply::Node,
}

impl FromStr for WindowEventInfo {
    type Err = json::error::Error;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let val: json::Value = try!(json::from_str(s));
        Ok(WindowEventInfo {
            change: match val.get("change").unwrap().as_str().unwrap() {
                "new" => WindowChange::New,
                "close" => WindowChange::Close,
                "focus" => WindowChange::Focus,
                "title" => WindowChange::Title,
                "fullscreen_mode" => WindowChange::FullscreenMode,
                "move" => WindowChange::Move,
                "floating" => WindowChange::Floating,
                "urgent" => WindowChange::Urgent,

                #[cfg(feature = "i3-4-13")]
                "mark" => WindowChange::Mark,

                other => {
                    warn!(target: "i3ipc", "Unknown WindowChange {}", other);
                    WindowChange::Unknown
                }
            },
            container: common::build_tree(val.get("container").unwrap()),
        })
    }
}

/// Data for `BarConfigEvent`.
#[derive(Debug)]
pub struct BarConfigEventInfo {
    /// The new i3 bar configuration.
    pub bar_config: reply::BarConfig,
}

impl FromStr for BarConfigEventInfo {
    type Err = json::error::Error;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let val: json::Value = try!(json::from_str(s));
        Ok(BarConfigEventInfo {
            bar_config: common::build_bar_config(&val),
        })
    }
}

/// Data for `BindingEvent`.
///
/// Reports on the details of a binding that ran a command because of user input.
#[derive(Debug)]
pub struct BindingEventInfo {
    /// Indicates what sort of binding event was triggered (right now it will always be "run" but
    /// that may be expanded in the future).
    pub change: BindingChange,
    pub binding: Binding,
}

impl FromStr for BindingEventInfo {
    type Err = json::error::Error;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let val: json::Value = try!(json::from_str(s));
        let bind = val.get("binding").unwrap();
        Ok(BindingEventInfo {
            change: match val.get("change").unwrap().as_str().unwrap() {
                "run" => BindingChange::Run,
                other => {
                    warn!(target: "i3ipc", "Unknown BindingChange {}", other);
                    BindingChange::Unknown
                }
            },
            binding: Binding {
                command: bind.get("command").unwrap().as_str().unwrap().to_owned(),
                event_state_mask: bind
                    .get("event_state_mask")
                    .unwrap()
                    .as_array()
                    .unwrap()
                    .iter()
                    .map(|m| m.as_str().unwrap().to_owned())
                    .collect(),
                input_code: bind.get("input_code").unwrap().as_i64().unwrap() as i32,
                symbol: match bind.get("symbol").unwrap().clone() {
                    json::Value::String(s) => Some(s),
                    json::Value::Null => None,
                    _ => unreachable!(),
                },
                input_type: match bind.get("input_type").unwrap().as_str().unwrap() {
                    "keyboard" => InputType::Keyboard,
                    "mouse" => InputType::Mouse,
                    other => {
                        warn!(target: "i3ipc", "Unknown InputType {}", other);
                        InputType::Unknown
                    }
                },
            },
        })
    }
}

/// Data for `ShutdownEvent`.
#[derive(Debug)]
#[cfg(feature = "i3-4-14")]
#[cfg_attr(feature = "dox", doc(cfg(feature = "i3-4-14")))]
pub struct ShutdownEventInfo {
    pub change: ShutdownChange,
}

#[cfg(feature = "i3-4-14")]
#[cfg_attr(feature = "dox", doc(cfg(feature = "i3-4-14")))]
impl FromStr for ShutdownEventInfo {
    type Err = json::error::Error;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let val: json::Value = try!(json::from_str(s));
        let change = match val.get("change").unwrap().as_str().unwrap() {
            "restart" => ShutdownChange::Restart,
            "exit" => ShutdownChange::Exit,
            other => {
                warn!(target: "i3ipc", "Unknown ShutdownChange {}", other);
                ShutdownChange::Unknown
            }
        };
        Ok(ShutdownEventInfo { change })
    }
}

/// Less important types
pub mod inner {
    /// The kind of workspace change.
    #[derive(Debug)]
    pub enum WorkspaceChange {
        Focus,
        Init,
        Empty,
        Urgent,
        Rename,
        Reload,
        Restored,
        Move,
        /// A WorkspaceChange we don't support yet.
        Unknown,
    }

    /// The kind of output change.
    #[derive(Debug)]
    pub enum OutputChange {
        Unspecified,
        /// An OutputChange we don't support yet.
        Unknown,
    }

    /// The kind of window change.
    #[derive(Debug)]
    pub enum WindowChange {
        /// The window has become managed by i3.
        New,
        /// The window has closed>.
        Close,
        /// The window has received input focus.
        Focus,
        /// The window's title has changed.
        Title,
        /// The window has entered or exited fullscreen mode.
        FullscreenMode,
        /// The window has changed its position in the tree.
        Move,
        /// The window has transitioned to or from floating.
        Floating,
        /// The window has become urgent or lost its urgent status.
        Urgent,

        /// A mark has been added to or removed from the window.
        #[cfg(feature = "i3-4-13")]
        #[cfg_attr(feature = "dox", doc(cfg(feature = "i3-4-13")))]
        Mark,

        /// A WindowChange we don't support yet.
        Unknown,
    }

    /// Either keyboard or mouse.
    #[derive(Debug)]
    pub enum InputType {
        Keyboard,
        Mouse,
        /// An InputType we don't support yet.
        Unknown,
    }

    /// Contains details about the binding that was run.
    #[derive(Debug)]
    pub struct Binding {
        /// The i3 command that is configured to run for this binding.
        pub command: String,

        /// The group and modifier keys that were configured with this binding.
        pub event_state_mask: Vec<String>,

        /// If the binding was configured with blindcode, this will be the key code that was given for
        /// the binding. If the binding is a mouse binding, it will be the number of times the mouse
        /// button was pressed. Otherwise it will be 0.
        pub input_code: i32,

        /// If this is a keyboard binding that was configured with bindsym, this field will contain the
        /// given symbol. Otherwise it will be None.
        pub symbol: Option<String>,

        /// Will be Keyboard or Mouse depending on whether this was a keyboard or mouse binding.
        pub input_type: InputType,
    }

    /// The kind of binding change.
    #[derive(Debug)]
    pub enum BindingChange {
        Run,
        /// A BindingChange we don't support yet.
        Unknown,
    }

    /// The kind of shutdown change.
    #[derive(Debug)]
    #[cfg(feature = "i3-4-14")]
    #[cfg_attr(feature = "dox", doc(cfg(feature = "i3-4-14")))]
    pub enum ShutdownChange {
        Restart,
        Exit,
        /// A ShutdownChange we don't support yet.
        Unknown,
    }
}
<commit_msg>Adds derive(PartialEq) for enums in event::inner<commit_after>//! Abstractions for the events passed back from i3.

use common;
use reply;
use serde_json as json;
use std::str::FromStr;

use event::inner::*;

/// An event passed back from i3.
#[derive(Debug)]
pub enum Event {
    WorkspaceEvent(WorkspaceEventInfo),
    OutputEvent(OutputEventInfo),
    ModeEvent(ModeEventInfo),
    WindowEvent(WindowEventInfo),
    BarConfigEvent(BarConfigEventInfo),
    BindingEvent(BindingEventInfo),

    #[cfg(feature = "i3-4-14")]
    #[cfg_attr(feature = "dox", doc(cfg(feature = "i3-4-14")))]
    ShutdownEvent(ShutdownEventInfo),
}

/// Data for `WorkspaceEvent`.
#[derive(Debug)]
pub struct WorkspaceEventInfo {
    /// The type of change.
    pub change: WorkspaceChange,
    /// Will be `Some` if the type of event affects the workspace.
    pub current: Option<reply::Node>,
    /// Will be `Some` only when `change == Focus` *and* there was a previous workspace.
    /// Note that if the previous workspace was empty it will get destroyed when switching, but
    /// will still appear here.
    pub old: Option<reply::Node>,
}

impl FromStr for WorkspaceEventInfo {
    type Err = json::error::Error;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let val: json::Value = try!(json::from_str(s));
        Ok(WorkspaceEventInfo {
            change: match val.get("change").unwrap().as_str().unwrap() {
                "focus" => WorkspaceChange::Focus,
                "init" => WorkspaceChange::Init,
                "empty" => WorkspaceChange::Empty,
                "urgent" => WorkspaceChange::Urgent,
                "rename" => WorkspaceChange::Rename,
                "reload" => WorkspaceChange::Reload,
                "move" => WorkspaceChange::Move,
                "restored" => WorkspaceChange::Restored,
                other => {
                    warn!(target: "i3ipc", "Unknown WorkspaceChange {}", other);
                    WorkspaceChange::Unknown
                }
            },
            current: match val.get("current").unwrap().clone() {
                json::Value::Null => None,
                val => Some(common::build_tree(&val)),
            },
            old: match val.get("old") {
                Some(o) => match o.clone() {
                    json::Value::Null => None,
                    val => Some(common::build_tree(&val)),
                },
                None => None,
            },
        })
    }
}

/// Data for `OutputEvent`.
#[derive(Debug)]
pub struct OutputEventInfo {
    /// The type of change.
    pub change: OutputChange,
}

impl FromStr for OutputEventInfo {
    type Err = json::error::Error;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let val: json::Value = try!(json::from_str(s));
        Ok(OutputEventInfo {
            change: match val.get("change").unwrap().as_str().unwrap() {
                "unspecified" => OutputChange::Unspecified,
                other => {
                    warn!(target: "i3ipc", "Unknown OutputChange {}", other);
                    OutputChange::Unknown
                }
            },
        })
    }
}

/// Data for `ModeEvent`.
#[derive(Debug)]
pub struct ModeEventInfo {
    /// The name of current mode in use. It is the same as specified in config when creating a
    /// mode. The default mode is simply named default.
    pub change: String,
}

impl FromStr for ModeEventInfo {
    type Err = json::error::Error;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let val: json::Value = try!(json::from_str(s));
        Ok(ModeEventInfo {
            change: val.get("change").unwrap().as_str().unwrap().to_owned(),
        })
    }
}

/// Data for `WindowEvent`.
#[derive(Debug)]
pub struct WindowEventInfo {
    /// Indicates the type of change
    pub change: WindowChange,
    /// The window's parent container. Be aware that for the "new" event, the container will hold
    /// the initial name of the newly reparented window (e.g. if you run urxvt with a shell that
    /// changes the title, you will still at this point get the window title as "urxvt").
    pub container: reply::Node,
}

impl FromStr for WindowEventInfo {
    type Err = json::error::Error;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let val: json::Value = try!(json::from_str(s));
        Ok(WindowEventInfo {
            change: match val.get("change").unwrap().as_str().unwrap() {
                "new" => WindowChange::New,
                "close" => WindowChange::Close,
                "focus" => WindowChange::Focus,
                "title" => WindowChange::Title,
                "fullscreen_mode" => WindowChange::FullscreenMode,
                "move" => WindowChange::Move,
                "floating" => WindowChange::Floating,
                "urgent" => WindowChange::Urgent,

                #[cfg(feature = "i3-4-13")]
                "mark" => WindowChange::Mark,

                other => {
                    warn!(target: "i3ipc", "Unknown WindowChange {}", other);
                    WindowChange::Unknown
                }
            },
            container: common::build_tree(val.get("container").unwrap()),
        })
    }
}

/// Data for `BarConfigEvent`.
#[derive(Debug)]
pub struct BarConfigEventInfo {
    /// The new i3 bar configuration.
    pub bar_config: reply::BarConfig,
}

impl FromStr for BarConfigEventInfo {
    type Err = json::error::Error;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let val: json::Value = try!(json::from_str(s));
        Ok(BarConfigEventInfo {
            bar_config: common::build_bar_config(&val),
        })
    }
}

/// Data for `BindingEvent`.
///
/// Reports on the details of a binding that ran a command because of user input.
#[derive(Debug)]
pub struct BindingEventInfo {
    /// Indicates what sort of binding event was triggered (right now it will always be "run" but
    /// that may be expanded in the future).
    pub change: BindingChange,
    pub binding: Binding,
}

impl FromStr for BindingEventInfo {
    type Err = json::error::Error;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let val: json::Value = try!(json::from_str(s));
        let bind = val.get("binding").unwrap();
        Ok(BindingEventInfo {
            change: match val.get("change").unwrap().as_str().unwrap() {
                "run" => BindingChange::Run,
                other => {
                    warn!(target: "i3ipc", "Unknown BindingChange {}", other);
                    BindingChange::Unknown
                }
            },
            binding: Binding {
                command: bind.get("command").unwrap().as_str().unwrap().to_owned(),
                event_state_mask: bind
                    .get("event_state_mask")
                    .unwrap()
                    .as_array()
                    .unwrap()
                    .iter()
                    .map(|m| m.as_str().unwrap().to_owned())
                    .collect(),
                input_code: bind.get("input_code").unwrap().as_i64().unwrap() as i32,
                symbol: match bind.get("symbol").unwrap().clone() {
                    json::Value::String(s) => Some(s),
                    json::Value::Null => None,
                    _ => unreachable!(),
                },
                input_type: match bind.get("input_type").unwrap().as_str().unwrap() {
                    "keyboard" => InputType::Keyboard,
                    "mouse" => InputType::Mouse,
                    other => {
                        warn!(target: "i3ipc", "Unknown InputType {}", other);
                        InputType::Unknown
                    }
                },
            },
        })
    }
}

/// Data for `ShutdownEvent`.
#[derive(Debug)]
#[cfg(feature = "i3-4-14")]
#[cfg_attr(feature = "dox", doc(cfg(feature = "i3-4-14")))]
pub struct ShutdownEventInfo {
    pub change: ShutdownChange,
}

#[cfg(feature = "i3-4-14")]
#[cfg_attr(feature = "dox", doc(cfg(feature = "i3-4-14")))]
impl FromStr for ShutdownEventInfo {
    type Err = json::error::Error;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let val: json::Value = try!(json::from_str(s));
        let change = match val.get("change").unwrap().as_str().unwrap() {
            "restart" => ShutdownChange::Restart,
            "exit" => ShutdownChange::Exit,
            other => {
                warn!(target: "i3ipc", "Unknown ShutdownChange {}", other);
                ShutdownChange::Unknown
            }
        };
        Ok(ShutdownEventInfo { change })
    }
}

/// Less important types
pub mod inner {
    /// The kind of workspace change.
    #[derive(Debug, PartialEq)]
    pub enum WorkspaceChange {
        Focus,
        Init,
        Empty,
        Urgent,
        Rename,
        Reload,
        Restored,
        Move,
        /// A WorkspaceChange we don't support yet.
        Unknown,
    }

    /// The kind of output change.
    #[derive(Debug, PartialEq)]
    pub enum OutputChange {
        Unspecified,
        /// An OutputChange we don't support yet.
        Unknown,
    }

    /// The kind of window change.
    #[derive(Debug, PartialEq)]
    pub enum WindowChange {
        /// The window has become managed by i3.
        New,
        /// The window has closed>.
        Close,
        /// The window has received input focus.
        Focus,
        /// The window's title has changed.
        Title,
        /// The window has entered or exited fullscreen mode.
        FullscreenMode,
        /// The window has changed its position in the tree.
        Move,
        /// The window has transitioned to or from floating.
        Floating,
        /// The window has become urgent or lost its urgent status.
        Urgent,

        /// A mark has been added to or removed from the window.
        #[cfg(feature = "i3-4-13")]
        #[cfg_attr(feature = "dox", doc(cfg(feature = "i3-4-13")))]
        Mark,

        /// A WindowChange we don't support yet.
        Unknown,
    }

    /// Either keyboard or mouse.
    #[derive(Debug, PartialEq)]
    pub enum InputType {
        Keyboard,
        Mouse,
        /// An InputType we don't support yet.
        Unknown,
    }

    /// Contains details about the binding that was run.
    #[derive(Debug, PartialEq)]
    pub struct Binding {
        /// The i3 command that is configured to run for this binding.
        pub command: String,

        /// The group and modifier keys that were configured with this binding.
        pub event_state_mask: Vec<String>,

        /// If the binding was configured with blindcode, this will be the key code that was given for
        /// the binding. If the binding is a mouse binding, it will be the number of times the mouse
        /// button was pressed. Otherwise it will be 0.
        pub input_code: i32,

        /// If this is a keyboard binding that was configured with bindsym, this field will contain the
        /// given symbol. Otherwise it will be None.
        pub symbol: Option<String>,

        /// Will be Keyboard or Mouse depending on whether this was a keyboard or mouse binding.
        pub input_type: InputType,
    }

    /// The kind of binding change.
    #[derive(Debug, PartialEq)]
    pub enum BindingChange {
        Run,
        /// A BindingChange we don't support yet.
        Unknown,
    }

    /// The kind of shutdown change.
    #[derive(Debug, PartialEq)]
    #[cfg(feature = "i3-4-14")]
    #[cfg_attr(feature = "dox", doc(cfg(feature = "i3-4-14")))]
    pub enum ShutdownChange {
        Restart,
        Exit,
        /// A ShutdownChange we don't support yet.
        Unknown,
    }
}
<|endoftext|>


<commit_before>
use {
    AddAll,
    AddAny,
    AddInterval,
    AddPress,
    AllEvent,
    AnyEvent,
    IntervalEvent,
    KeyType,
    PressEvent,
    Triggered,
    Value,
};

/// An immutable event context. All Request starting here.
pub struct Event<'a>;

impl<'a> Event<'a> {
    /// Returns a new event context.
    #[inline(always)]
    pub fn new() -> Event<'a> {
        Event
    }
/*
    #[inline(always)]
    pub fn after<'b>(before: &'a Triggered, after: &'a Triggered) -> AfterEvent<'a, 'b> {
        AfterEvent {
            before: Value(before),
            after: Value(after),
        }
    }
    */
}

impl<'a> Clone for Event<'a> {
    fn clone(&self) -> Event<'static> {
        Event
    }
}

impl<'a, 'b> AddPress<'a, PressEvent<'a, 'b>> for Event<'a> {
    #[inline(always)]
    fn press(&'a self, key: &'a KeyType) -> PressEvent<'a, 'b> {
        PressEvent {
            key: Value(key),
        }
    }
}

impl<'a> AddInterval<IntervalEvent<'a>> for Event<'a> {
    #[inline(always)]
    fn interval(&self, seconds: f64) -> IntervalEvent<'a> {
        IntervalEvent {
            interval: Value(seconds),
        }
    }
}

impl<'a, 'b> AddAll<'a, AllEvent<'a, 'b>> for Event<'a> {
    #[inline(always)]
    fn all(&'a self, events: &'b [&'b Triggered]) -> AllEvent<'a, 'b> {
        AllEvent {
            events: Value(events),
        }
    }
}

impl<'a, 'b> AddAny<'a, AnyEvent<'a, 'b>> for Event<'a> {
    #[inline(always)]
    fn any(&'a self, events: &'b [&'b Triggered]) -> AnyEvent<'a, 'b> {
        AnyEvent {
            events: Value(events),
        }
    }
}

<commit_msg>Upgraded to latest Rust<commit_after>
use {
    AddAll,
    AddAny,
    AddInterval,
    AddPress,
    AllEvent,
    AnyEvent,
    IntervalEvent,
    KeyType,
    PressEvent,
    Triggered,
    Value,
};

/// An immutable event context. All Request starting here.
pub struct Event<'a>;

impl<'a> Event<'a> {
    /// Returns a new event context.
    #[inline(always)]
    pub fn new() -> Event<'a> {
        Event
    }
/*
    #[inline(always)]
    pub fn after<'b>(before: &'a Triggered, after: &'a Triggered) -> AfterEvent<'a, 'b> {
        AfterEvent {
            before: Value(before),
            after: Value(after),
        }
    }
    */
}

impl<'a> Clone for Event<'a> {
    fn clone(&self) -> Event<'static> {
        Event
    }
}

impl<'a, 'b> AddPress<'a, PressEvent<'a, 'b>> for Event<'a> {
    #[inline(always)]
    fn press(&'a self, key: &'a KeyType) -> PressEvent<'a, 'a> {
        PressEvent {
            key: Value(key),
        }
    }
}

impl<'a> AddInterval<IntervalEvent<'a>> for Event<'a> {
    #[inline(always)]
    fn interval(&self, seconds: f64) -> IntervalEvent<'a> {
        IntervalEvent {
            interval: Value(seconds),
        }
    }
}

impl<'a, 'b> AddAll<'a, AllEvent<'a, 'b>> for Event<'a> {
    #[inline(always)]
    fn all(&'a self, events: &'b [&'b Triggered]) -> AllEvent<'a, 'b> {
        AllEvent {
            events: Value(events),
        }
    }
}

impl<'a, 'b> AddAny<'a, AnyEvent<'a, 'b>> for Event<'a> {
    #[inline(always)]
    fn any(&'a self, events: &'b [&'b Triggered]) -> AnyEvent<'a, 'b> {
        AnyEvent {
            events: Value(events),
        }
    }
}

<|endoftext|>


<commit_before>#[cfg(unix)]
mod unix;

#[cfg(any(target_os = "linux", target_os = "android"))]
fn epoll_examples() {
    unix::cares_epoll::main();
    unix::cares_epoll_resolver::main();
}

#[cfg(not(any(target_os = "linux", target_os = "android")))]
fn epoll_examples() { }

#[cfg(unix)]
fn main() {
    unix::cares_event_loop::main();
    unix::cares_event_loop_resolver::main();
    epoll_examples();
}

#[cfg(windows)]
extern crate winapi;

#[cfg(windows)]
extern crate ws2_32;

#[cfg(windows)]
fn main() {
    mod windows;
    windows::cares_select::main();
}
<commit_msg>Clean windows build<commit_after>#[cfg(unix)]
mod unix;

#[cfg(all(unix, any(target_os = "linux", target_os = "android")))]
fn epoll_examples() {
    unix::cares_epoll::main();
    unix::cares_epoll_resolver::main();
}

#[cfg(all(unix, not(any(target_os = "linux", target_os = "android"))))]
fn epoll_examples() { }

#[cfg(unix)]
fn main() {
    unix::cares_event_loop::main();
    unix::cares_event_loop_resolver::main();
    epoll_examples();
}

#[cfg(windows)]
extern crate winapi;

#[cfg(windows)]
extern crate ws2_32;

#[cfg(windows)]
fn main() {
    mod windows;
    windows::cares_select::main();
}
<|endoftext|>


<commit_before>// Copyright 2016 Google Inc. All Rights Reserved.
//
// Licensed under the MIT License, <LICENSE or http://opensource.org/licenses/MIT>.
// This file may not be copied, modified, or distributed except according to those terms.

use bincode;
use serde;
use scoped_pool::Pool;
use std::fmt;
use std::io::{self, BufReader, BufWriter, Write};
use std::net::{SocketAddr, TcpListener, TcpStream, ToSocketAddrs};
use std::sync::{Condvar, Mutex};
use std::sync::mpsc::{Receiver, Sender, TryRecvError, channel};
use std::sync::atomic::{AtomicBool, Ordering};
use std::time::Duration;
use std::thread::{self, JoinHandle};
use super::{Packet, Result};

struct ConnectionHandler<'a, S>
    where S: Serve
{
    read_stream: BufReader<TcpStream>,
    write_stream: BufWriter<TcpStream>,
    shutdown: &'a AtomicBool,
    inflight_rpcs: &'a InflightRpcs,
    server: S,
    pool: &'a Pool,
}

impl<'a, S> Drop for ConnectionHandler<'a, S> where S: Serve {
    fn drop(&mut self) {
        trace!("ConnectionHandler: finished serving client.");
        self.inflight_rpcs.decrement_and_notify();
    }
}

impl<'a, S> ConnectionHandler<'a, S> where S: Serve {
    fn handle_conn(&mut self) -> Result<()> {
        let ConnectionHandler {
            ref mut read_stream,
            ref mut write_stream,
            shutdown,
            ref inflight_rpcs,
            ref server,
            pool,
        } = *self;
        trace!("ConnectionHandler: serving client...");
        pool.scoped(|scope| {
            let (tx, rx) = channel();
            scope.execute(|| Self::write(rx, write_stream, inflight_rpcs));
            loop {
                match bincode::serde::deserialize_from(read_stream, bincode::SizeLimit::Infinite) {
                    Ok(Packet { rpc_id, message, }) => {
                        inflight_rpcs.increment();
                        let tx = tx.clone();
                        scope.execute(move || {
                            let reply = server.serve(message);
                            let reply_packet = Packet {
                                rpc_id: rpc_id,
                                message: reply
                            };
                            tx.send(reply_packet).unwrap();
                        });
                        if shutdown.load(Ordering::SeqCst) {
                            info!("ConnectionHandler: server shutdown, so closing connection.");
                            break;
                        }
                    }
                    Err(bincode::serde::DeserializeError::IoError(ref err))
                        if Self::timed_out(err.kind()) => {
                        if !shutdown.load(Ordering::SeqCst) {
                            info!("ConnectionHandler: read timed out ({:?}). Server not \
                                   shutdown, so retrying read.",
                                  err);
                            continue;
                        } else {
                            info!("ConnectionHandler: read timed out ({:?}). Server shutdown, so \
                                   closing connection.",
                                  err);
                            break;
                        }
                    }
                    Err(e) => {
                        warn!("ConnectionHandler: closing client connection due to {:?}",
                              e);
                        return Err(e.into());
                    }
                }
            }
            Ok(())
        })
    }

    fn timed_out(error_kind: io::ErrorKind) -> bool {
        match error_kind {
            io::ErrorKind::TimedOut | io::ErrorKind::WouldBlock => true,
            _ => false,
        }
    }

    fn write(rx: Receiver<Packet<<S as Serve>::Reply>>,
             stream: &mut BufWriter<TcpStream>,
             inflight_rpcs: &InflightRpcs) {
        loop {
            match rx.recv() {
                Err(e) => {
                    debug!("Write thread: returning due to {:?}", e);
                    return;
                }
                Ok(reply_packet) => {
                    if let Err(e) =
                           bincode::serde::serialize_into(stream,
                                                          &reply_packet,
                                                          bincode::SizeLimit::Infinite) {
                        warn!("Writer: failed to write reply to Client: {:?}",
                              e);
                    }
                    if let Err(e) = stream.flush() {
                        warn!("Writer: failed to flush reply to Client: {:?}",
                              e);
                    }
                    inflight_rpcs.decrement();
                }
            }
        }
    }
}

struct InflightRpcs {
    count: Mutex<u64>,
    cvar: Condvar,
}

impl InflightRpcs {
    fn new() -> InflightRpcs {
        InflightRpcs {
            count: Mutex::new(0),
            cvar: Condvar::new(),
        }
    }

    fn wait_until_zero(&self) {
        let mut count = self.count.lock().unwrap();
        while *count != 0 {
            count = self.cvar.wait(count).unwrap();
        }
        info!("serve_async: shutdown complete ({} connections alive)",
              *count);
    }

    fn increment(&self) {
        *self.count.lock().unwrap() += 1;
    }

    fn decrement(&self) {
        *self.count.lock().unwrap() -= 1;
    }

    fn decrement_and_notify(&self) {
        *self.count.lock().unwrap() -= 1;
        self.cvar.notify_one();
    }

}

/// Provides methods for blocking until the server completes,
pub struct ServeHandle {
    tx: Sender<()>,
    join_handle: JoinHandle<()>,
    addr: SocketAddr,
}

impl ServeHandle {
    /// Block until the server completes
    pub fn wait(self) {
        self.join_handle.join().expect(pos!());
    }

    /// Returns the address the server is bound to
    pub fn local_addr(&self) -> &SocketAddr {
        &self.addr
    }

    /// Shutdown the server. Gracefully shuts down the serve thread but currently does not
    /// gracefully close open connections.
    pub fn shutdown(self) {
        info!("ServeHandle: attempting to shut down the server.");
        self.tx.send(()).expect(pos!());
        if let Ok(_) = TcpStream::connect(self.addr) {
            self.join_handle.join().expect(pos!());
        } else {
            warn!("ServeHandle: best effort shutdown of serve thread failed");
        }
    }
}

/// Start
pub fn serve_async<A, S>(addr: A,
                         server: S,
                         read_timeout: Option<Duration>)
                         -> io::Result<ServeHandle>
    where A: ToSocketAddrs,
          S: 'static + Serve
{
    let listener = try!(TcpListener::bind(&addr));
    let addr = try!(listener.local_addr());
    info!("serve_async: spinning up server on {:?}", addr);
    let (die_tx, die_rx) = channel();
    let join_handle = thread::spawn(move || {
        let pool = Pool::new(100); // TODO(tjk): make this configurable, and expire idle threads
        let shutdown = AtomicBool::new(false);
        let inflight_rpcs = InflightRpcs::new();
        pool.scoped(|scope| {
            for conn in listener.incoming() {
                match die_rx.try_recv() {
                    Ok(_) => {
                        info!("serve_async: shutdown received. Waiting for open connections to \
                               return...");
                        shutdown.store(true, Ordering::SeqCst);
                        inflight_rpcs.wait_until_zero();
                        break;
                    }
                    Err(TryRecvError::Disconnected) => {
                        info!("serve_async: sender disconnected.");
                        break;
                    }
                    _ => (),
                }
                let conn = match conn {
                    Err(err) => {
                        error!("serve_async: failed to accept connection: {:?}", err);
                        return;
                    }
                    Ok(c) => c,
                };
                if let Err(err) = conn.set_read_timeout(read_timeout) {
                    info!("Server: could not set read timeout: {:?}", err);
                    return;
                }
                inflight_rpcs.increment();
                scope.execute(|| {
                    let mut handler = ConnectionHandler {
                        read_stream: BufReader::new(conn.try_clone().expect(pos!())),
                        write_stream: BufWriter::new(conn),
                        shutdown: &shutdown,
                        inflight_rpcs: &inflight_rpcs,
                        server: &server,
                        pool: &pool,
                    };
                    if let Err(err) = handler.handle_conn() {
                        info!("ConnectionHandler: err in connection handling: {:?}", err);
                    }
                });
            }
        });
    });
    Ok(ServeHandle {
        tx: die_tx,
        join_handle: join_handle,
        addr: addr.clone(),
    })
}

/// A service provided by a server
pub trait Serve: Send + Sync {
    /// The type of request received by the server
    type Request: 'static + fmt::Debug + serde::ser::Serialize + serde::de::Deserialize + Send;
    /// The type of reply sent by the server
    type Reply  : 'static + fmt::Debug + serde::ser::Serialize + serde::de::Deserialize + Send;

    /// Return a reply for a given request
    fn serve(&self, request: Self::Request) -> Self::Reply;
}

impl<P, S> Serve for P
    where P: Send + Sync + ::std::ops::Deref<Target=S>,
          S: Serve
{
    type Request = S::Request;
    type Reply = S::Reply;

    fn serve(&self, request: S::Request) -> S::Reply {
        S::serve(self, request)
    }
}
<commit_msg>Remove unwanted whitespace<commit_after>// Copyright 2016 Google Inc. All Rights Reserved.
//
// Licensed under the MIT License, <LICENSE or http://opensource.org/licenses/MIT>.
// This file may not be copied, modified, or distributed except according to those terms.

use bincode;
use serde;
use scoped_pool::Pool;
use std::fmt;
use std::io::{self, BufReader, BufWriter, Write};
use std::net::{SocketAddr, TcpListener, TcpStream, ToSocketAddrs};
use std::sync::{Condvar, Mutex};
use std::sync::mpsc::{Receiver, Sender, TryRecvError, channel};
use std::sync::atomic::{AtomicBool, Ordering};
use std::time::Duration;
use std::thread::{self, JoinHandle};
use super::{Packet, Result};

struct ConnectionHandler<'a, S>
    where S: Serve
{
    read_stream: BufReader<TcpStream>,
    write_stream: BufWriter<TcpStream>,
    shutdown: &'a AtomicBool,
    inflight_rpcs: &'a InflightRpcs,
    server: S,
    pool: &'a Pool,
}

impl<'a, S> Drop for ConnectionHandler<'a, S> where S: Serve {
    fn drop(&mut self) {
        trace!("ConnectionHandler: finished serving client.");
        self.inflight_rpcs.decrement_and_notify();
    }
}

impl<'a, S> ConnectionHandler<'a, S> where S: Serve {
    fn handle_conn(&mut self) -> Result<()> {
        let ConnectionHandler {
            ref mut read_stream,
            ref mut write_stream,
            shutdown,
            ref inflight_rpcs,
            ref server,
            pool,
        } = *self;
        trace!("ConnectionHandler: serving client...");
        pool.scoped(|scope| {
            let (tx, rx) = channel();
            scope.execute(|| Self::write(rx, write_stream, inflight_rpcs));
            loop {
                match bincode::serde::deserialize_from(read_stream, bincode::SizeLimit::Infinite) {
                    Ok(Packet { rpc_id, message, }) => {
                        inflight_rpcs.increment();
                        let tx = tx.clone();
                        scope.execute(move || {
                            let reply = server.serve(message);
                            let reply_packet = Packet {
                                rpc_id: rpc_id,
                                message: reply
                            };
                            tx.send(reply_packet).unwrap();
                        });
                        if shutdown.load(Ordering::SeqCst) {
                            info!("ConnectionHandler: server shutdown, so closing connection.");
                            break;
                        }
                    }
                    Err(bincode::serde::DeserializeError::IoError(ref err))
                        if Self::timed_out(err.kind()) => {
                        if !shutdown.load(Ordering::SeqCst) {
                            info!("ConnectionHandler: read timed out ({:?}). Server not \
                                   shutdown, so retrying read.",
                                  err);
                            continue;
                        } else {
                            info!("ConnectionHandler: read timed out ({:?}). Server shutdown, so \
                                   closing connection.",
                                  err);
                            break;
                        }
                    }
                    Err(e) => {
                        warn!("ConnectionHandler: closing client connection due to {:?}",
                              e);
                        return Err(e.into());
                    }
                }
            }
            Ok(())
        })
    }

    fn timed_out(error_kind: io::ErrorKind) -> bool {
        match error_kind {
            io::ErrorKind::TimedOut | io::ErrorKind::WouldBlock => true,
            _ => false,
        }
    }

    fn write(rx: Receiver<Packet<<S as Serve>::Reply>>,
             stream: &mut BufWriter<TcpStream>,
             inflight_rpcs: &InflightRpcs) {
        loop {
            match rx.recv() {
                Err(e) => {
                    debug!("Write thread: returning due to {:?}", e);
                    return;
                }
                Ok(reply_packet) => {
                    if let Err(e) =
                           bincode::serde::serialize_into(stream,
                                                          &reply_packet,
                                                          bincode::SizeLimit::Infinite) {
                        warn!("Writer: failed to write reply to Client: {:?}",
                              e);
                    }
                    if let Err(e) = stream.flush() {
                        warn!("Writer: failed to flush reply to Client: {:?}",
                              e);
                    }
                    inflight_rpcs.decrement();
                }
            }
        }
    }
}

struct InflightRpcs {
    count: Mutex<u64>,
    cvar: Condvar,
}

impl InflightRpcs {
    fn new() -> InflightRpcs {
        InflightRpcs {
            count: Mutex::new(0),
            cvar: Condvar::new(),
        }
    }

    fn wait_until_zero(&self) {
        let mut count = self.count.lock().unwrap();
        while *count != 0 {
            count = self.cvar.wait(count).unwrap();
        }
        info!("serve_async: shutdown complete ({} connections alive)",
              *count);
    }

    fn increment(&self) {
        *self.count.lock().unwrap() += 1;
    }

    fn decrement(&self) {
        *self.count.lock().unwrap() -= 1;
    }

    fn decrement_and_notify(&self) {
        *self.count.lock().unwrap() -= 1;
        self.cvar.notify_one();
    }

}

/// Provides methods for blocking until the server completes,
pub struct ServeHandle {
    tx: Sender<()>,
    join_handle: JoinHandle<()>,
    addr: SocketAddr,
}

impl ServeHandle {
    /// Block until the server completes
    pub fn wait(self) {
        self.join_handle.join().expect(pos!());
    }

    /// Returns the address the server is bound to
    pub fn local_addr(&self) -> &SocketAddr {
        &self.addr
    }

    /// Shutdown the server. Gracefully shuts down the serve thread but currently does not
    /// gracefully close open connections.
    pub fn shutdown(self) {
        info!("ServeHandle: attempting to shut down the server.");
        self.tx.send(()).expect(pos!());
        if let Ok(_) = TcpStream::connect(self.addr) {
            self.join_handle.join().expect(pos!());
        } else {
            warn!("ServeHandle: best effort shutdown of serve thread failed");
        }
    }
}

/// Start
pub fn serve_async<A, S>(addr: A,
                         server: S,
                         read_timeout: Option<Duration>)
                         -> io::Result<ServeHandle>
    where A: ToSocketAddrs,
          S: 'static + Serve
{
    let listener = try!(TcpListener::bind(&addr));
    let addr = try!(listener.local_addr());
    info!("serve_async: spinning up server on {:?}", addr);
    let (die_tx, die_rx) = channel();
    let join_handle = thread::spawn(move || {
        let pool = Pool::new(100); // TODO(tjk): make this configurable, and expire idle threads
        let shutdown = AtomicBool::new(false);
        let inflight_rpcs = InflightRpcs::new();
        pool.scoped(|scope| {
            for conn in listener.incoming() {
                match die_rx.try_recv() {
                    Ok(_) => {
                        info!("serve_async: shutdown received. Waiting for open connections to \
                               return...");
                        shutdown.store(true, Ordering::SeqCst);
                        inflight_rpcs.wait_until_zero();
                        break;
                    }
                    Err(TryRecvError::Disconnected) => {
                        info!("serve_async: sender disconnected.");
                        break;
                    }
                    _ => (),
                }
                let conn = match conn {
                    Err(err) => {
                        error!("serve_async: failed to accept connection: {:?}", err);
                        return;
                    }
                    Ok(c) => c,
                };
                if let Err(err) = conn.set_read_timeout(read_timeout) {
                    info!("Server: could not set read timeout: {:?}", err);
                    return;
                }
                inflight_rpcs.increment();
                scope.execute(|| {
                    let mut handler = ConnectionHandler {
                        read_stream: BufReader::new(conn.try_clone().expect(pos!())),
                        write_stream: BufWriter::new(conn),
                        shutdown: &shutdown,
                        inflight_rpcs: &inflight_rpcs,
                        server: &server,
                        pool: &pool,
                    };
                    if let Err(err) = handler.handle_conn() {
                        info!("ConnectionHandler: err in connection handling: {:?}", err);
                    }
                });
            }
        });
    });
    Ok(ServeHandle {
        tx: die_tx,
        join_handle: join_handle,
        addr: addr.clone(),
    })
}

/// A service provided by a server
pub trait Serve: Send + Sync {
    /// The type of request received by the server
    type Request: 'static + fmt::Debug + serde::ser::Serialize + serde::de::Deserialize + Send;
    /// The type of reply sent by the server
    type Reply: 'static + fmt::Debug + serde::ser::Serialize + serde::de::Deserialize + Send;

    /// Return a reply for a given request
    fn serve(&self, request: Self::Request) -> Self::Reply;
}

impl<P, S> Serve for P
    where P: Send + Sync + ::std::ops::Deref<Target=S>,
          S: Serve
{
    type Request = S::Request;
    type Reply = S::Reply;

    fn serve(&self, request: S::Request) -> S::Reply {
        S::serve(self, request)
    }
}
<|endoftext|>


<commit_before>extern crate futures;
extern crate tokio_core;
extern crate tokio_signal;

use futures::stream::Stream;
use tokio_core::reactor::Core;

fn main() {
    let mut core = Core::new().unwrap();
    let ctrlc = tokio_signal::ctrl_c(&core.handle());
    let stream = core.run(ctrlc).unwrap();

    core.run(stream.for_each(|()| {
        println!("Ctrl-C received!");
        Ok(())
    })).unwrap();
}
<commit_msg>signal: ctrl+C example: Prompt user to do something.<commit_after>extern crate futures;
extern crate tokio_core;
extern crate tokio_signal;

use futures::stream::Stream;
use tokio_core::reactor::Core;

fn main() {
    let mut core = Core::new().unwrap();
    let ctrlc = tokio_signal::ctrl_c(&core.handle());
    let stream = core.run(ctrlc).unwrap();

    println!("This program is now waiting for you to press Ctrl+C");

    core.run(stream.for_each(|()| {
        println!("Ctrl-C received!");
        Ok(())
    })).unwrap();
}
<|endoftext|>


<commit_before>/*
 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
 * SPDX-License-Identifier: Apache-2.0.
 */

use aws_config::meta::region::RegionProviderChain;
use aws_sdk_apigateway::{Client, Error, Region, PKG_VERSION};
use structopt::StructOpt;

#[derive(Debug, StructOpt)]
struct Opt {
    /// The AWS Region.
    #[structopt(short, long)]
    region: Option<String>,

    /// Whether to display additional information.
    #[structopt(short, long)]
    verbose: bool,
}

/// Displays information about the Amazon API Gateway REST APIs in the Region.
///
/// # Arguments
///
/// * `[-r REGION]` - The Region in which the client is created.
///   If not supplied, uses the value of the **AWS_REGION** environment variable.
///   If the environment variable is not set, defaults to **us-west-2**.
/// * `[-v]` - Whether to display information.
#[tokio::main]
async fn main() -> Result<(), Error> {
    tracing_subscriber::fmt::init();
    let Opt { region, verbose } = Opt::from_args();

    let region_provider = RegionProviderChain::first_try(region.map(Region::new))
        .or_default_provider()
        .or_else(Region::new("us-west-2"));
    println!();

    if verbose {
        println!("APIGateway client version: {}", PKG_VERSION);
        println!(
            "Region:                    {}",
            region_provider.region().await.unwrap().as_ref()
        );

        println!();
    }

    let shared_config = aws_config::from_env().region(region_provider).load().await;
    let client = Client::new(&shared_config);

    let resp = client.get_rest_apis().send().await?;

    for api in resp.items.unwrap_or_default() {
        println!("ID:          {}", api.id.as_deref().unwrap_or_default());
        println!("Name:        {}", api.name.as_deref().unwrap_or_default());
        println!(
            "Description: {}",
            api.description.as_deref().unwrap_or_default()
        );
        println!(
            "Version:     {}",
            api.version.as_deref().unwrap_or_default()
        );
        println!("Created:     {}", api.created_date.unwrap().to_chrono());
        println!();
    }

    Ok(())
}
<commit_msg>Removed unit test from API Gateway code example in Rust.<commit_after>/*
 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
 * SPDX-License-Identifier: Apache-2.0.
 */

use aws_config::meta::region::RegionProviderChain;
use aws_sdk_apigateway::{Client, Error, Region, PKG_VERSION};
use structopt::StructOpt;

#[derive(Debug, StructOpt)]
struct Opt {
    /// The AWS Region.
    #[structopt(short, long)]
    region: Option<String>,

    /// Whether to display additional information.
    #[structopt(short, long)]
    verbose: bool,
}

// Displays the Amazon API Gateway REST APIs in the Region.
async fn show_apis(client: &aws_sdk_apigateway::Client) -> Result<(), aws_sdk_apigateway::Error> {
    let resp = client.get_rest_apis().send().await?;

    for api in resp.items.unwrap_or_default() {
        println!("ID:          {}", api.id.as_deref().unwrap_or_default());
        println!("Name:        {}", api.name.as_deref().unwrap_or_default());
        println!(
            "Description: {}",
            api.description.as_deref().unwrap_or_default()
        );
        println!(
            "Version:     {}",
            api.version.as_deref().unwrap_or_default()
        );
        println!("Created:     {}", api.created_date.unwrap().to_chrono());
        println!();
    }

    Ok(())
}

/// Displays information about the Amazon API Gateway REST APIs in the Region.
///
/// # Arguments
///
/// * `[-r REGION]` - The Region in which the client is created.
///   If not supplied, uses the value of the **AWS_REGION** environment variable.
///   If the environment variable is not set, defaults to **us-west-2**.
/// * `[-v]` - Whether to display information.
#[tokio::main]
async fn main() -> Result<(), Error> {
    tracing_subscriber::fmt::init();
    let Opt { region, verbose } = Opt::from_args();

    let region_provider = RegionProviderChain::first_try(region.map(Region::new))
        .or_default_provider()
        .or_else(Region::new("us-west-2"));
    println!();

    if verbose {
        println!("APIGateway client version: {}", PKG_VERSION);
        println!(
            "Region:                    {}",
            region_provider.region().await.unwrap().as_ref()
        );

        println!();
    }

    let shared_config = aws_config::from_env().region(region_provider).load().await;
    let client = Client::new(&shared_config);

    show_apis(&client).await.unwrap();

    Ok(())
}
<|endoftext|>