<commit_before>use std::collections::VecDeque;
use std::io::Read;

use crate::common::LineIter;

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
struct Segments([bool; 7]);

impl Segments {
    pub fn overlap(&self, other: &Segments) -> usize {
        self.0
            .iter()
            .zip(&other.0)
            .filter(|&(&ours, &theirs)| ours || theirs)
            .count()
    }
}

impl FromIterator<char> for Segments {
    fn from_iter<T: IntoIterator<Item = char>>(iter: T) -> Self {
        let mut buffer = [false; 7];

        for c in iter {
            buffer[c as usize - b'a' as usize] = true;
        }

        Self(buffer)
    }
}

pub fn part1(input: &mut dyn Read) -> String {
    let mut reader = LineIter::new(input);

    let mut total = 0;

    while let Some(line) = reader.next() {
        total += line
            .split(' ')
            .skip_while(|&s| s != "|")
            .filter(|s| [2, 3, 4, 7].contains(&s.len()))
            .count();
    }

    total.to_string()
}

fn decode(line: &str) -> usize {
    let mut mapping = [None; 10];

    let mut unmatched: VecDeque<_> = line.split(' ').filter(|&s| s != "|").collect();

    while let Some(digit) = unmatched.pop_front() {
        let segments: Segments = digit.chars().collect();

        match digit.len() {
            2 => mapping[1] = Some(segments),
            3 => mapping[7] = Some(segments),
            4 => mapping[4] = Some(segments),
            5 => {
                // Could be 2, 3, or 5
                if let Some(one) = mapping[1] {
                    if segments.overlap(&one) == 5 {
                        // No lines added, so must be a three
                        mapping[3] = Some(segments);
                        continue;
                    } else if let Some(four) = mapping[4] {
                        // Should be 6 for 5 and 7 for 2
                        if segments.overlap(&four) == 6 {
                            mapping[5] = Some(segments);
                        } else {
                            mapping[2] = Some(segments);
                        }
                        continue;
                    }
                }
                unmatched.push_back(digit);
            }
            6 => {
                // Could be 0, 6, or 9
                if let Some(one) = mapping[1] {
                    if segments.overlap(&one) == 7 {
                        mapping[6] = Some(segments);
                        continue;
                    } else if let Some(four) = mapping[4] {
                        if segments.overlap(&four) == 6 {
                            mapping[9] = Some(segments);
                        } else {
                            mapping[0] = Some(segments);
                        }
                        continue;
                    }
                    unmatched.push_back(digit);
                }
            }
            7 => mapping[8] = Some(segments),
            _ => panic!("Invalid digit: {}", digit),
        }
    }

    line.split(' ')
        .skip_while(|&s| s != "|")
        .skip(1)
        .map(|s| {
            let segments: Segments = s.chars().collect();
            mapping.iter().position(|s| &Some(segments) == s).unwrap()
        })
        .fold(0, |acc, n| acc * 10 + n)
}

pub fn part2(input: &mut dyn Read) -> String {
    let mut reader = LineIter::new(input);
    let mut total = 0;

    while let Some(line) = reader.next() {
        total += decode(line);
    }

    total.to_string()
}

#[cfg(test)]
mod tests {
    use crate::test_implementation;

    use super::*;

    const SAMPLE1: &[u8] =
        &*b"acedgfb cdfbe gcdfa fbcad dab cefabd cdfgeb eafb cagedb ab | cdfeb fcadb cdfeb cdbaf";
    const SAMPLE2: &[u8] = include_bytes!("samples/08.txt");

    #[test]
    fn sample_part1() {
        test_implementation(part1, SAMPLE1, 0);
        test_implementation(part1, SAMPLE2, 26);
    }

    #[test]
    fn sample_part2() {
        test_implementation(part2, SAMPLE1, 5353);
        test_implementation(part2, SAMPLE2, 61229);
    }
}
<commit_msg>Bit-optimize day 8<commit_after>use std::collections::VecDeque;
use std::io::Read;
use std::num::NonZeroU8;

use crate::common::LineIter;

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
struct Segments(NonZeroU8);

impl Segments {
    pub fn overlap(self, other: Segments) -> u32 {
        (self.0 | other.0).get().count_ones()
    }

    pub fn len(self) -> u32 {
        self.0.get().count_ones()
    }
}

impl<'a> From<&'a str> for Segments {
    fn from(s: &'a str) -> Self {
        let mut buffer = 0;

        for &b in s.as_bytes() {
            debug_assert!((b'a'..=b'g').contains(&b));

            buffer |= 1 << (b - b'a');
        }

        Self(NonZeroU8::new(buffer).unwrap())
    }
}

pub fn part1(input: &mut dyn Read) -> String {
    let mut reader = LineIter::new(input);

    let mut total = 0;

    while let Some(line) = reader.next() {
        total += line
            .split(' ')
            .skip_while(|&s| s != "|")
            .filter(|s| [2, 3, 4, 7].contains(&s.len()))
            .count();
    }

    total.to_string()
}

fn decode(line: &str, unmatched: &mut VecDeque<Segments>) -> usize {
    let mut mapping = [None; 10];

    unmatched.clear();
    unmatched.extend(line.split(' ').filter(|&s| s != "|").map(Segments::from));

    while let Some(segments) = unmatched.pop_front() {
        // Note: this loop might "deduce" a combination more than once, but deducing digits is
        // idempotent so it does not interfere.
        match segments.len() {
            2 => mapping[1] = Some(segments),
            3 => mapping[7] = Some(segments),
            4 => mapping[4] = Some(segments),
            5 => {
                // Could be 2, 3, or 5
                if let Some(one) = mapping[1] {
                    if segments.overlap(one) == 5 {
                        // No lines added, so must be a three
                        mapping[3] = Some(segments);
                        continue;
                    } else if let Some(four) = mapping[4] {
                        // Should be 6 for 5 and 7 for 2
                        if segments.overlap(four) == 6 {
                            mapping[5] = Some(segments);
                        } else {
                            mapping[2] = Some(segments);
                        }
                        continue;
                    }
                }
                unmatched.push_back(segments);
            }
            6 => {
                // Could be 0, 6, or 9
                if let Some(one) = mapping[1] {
                    if segments.overlap(one) == 7 {
                        mapping[6] = Some(segments);
                        continue;
                    } else if let Some(four) = mapping[4] {
                        if segments.overlap(four) == 6 {
                            mapping[9] = Some(segments);
                        } else {
                            mapping[0] = Some(segments);
                        }
                        continue;
                    }
                    unmatched.push_back(segments);
                }
            }
            7 => mapping[8] = Some(segments),
            _ => panic!("Invalid segments!"),
        }
    }

    line.split(' ')
        .skip_while(|&s| s != "|")
        .skip(1)
        .map(|s| {
            let segments = Segments::from(s);
            mapping.iter().position(|s| &Some(segments) == s).unwrap()
        })
        .fold(0, |acc, n| acc * 10 + n)
}

pub fn part2(input: &mut dyn Read) -> String {
    let mut reader = LineIter::new(input);
    let mut total = 0;

    // Allocate work memory outside the decode function so we can reuse it
    let mut work_buffer = VecDeque::new();

    while let Some(line) = reader.next() {
        total += decode(line, &mut work_buffer);
    }

    total.to_string()
}

#[cfg(test)]
mod tests {
    use crate::test_implementation;

    use super::*;

    const SAMPLE1: &[u8] =
        &*b"acedgfb cdfbe gcdfa fbcad dab cefabd cdfgeb eafb cagedb ab | cdfeb fcadb cdfeb cdbaf";
    const SAMPLE2: &[u8] = include_bytes!("samples/08.txt");

    #[test]
    fn sample_part1() {
        test_implementation(part1, SAMPLE1, 0);
        test_implementation(part1, SAMPLE2, 26);
    }

    #[test]
    fn sample_part2() {
        test_implementation(part2, SAMPLE1, 5353);
        test_implementation(part2, SAMPLE2, 61229);
    }
}
<|endoftext|>


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

//! The implementation of the DOM.
//!
//! The DOM is comprised of interfaces (defined by specifications using
//! [WebIDL](https://heycam.github.io/webidl/)) that are implemented as Rust
//! structs in submodules of this module. Its implementation is documented
//! below.
//!
//! A DOM object and its reflector
//! ==============================
//!
//! The implementation of an interface `Foo` in Servo's DOM involves two
//! related but distinct objects:
//!
//! * the **DOM object**: an instance of the Rust struct `dom::foo::Foo`
//!   (marked with the `#[dom_struct]` attribute) on the Rust heap;
//! * the **reflector**: a `JSObject` allocated by SpiderMonkey, that owns the
//!   DOM object.
//!
//! Memory management
//! =================
//!
//! Reflectors of DOM objects, and thus the DOM objects themselves, are managed
//! by the SpiderMonkey Garbage Collector. Thus, keeping alive a DOM object
//! is done through its reflector.
//!
//! For more information, see:
//!
//! * rooting pointers on the stack:
//!   the [`Root`](bindings/js/struct.Root.html) smart pointer;
//! * tracing pointers in member fields: the [`JS`](bindings/js/struct.JS.html),
//!   [`MutNullableJS`](bindings/js/struct.MutNullableJS.html) and
//!   [`MutHeap`](bindings/js/struct.MutHeap.html) smart pointers and
//!   [the tracing implementation](bindings/trace/index.html);
//! * rooting pointers from across thread boundaries or in channels: the
//!   [`Trusted`](bindings/refcounted/struct.Trusted.html) smart pointer;
//! * extracting pointers to DOM objects from their reflectors: the
//!   [`Unrooted`](bindings/js/struct.Unrooted.html) smart pointer.
//!
//! Inheritance
//! ===========
//!
//! Rust does not support struct inheritance, as would be used for the
//! object-oriented DOM APIs. To work around this issue, Servo stores an
//! instance of the superclass in the first field of its subclasses. (Note that
//! it is stored by value, rather than in a smart pointer such as `JS<T>`.)
//!
//! This implies that a pointer to an object can safely be cast to a pointer
//! to all its classes.
//!
//! This invariant is enforced by the lint in
//! `plugins::lints::inheritance_integrity`.
//!
//! Interfaces which either derive from or are derived by other interfaces
//! implement the `Castable` trait, which provides three methods `is::<T>()`,
//! `downcast::<T>()` and `upcast::<T>()` to cast across the type hierarchy
//! and check whether a given instance is of a given type.
//!
//! ```ignore
//! use dom::bindings::inheritance::Castable;
//! use dom::element::Element;
//! use dom::htmlelement::HTMLElement;
//! use dom::htmlinputelement::HTMLInputElement;
//!
//! if let Some(elem) = node.downcast::<Element> {
//!     if elem.is::<HTMLInputElement>() {
//!         return elem.upcast::<HTMLElement>();
//!     }
//! }
//! ```
//!
//! Furthermore, when discriminating a given instance against multiple
//! interface types, code generation provides a convenient TypeId enum
//! which can be used to write `match` expressions instead of multiple
//! calls to `Castable::is::<T>`. The `type_id()` method of an instance is
//! provided by the farthest interface it derives from, e.g. `EventTarget`
//! for `HTMLMediaElement`. For convenience, that method is also provided
//! on the `Node` interface to avoid unnecessary upcasts to `EventTarget`.
//!
//! ```ignore
//! use dom::bindings::inheritance::{EventTargetTypeId, NodeTypeId};
//!
//! match *node.type_id() {
//!     EventTargetTypeId::Node(NodeTypeId::CharacterData(_)) => ...,
//!     EventTargetTypeId::Node(NodeTypeId::Element(_)) => ...,
//!     ...,
//! }
//! ```
//!
//! Construction
//! ============
//!
//! DOM objects of type `T` in Servo have two constructors:
//!
//! * a `T::new_inherited` static method that returns a plain `T`, and
//! * a `T::new` static method that returns `Root<T>`.
//!
//! (The result of either method can be wrapped in `Result`, if that is
//! appropriate for the type in question.)
//!
//! The latter calls the former, boxes the result, and creates a reflector
//! corresponding to it by calling `dom::bindings::utils::reflect_dom_object`
//! (which yields ownership of the object to the SpiderMonkey Garbage Collector).
//! This is the API to use when creating a DOM object.
//!
//! The former should only be called by the latter, and by subclasses'
//! `new_inherited` methods.
//!
//! DOM object constructors in JavaScript correspond to a `T::Constructor`
//! static method. This method is always fallible.
//!
//! Destruction
//! ===========
//!
//! When the SpiderMonkey Garbage Collector discovers that the reflector of a
//! DOM object is garbage, it calls the reflector's finalization hook. This
//! function deletes the reflector's DOM object, calling its destructor in the
//! process.
//!
//! Mutability and aliasing
//! =======================
//!
//! Reflectors are JavaScript objects, and as such can be freely aliased. As
//! Rust does not allow mutable aliasing, mutable borrows of DOM objects are
//! not allowed. In particular, any mutable fields use `Cell` or `DOMRefCell`
//! to manage their mutability.
//!
//! `Reflector` and `Reflectable`
//! =============================
//!
//! Every DOM object has a `Reflector` as its first (transitive) member field.
//! This contains a `*mut JSObject` that points to its reflector.
//!
//! The `FooBinding::Wrap` function creates the reflector, stores a pointer to
//! the DOM object in the reflector, and initializes the pointer to the reflector
//! in the `Reflector` field.
//!
//! The `Reflectable` trait provides a `reflector()` method that returns the
//! DOM object's `Reflector`. It is implemented automatically for DOM structs
//! through the `#[dom_struct]` attribute.
//!
//! Implementing methods for a DOM object
//! =====================================
//!
//! * `dom::bindings::codegen::Bindings::FooBindings::FooMethods` for methods
//!   defined through IDL;
//! * `&self` public methods for public helpers;
//! * `&self` methods for private helpers.
//!
//! Accessing fields of a DOM object
//! ================================
//!
//! All fields of DOM objects are private; accessing them from outside their
//! module is done through explicit getter or setter methods.
//!
//! Inheritance and casting
//! =======================
//!
//! For all DOM interfaces `Foo` in an inheritance chain, a
//! `dom::bindings::inheritance::FooCast` provides methods to cast
//! to other types in the inheritance chain. For example:
//!
//! ```ignore
//! # use script::dom::bindings::inheritance::{NodeCast, HTMLElementCast};
//! # use script::dom::element::Element;
//! # use script::dom::node::Node;
//! # use script::dom::htmlelement::HTMLElement;
//! fn f(element: &Element) {
//!     let base = element.upcast::<Node>();
//!     let derived = element.downcast::<HTMLElement>();
//! }
//! ```
//!
//! Adding a new DOM interface
//! ==========================
//!
//! Adding a new interface `Foo` requires at least the following:
//!
//! * adding the new IDL file at `components/script/dom/webidls/Foo.webidl`;
//! * creating `components/script/dom/foo.rs`;
//! * listing `foo.rs` in `components/script/dom/mod.rs`;
//! * defining the DOM struct `Foo` with a `#[dom_struct]` attribute, a
//!   superclass or `Reflector` member, and other members as appropriate;
//! * implementing the
//!   `dom::bindings::codegen::Bindings::FooBindings::FooMethods` trait for
//!   `&'a Foo`;
//! * adding/updating the match arm in create_element in
//!   `components/script/dom/create.rs` (only applicable to new `HTMLElement`s)
//!
//! More information is available in the [bindings module](bindings/index.html).
//!
//! Accessing DOM objects from layout
//! =================================
//!
//! Layout code can access the DOM through the
//! [`LayoutJS`](bindings/js/struct.LayoutJS.html) smart pointer. This does not
//! keep the DOM object alive; we ensure that no DOM code (Garbage Collection
//! in particular) runs while the layout thread is accessing the DOM.
//!
//! Methods accessible to layout are implemented on `LayoutJS<Foo>` using
//! `LayoutFooHelpers` traits.

#[macro_use]
pub mod macros;

pub mod types {
    include!(concat!(env!("OUT_DIR"), "/InterfaceTypes.rs"));
}

pub mod activation;
pub mod attr;
mod create;
#[allow(unsafe_code)]
#[deny(missing_docs, non_snake_case)]
pub mod bindings;
pub mod blob;
pub mod browsingcontext;
pub mod canvasgradient;
pub mod canvaspattern;
pub mod canvasrenderingcontext2d;
pub mod characterdata;
pub mod closeevent;
pub mod comment;
pub mod console;
pub mod crypto;
pub mod css;
pub mod cssstyledeclaration;
pub mod customevent;
pub mod dedicatedworkerglobalscope;
pub mod document;
pub mod documentfragment;
pub mod documenttype;
pub mod domexception;
pub mod domimplementation;
pub mod domparser;
pub mod dompoint;
pub mod dompointreadonly;
pub mod domquad;
pub mod domrect;
pub mod domrectlist;
pub mod domrectreadonly;
pub mod domstringmap;
pub mod domtokenlist;
pub mod element;
pub mod errorevent;
pub mod event;
pub mod eventdispatcher;
pub mod eventsource;
pub mod eventtarget;
pub mod file;
pub mod filelist;
pub mod filereader;
pub mod focusevent;
pub mod formdata;
pub mod htmlanchorelement;
pub mod htmlappletelement;
pub mod htmlareaelement;
pub mod htmlaudioelement;
pub mod htmlbaseelement;
pub mod htmlbodyelement;
pub mod htmlbrelement;
pub mod htmlbuttonelement;
pub mod htmlcanvaselement;
pub mod htmlcollection;
pub mod htmldataelement;
pub mod htmldatalistelement;
pub mod htmldetailselement;
pub mod htmldialogelement;
pub mod htmldirectoryelement;
pub mod htmldivelement;
pub mod htmldlistelement;
pub mod htmlelement;
pub mod htmlembedelement;
pub mod htmlfieldsetelement;
pub mod htmlfontelement;
pub mod htmlformcontrolscollection;
pub mod htmlformelement;
pub mod htmlframeelement;
pub mod htmlframesetelement;
pub mod htmlheadelement;
pub mod htmlheadingelement;
pub mod htmlhrelement;
pub mod htmlhtmlelement;
pub mod htmliframeelement;
pub mod htmlimageelement;
pub mod htmlinputelement;
pub mod htmllabelelement;
pub mod htmllegendelement;
pub mod htmllielement;
pub mod htmllinkelement;
pub mod htmlmapelement;
pub mod htmlmediaelement;
pub mod htmlmetaelement;
pub mod htmlmeterelement;
pub mod htmlmodelement;
pub mod htmlobjectelement;
pub mod htmlolistelement;
pub mod htmloptgroupelement;
pub mod htmloptionelement;
pub mod htmloutputelement;
pub mod htmlparagraphelement;
pub mod htmlparamelement;
pub mod htmlpreelement;
pub mod htmlprogresselement;
pub mod htmlquoteelement;
pub mod htmlscriptelement;
pub mod htmlselectelement;
pub mod htmlsourceelement;
pub mod htmlspanelement;
pub mod htmlstyleelement;
pub mod htmltablecaptionelement;
pub mod htmltablecellelement;
pub mod htmltablecolelement;
pub mod htmltabledatacellelement;
pub mod htmltableelement;
pub mod htmltableheadercellelement;
pub mod htmltablerowelement;
pub mod htmltablesectionelement;
pub mod htmltemplateelement;
pub mod htmltextareaelement;
pub mod htmltimeelement;
pub mod htmltitleelement;
pub mod htmltrackelement;
pub mod htmlulistelement;
pub mod htmlunknownelement;
pub mod htmlvideoelement;
pub mod imagedata;
pub mod keyboardevent;
pub mod location;
pub mod messageevent;
pub mod mouseevent;
pub mod namednodemap;
pub mod navigator;
pub mod navigatorinfo;
pub mod node;
pub mod nodeiterator;
pub mod nodelist;
pub mod performance;
pub mod performancetiming;
pub mod processinginstruction;
pub mod progressevent;
pub mod radionodelist;
pub mod range;
pub mod screen;
pub mod servohtmlparser;
pub mod servoxmlparser;
pub mod storage;
pub mod storageevent;
pub mod testbinding;
pub mod testbindingproxy;
pub mod text;
pub mod textdecoder;
pub mod textencoder;
pub mod touch;
pub mod touchevent;
pub mod touchlist;
pub mod treewalker;
pub mod uievent;
pub mod url;
pub mod urlhelper;
pub mod urlsearchparams;
pub mod userscripts;
pub mod validitystate;
pub mod values;
pub mod virtualmethods;
pub mod webglactiveinfo;
pub mod webglbuffer;
pub mod webglcontextevent;
pub mod webglframebuffer;
pub mod webglobject;
pub mod webglprogram;
pub mod webglrenderbuffer;
pub mod webglrenderingcontext;
pub mod webglshader;
pub mod webglshaderprecisionformat;
pub mod webgltexture;
pub mod webgluniformlocation;
pub mod websocket;
pub mod window;
pub mod worker;
pub mod workerglobalscope;
pub mod workerlocation;
pub mod workernavigator;
pub mod xmldocument;
pub mod xmlhttprequest;
pub mod xmlhttprequesteventtarget;
pub mod xmlhttprequestupload;
<commit_msg>Fix some errors in the DOM documentation.<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/. */

//! The implementation of the DOM.
//!
//! The DOM is comprised of interfaces (defined by specifications using
//! [WebIDL](https://heycam.github.io/webidl/)) that are implemented as Rust
//! structs in submodules of this module. Its implementation is documented
//! below.
//!
//! A DOM object and its reflector
//! ==============================
//!
//! The implementation of an interface `Foo` in Servo's DOM involves two
//! related but distinct objects:
//!
//! * the **DOM object**: an instance of the Rust struct `dom::foo::Foo`
//!   (marked with the `#[dom_struct]` attribute) on the Rust heap;
//! * the **reflector**: a `JSObject` allocated by SpiderMonkey, that owns the
//!   DOM object.
//!
//! Memory management
//! =================
//!
//! Reflectors of DOM objects, and thus the DOM objects themselves, are managed
//! by the SpiderMonkey Garbage Collector. Thus, keeping alive a DOM object
//! is done through its reflector.
//!
//! For more information, see:
//!
//! * rooting pointers on the stack:
//!   the [`Root`](bindings/js/struct.Root.html) smart pointer;
//! * tracing pointers in member fields: the [`JS`](bindings/js/struct.JS.html),
//!   [`MutNullableJS`](bindings/js/struct.MutNullableJS.html) and
//!   [`MutHeap`](bindings/js/struct.MutHeap.html) smart pointers and
//!   [the tracing implementation](bindings/trace/index.html);
//! * rooting pointers from across thread boundaries or in channels: the
//!   [`Trusted`](bindings/refcounted/struct.Trusted.html) smart pointer;
//! * extracting pointers to DOM objects from their reflectors: the
//!   [`Unrooted`](bindings/js/struct.Unrooted.html) smart pointer.
//!
//! Inheritance
//! ===========
//!
//! Rust does not support struct inheritance, as would be used for the
//! object-oriented DOM APIs. To work around this issue, Servo stores an
//! instance of the superclass in the first field of its subclasses. (Note that
//! it is stored by value, rather than in a smart pointer such as `JS<T>`.)
//!
//! This implies that a pointer to an object can safely be cast to a pointer
//! to all its classes.
//!
//! This invariant is enforced by the lint in
//! `plugins::lints::inheritance_integrity`.
//!
//! Interfaces which either derive from or are derived by other interfaces
//! implement the `Castable` trait, which provides three methods `is::<T>()`,
//! `downcast::<T>()` and `upcast::<T>()` to cast across the type hierarchy
//! and check whether a given instance is of a given type.
//!
//! ```ignore
//! use dom::bindings::inheritance::Castable;
//! use dom::element::Element;
//! use dom::htmlelement::HTMLElement;
//! use dom::htmlinputelement::HTMLInputElement;
//!
//! if let Some(elem) = node.downcast::<Element> {
//!     if elem.is::<HTMLInputElement>() {
//!         return elem.upcast::<HTMLElement>();
//!     }
//! }
//! ```
//!
//! Furthermore, when discriminating a given instance against multiple
//! interface types, code generation provides a convenient TypeId enum
//! which can be used to write `match` expressions instead of multiple
//! calls to `Castable::is::<T>`. The `type_id()` method of an instance is
//! provided by the farthest interface it derives from, e.g. `EventTarget`
//! for `HTMLMediaElement`. For convenience, that method is also provided
//! on the `Node` interface to avoid unnecessary upcasts to `EventTarget`.
//!
//! ```ignore
//! use dom::bindings::inheritance::{EventTargetTypeId, NodeTypeId};
//!
//! match *node.type_id() {
//!     EventTargetTypeId::Node(NodeTypeId::CharacterData(_)) => ...,
//!     EventTargetTypeId::Node(NodeTypeId::Element(_)) => ...,
//!     ...,
//! }
//! ```
//!
//! Construction
//! ============
//!
//! DOM objects of type `T` in Servo have two constructors:
//!
//! * a `T::new_inherited` static method that returns a plain `T`, and
//! * a `T::new` static method that returns `Root<T>`.
//!
//! (The result of either method can be wrapped in `Result`, if that is
//! appropriate for the type in question.)
//!
//! The latter calls the former, boxes the result, and creates a reflector
//! corresponding to it by calling `dom::bindings::utils::reflect_dom_object`
//! (which yields ownership of the object to the SpiderMonkey Garbage Collector).
//! This is the API to use when creating a DOM object.
//!
//! The former should only be called by the latter, and by subclasses'
//! `new_inherited` methods.
//!
//! DOM object constructors in JavaScript correspond to a `T::Constructor`
//! static method. This method is always fallible.
//!
//! Destruction
//! ===========
//!
//! When the SpiderMonkey Garbage Collector discovers that the reflector of a
//! DOM object is garbage, it calls the reflector's finalization hook. This
//! function deletes the reflector's DOM object, calling its destructor in the
//! process.
//!
//! Mutability and aliasing
//! =======================
//!
//! Reflectors are JavaScript objects, and as such can be freely aliased. As
//! Rust does not allow mutable aliasing, mutable borrows of DOM objects are
//! not allowed. In particular, any mutable fields use `Cell` or `DOMRefCell`
//! to manage their mutability.
//!
//! `Reflector` and `Reflectable`
//! =============================
//!
//! Every DOM object has a `Reflector` as its first (transitive) member field.
//! This contains a `*mut JSObject` that points to its reflector.
//!
//! The `FooBinding::Wrap` function creates the reflector, stores a pointer to
//! the DOM object in the reflector, and initializes the pointer to the reflector
//! in the `Reflector` field.
//!
//! The `Reflectable` trait provides a `reflector()` method that returns the
//! DOM object's `Reflector`. It is implemented automatically for DOM structs
//! through the `#[dom_struct]` attribute.
//!
//! Implementing methods for a DOM object
//! =====================================
//!
//! * `dom::bindings::codegen::Bindings::FooBindings::FooMethods` for methods
//!   defined through IDL;
//! * `&self` public methods for public helpers;
//! * `&self` methods for private helpers.
//!
//! Accessing fields of a DOM object
//! ================================
//!
//! All fields of DOM objects are private; accessing them from outside their
//! module is done through explicit getter or setter methods.
//!
//! Inheritance and casting
//! =======================
//!
//! For all DOM interfaces `Foo` in an inheritance chain, a
//! `dom::bindings::inheritance::FooCast` provides methods to cast
//! to other types in the inheritance chain. For example:
//!
//! ```ignore
//! # use script::dom::bindings::inheritance::{NodeCast, HTMLElementCast};
//! # use script::dom::element::Element;
//! # use script::dom::node::Node;
//! # use script::dom::htmlelement::HTMLElement;
//! fn f(element: &Element) {
//!     let base = element.upcast::<Node>();
//!     let derived = element.downcast::<HTMLElement>();
//! }
//! ```
//!
//! Adding a new DOM interface
//! ==========================
//!
//! Adding a new interface `Foo` requires at least the following:
//!
//! * adding the new IDL file at `components/script/dom/webidls/Foo.webidl`;
//! * creating `components/script/dom/foo.rs`;
//! * listing `foo.rs` in `components/script/dom/mod.rs`;
//! * defining the DOM struct `Foo` with a `#[dom_struct]` attribute, a
//!   superclass or `Reflector` member, and other members as appropriate;
//! * implementing the
//!   `dom::bindings::codegen::Bindings::FooBindings::FooMethods` trait for
//!   `Foo`;
//! * adding/updating the match arm in create_element in
//!   `components/script/dom/create.rs` (only applicable to new types inheriting
//!   from `HTMLElement`)
//!
//! More information is available in the [bindings module](bindings/index.html).
//!
//! Accessing DOM objects from layout
//! =================================
//!
//! Layout code can access the DOM through the
//! [`LayoutJS`](bindings/js/struct.LayoutJS.html) smart pointer. This does not
//! keep the DOM object alive; we ensure that no DOM code (Garbage Collection
//! in particular) runs while the layout thread is accessing the DOM.
//!
//! Methods accessible to layout are implemented on `LayoutJS<Foo>` using
//! `LayoutFooHelpers` traits.

#[macro_use]
pub mod macros;

pub mod types {
    include!(concat!(env!("OUT_DIR"), "/InterfaceTypes.rs"));
}

pub mod activation;
pub mod attr;
mod create;
#[allow(unsafe_code)]
#[deny(missing_docs, non_snake_case)]
pub mod bindings;
pub mod blob;
pub mod browsingcontext;
pub mod canvasgradient;
pub mod canvaspattern;
pub mod canvasrenderingcontext2d;
pub mod characterdata;
pub mod closeevent;
pub mod comment;
pub mod console;
pub mod crypto;
pub mod css;
pub mod cssstyledeclaration;
pub mod customevent;
pub mod dedicatedworkerglobalscope;
pub mod document;
pub mod documentfragment;
pub mod documenttype;
pub mod domexception;
pub mod domimplementation;
pub mod domparser;
pub mod dompoint;
pub mod dompointreadonly;
pub mod domquad;
pub mod domrect;
pub mod domrectlist;
pub mod domrectreadonly;
pub mod domstringmap;
pub mod domtokenlist;
pub mod element;
pub mod errorevent;
pub mod event;
pub mod eventdispatcher;
pub mod eventsource;
pub mod eventtarget;
pub mod file;
pub mod filelist;
pub mod filereader;
pub mod focusevent;
pub mod formdata;
pub mod htmlanchorelement;
pub mod htmlappletelement;
pub mod htmlareaelement;
pub mod htmlaudioelement;
pub mod htmlbaseelement;
pub mod htmlbodyelement;
pub mod htmlbrelement;
pub mod htmlbuttonelement;
pub mod htmlcanvaselement;
pub mod htmlcollection;
pub mod htmldataelement;
pub mod htmldatalistelement;
pub mod htmldetailselement;
pub mod htmldialogelement;
pub mod htmldirectoryelement;
pub mod htmldivelement;
pub mod htmldlistelement;
pub mod htmlelement;
pub mod htmlembedelement;
pub mod htmlfieldsetelement;
pub mod htmlfontelement;
pub mod htmlformcontrolscollection;
pub mod htmlformelement;
pub mod htmlframeelement;
pub mod htmlframesetelement;
pub mod htmlheadelement;
pub mod htmlheadingelement;
pub mod htmlhrelement;
pub mod htmlhtmlelement;
pub mod htmliframeelement;
pub mod htmlimageelement;
pub mod htmlinputelement;
pub mod htmllabelelement;
pub mod htmllegendelement;
pub mod htmllielement;
pub mod htmllinkelement;
pub mod htmlmapelement;
pub mod htmlmediaelement;
pub mod htmlmetaelement;
pub mod htmlmeterelement;
pub mod htmlmodelement;
pub mod htmlobjectelement;
pub mod htmlolistelement;
pub mod htmloptgroupelement;
pub mod htmloptionelement;
pub mod htmloutputelement;
pub mod htmlparagraphelement;
pub mod htmlparamelement;
pub mod htmlpreelement;
pub mod htmlprogresselement;
pub mod htmlquoteelement;
pub mod htmlscriptelement;
pub mod htmlselectelement;
pub mod htmlsourceelement;
pub mod htmlspanelement;
pub mod htmlstyleelement;
pub mod htmltablecaptionelement;
pub mod htmltablecellelement;
pub mod htmltablecolelement;
pub mod htmltabledatacellelement;
pub mod htmltableelement;
pub mod htmltableheadercellelement;
pub mod htmltablerowelement;
pub mod htmltablesectionelement;
pub mod htmltemplateelement;
pub mod htmltextareaelement;
pub mod htmltimeelement;
pub mod htmltitleelement;
pub mod htmltrackelement;
pub mod htmlulistelement;
pub mod htmlunknownelement;
pub mod htmlvideoelement;
pub mod imagedata;
pub mod keyboardevent;
pub mod location;
pub mod messageevent;
pub mod mouseevent;
pub mod namednodemap;
pub mod navigator;
pub mod navigatorinfo;
pub mod node;
pub mod nodeiterator;
pub mod nodelist;
pub mod performance;
pub mod performancetiming;
pub mod processinginstruction;
pub mod progressevent;
pub mod radionodelist;
pub mod range;
pub mod screen;
pub mod servohtmlparser;
pub mod servoxmlparser;
pub mod storage;
pub mod storageevent;
pub mod testbinding;
pub mod testbindingproxy;
pub mod text;
pub mod textdecoder;
pub mod textencoder;
pub mod touch;
pub mod touchevent;
pub mod touchlist;
pub mod treewalker;
pub mod uievent;
pub mod url;
pub mod urlhelper;
pub mod urlsearchparams;
pub mod userscripts;
pub mod validitystate;
pub mod values;
pub mod virtualmethods;
pub mod webglactiveinfo;
pub mod webglbuffer;
pub mod webglcontextevent;
pub mod webglframebuffer;
pub mod webglobject;
pub mod webglprogram;
pub mod webglrenderbuffer;
pub mod webglrenderingcontext;
pub mod webglshader;
pub mod webglshaderprecisionformat;
pub mod webgltexture;
pub mod webgluniformlocation;
pub mod websocket;
pub mod window;
pub mod worker;
pub mod workerglobalscope;
pub mod workerlocation;
pub mod workernavigator;
pub mod xmldocument;
pub mod xmlhttprequest;
pub mod xmlhttprequesteventtarget;
pub mod xmlhttprequestupload;
<|endoftext|>


<commit_before>use std::{
    collections::BTreeMap,
    convert::{TryFrom, TryInto},
    fmt,
};

use js_int::Int;
use serde::{de::Deserializer, ser::Serializer, Deserialize, Serialize};
use serde_json::{to_string as to_json_string, Value as JsonValue};

use super::Error;

/// The inner type of `CanonicalJsonValue::Object`.
pub type Object = BTreeMap<String, CanonicalJsonValue>;

/// Represents a canonical JSON value as per the Matrix specification.
#[derive(Clone, Eq, PartialEq)]
pub enum CanonicalJsonValue {
    /// Represents a JSON null value.
    ///
    /// ```
    /// # use serde_json::json;
    /// # use std::convert::TryInto;
    /// # use ruma_serde::CanonicalJsonValue;
    /// let v: CanonicalJsonValue = json!(null).try_into().unwrap();
    /// ```
    Null,

    /// Represents a JSON boolean.
    ///
    /// ```
    /// # use serde_json::json;
    /// # use std::convert::TryInto;
    /// # use ruma_serde::CanonicalJsonValue;
    /// let v: CanonicalJsonValue = json!(true).try_into().unwrap();
    /// ```
    Bool(bool),

    /// Represents a JSON integer.
    ///
    /// ```
    /// # use serde_json::json;
    /// # use std::convert::TryInto;
    /// # use ruma_serde::CanonicalJsonValue;
    /// let v: CanonicalJsonValue = json!(12).try_into().unwrap();
    /// ```
    Integer(Int),

    /// Represents a JSON string.
    ///
    /// ```
    /// # use serde_json::json;
    /// # use std::convert::TryInto;
    /// # use ruma_serde::CanonicalJsonValue;
    /// let v: CanonicalJsonValue = json!("a string").try_into().unwrap();
    /// ```
    String(String),

    /// Represents a JSON array.
    ///
    /// ```
    /// # use serde_json::json;
    /// # use std::convert::TryInto;
    /// # use ruma_serde::CanonicalJsonValue;
    /// let v: CanonicalJsonValue = json!(["an", "array"]).try_into().unwrap();
    /// ```
    Array(Vec<CanonicalJsonValue>),

    /// Represents a JSON object.
    ///
    /// The map is backed by a BTreeMap to guarantee the sorting of keys.
    ///
    /// ```
    /// # use serde_json::json;
    /// # use std::convert::TryInto;
    /// # use ruma_serde::CanonicalJsonValue;
    /// let v: CanonicalJsonValue = json!({ "an": "object" }).try_into().unwrap();
    /// ```
    Object(Object),
}

impl CanonicalJsonValue {
    /// If the `CanonicalJsonValue` is a `Bool`, return the inner value.
    pub fn as_bool(&self) -> Option<bool> {
        match self {
            Self::Bool(b) => Some(*b),
            _ => None,
        }
    }

    /// If the `CanonicalJsonValue` is an `Integer`, return the inner value.
    pub fn as_integer(&self) -> Option<Int> {
        match self {
            Self::Integer(i) => Some(*i),
            _ => None,
        }
    }

    /// If the `CanonicalJsonValue` is a `String`, return a reference to the inner value.
    pub fn as_str(&self) -> Option<&str> {
        match self {
            Self::String(s) => Some(s),
            _ => None,
        }
    }

    /// If the `CanonicalJsonValue` is an `Array`, return a reference to the inner value.
    pub fn as_array(&self) -> Option<&[CanonicalJsonValue]> {
        match self {
            Self::Array(a) => Some(a),
            _ => None,
        }
    }

    /// If the `CanonicalJsonValue` is an `Object`, return a reference to the inner value.
    pub fn as_object(&self) -> Option<&Object> {
        match self {
            Self::Object(o) => Some(o),
            _ => None,
        }
    }

    /// If the `CanonicalJsonValue` is an `Array`, return a mutable reference to the inner value.
    pub fn as_array_mut(&mut self) -> Option<&mut Vec<CanonicalJsonValue>> {
        match self {
            Self::Array(a) => Some(a),
            _ => None,
        }
    }

    /// If the `CanonicalJsonValue` is an `Object`, return a mutable reference to the inner value.
    pub fn as_object_mut(&mut self) -> Option<&mut Object> {
        match self {
            Self::Object(o) => Some(o),
            _ => None,
        }
    }

    /// Returns `true` if the `CanonicalJsonValue` is a `Bool`.
    pub fn is_bool(&self) -> bool {
        matches!(self, Self::Bool(_))
    }

    /// Returns `true` if the `CanonicalJsonValue` is an `Integer`.
    pub fn is_integer(&self) -> bool {
        matches!(self, Self::Integer(_))
    }

    /// Returns `true` if the `CanonicalJsonValue` is a `String`.
    pub fn is_string(&self) -> bool {
        matches!(self, Self::String(_))
    }

    /// Returns `true` if the `CanonicalJsonValue` is an `Array`.
    pub fn is_array(&self) -> bool {
        matches!(self, Self::Array(_))
    }

    /// Returns `true` if the `CanonicalJsonValue` is an `Object`.
    pub fn is_object(&self) -> bool {
        matches!(self, Self::Object(_))
    }
}

impl Default for CanonicalJsonValue {
    fn default() -> Self {
        Self::Null
    }
}

impl fmt::Debug for CanonicalJsonValue {
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        match *self {
            Self::Null => formatter.debug_tuple("Null").finish(),
            Self::Bool(v) => formatter.debug_tuple("Bool").field(&v).finish(),
            Self::Integer(ref v) => fmt::Debug::fmt(v, formatter),
            Self::String(ref v) => formatter.debug_tuple("String").field(v).finish(),
            Self::Array(ref v) => {
                formatter.write_str("Array(")?;
                fmt::Debug::fmt(v, formatter)?;
                formatter.write_str(")")
            }
            Self::Object(ref v) => {
                formatter.write_str("Object(")?;
                fmt::Debug::fmt(v, formatter)?;
                formatter.write_str(")")
            }
        }
    }
}

impl fmt::Display for CanonicalJsonValue {
    /// Display this value as a string.
    ///
    /// This `Display` implementation is intentionally unaffected by any formatting parameters,
    /// because adding extra whitespace or otherwise pretty-printing it would make it not the
    /// canonical form anymore.
    ///
    /// If you want to pretty-print a `CanonicalJsonValue` for debugging purposes, use
    /// one of `serde_json::{to_string_pretty, to_vec_pretty, to_writer_pretty}`.
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", to_json_string(&self).map_err(|_| fmt::Error)?)
    }
}

impl TryFrom<JsonValue> for CanonicalJsonValue {
    type Error = Error;

    fn try_from(val: JsonValue) -> Result<Self, Self::Error> {
        Ok(match val {
            JsonValue::Bool(b) => Self::Bool(b),
            JsonValue::Number(num) => Self::Integer(
                Int::try_from(num.as_i64().ok_or(Error::IntConvert)?)
                    .map_err(|_| Error::IntConvert)?,
            ),
            JsonValue::Array(vec) => {
                Self::Array(vec.into_iter().map(TryInto::try_into).collect::<Result<Vec<_>, _>>()?)
            }
            JsonValue::String(string) => Self::String(string),
            JsonValue::Object(obj) => Self::Object(
                obj.into_iter()
                    .map(|(k, v)| Ok((k, v.try_into()?)))
                    .collect::<Result<Object, _>>()?,
            ),
            JsonValue::Null => Self::Null,
        })
    }
}

impl From<CanonicalJsonValue> for JsonValue {
    fn from(val: CanonicalJsonValue) -> Self {
        match val {
            CanonicalJsonValue::Bool(b) => Self::Bool(b),
            CanonicalJsonValue::Integer(int) => Self::Number(i64::from(int).into()),
            CanonicalJsonValue::String(string) => Self::String(string),
            CanonicalJsonValue::Array(vec) => {
                Self::Array(vec.into_iter().map(Into::into).collect())
            }
            CanonicalJsonValue::Object(obj) => {
                Self::Object(obj.into_iter().map(|(k, v)| (k, v.into())).collect())
            }
            CanonicalJsonValue::Null => Self::Null,
        }
    }
}

impl Serialize for CanonicalJsonValue {
    #[inline]
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        match self {
            Self::Null => serializer.serialize_unit(),
            Self::Bool(b) => serializer.serialize_bool(*b),
            Self::Integer(n) => n.serialize(serializer),
            Self::String(s) => serializer.serialize_str(s),
            Self::Array(v) => v.serialize(serializer),
            Self::Object(m) => {
                use serde::ser::SerializeMap;
                let mut map = serializer.serialize_map(Some(m.len()))?;
                for (k, v) in m {
                    map.serialize_entry(k, v)?;
                }
                map.end()
            }
        }
    }
}

impl<'de> Deserialize<'de> for CanonicalJsonValue {
    #[inline]
    fn deserialize<D>(deserializer: D) -> Result<CanonicalJsonValue, D::Error>
    where
        D: Deserializer<'de>,
    {
        let val = JsonValue::deserialize(deserializer)?;
        val.try_into().map_err(serde::de::Error::custom)
    }
}

#[cfg(test)]
mod tests {
    use std::convert::TryInto;

    use serde_json::json;

    use super::CanonicalJsonValue;

    #[test]
    fn to_string() {
        const CANONICAL_STR: &str = r#"{"city":"London","street":"10 Downing Street"}"#;

        let json: CanonicalJsonValue =
            json!({ "city": "London", "street": "10 Downing Street" }).try_into().unwrap();

        assert_eq!(format!("{}", json), CANONICAL_STR);
        assert_eq!(format!("{:#}", json), CANONICAL_STR);
    }
}
<commit_msg>serde: Add PartialEq implementations for CanonicalJsonValue<commit_after>use std::{
    collections::BTreeMap,
    convert::{TryFrom, TryInto},
    fmt,
};

use js_int::Int;
use serde::{de::Deserializer, ser::Serializer, Deserialize, Serialize};
use serde_json::{to_string as to_json_string, Value as JsonValue};

use super::Error;

/// The inner type of `CanonicalJsonValue::Object`.
pub type Object = BTreeMap<String, CanonicalJsonValue>;

/// Represents a canonical JSON value as per the Matrix specification.
#[derive(Clone, Eq, PartialEq)]
pub enum CanonicalJsonValue {
    /// Represents a JSON null value.
    ///
    /// ```
    /// # use serde_json::json;
    /// # use std::convert::TryInto;
    /// # use ruma_serde::CanonicalJsonValue;
    /// let v: CanonicalJsonValue = json!(null).try_into().unwrap();
    /// ```
    Null,

    /// Represents a JSON boolean.
    ///
    /// ```
    /// # use serde_json::json;
    /// # use std::convert::TryInto;
    /// # use ruma_serde::CanonicalJsonValue;
    /// let v: CanonicalJsonValue = json!(true).try_into().unwrap();
    /// ```
    Bool(bool),

    /// Represents a JSON integer.
    ///
    /// ```
    /// # use serde_json::json;
    /// # use std::convert::TryInto;
    /// # use ruma_serde::CanonicalJsonValue;
    /// let v: CanonicalJsonValue = json!(12).try_into().unwrap();
    /// ```
    Integer(Int),

    /// Represents a JSON string.
    ///
    /// ```
    /// # use serde_json::json;
    /// # use std::convert::TryInto;
    /// # use ruma_serde::CanonicalJsonValue;
    /// let v: CanonicalJsonValue = json!("a string").try_into().unwrap();
    /// ```
    String(String),

    /// Represents a JSON array.
    ///
    /// ```
    /// # use serde_json::json;
    /// # use std::convert::TryInto;
    /// # use ruma_serde::CanonicalJsonValue;
    /// let v: CanonicalJsonValue = json!(["an", "array"]).try_into().unwrap();
    /// ```
    Array(Vec<CanonicalJsonValue>),

    /// Represents a JSON object.
    ///
    /// The map is backed by a BTreeMap to guarantee the sorting of keys.
    ///
    /// ```
    /// # use serde_json::json;
    /// # use std::convert::TryInto;
    /// # use ruma_serde::CanonicalJsonValue;
    /// let v: CanonicalJsonValue = json!({ "an": "object" }).try_into().unwrap();
    /// ```
    Object(Object),
}

impl CanonicalJsonValue {
    /// If the `CanonicalJsonValue` is a `Bool`, return the inner value.
    pub fn as_bool(&self) -> Option<bool> {
        match self {
            Self::Bool(b) => Some(*b),
            _ => None,
        }
    }

    /// If the `CanonicalJsonValue` is an `Integer`, return the inner value.
    pub fn as_integer(&self) -> Option<Int> {
        match self {
            Self::Integer(i) => Some(*i),
            _ => None,
        }
    }

    /// If the `CanonicalJsonValue` is a `String`, return a reference to the inner value.
    pub fn as_str(&self) -> Option<&str> {
        match self {
            Self::String(s) => Some(s),
            _ => None,
        }
    }

    /// If the `CanonicalJsonValue` is an `Array`, return a reference to the inner value.
    pub fn as_array(&self) -> Option<&[CanonicalJsonValue]> {
        match self {
            Self::Array(a) => Some(a),
            _ => None,
        }
    }

    /// If the `CanonicalJsonValue` is an `Object`, return a reference to the inner value.
    pub fn as_object(&self) -> Option<&Object> {
        match self {
            Self::Object(o) => Some(o),
            _ => None,
        }
    }

    /// If the `CanonicalJsonValue` is an `Array`, return a mutable reference to the inner value.
    pub fn as_array_mut(&mut self) -> Option<&mut Vec<CanonicalJsonValue>> {
        match self {
            Self::Array(a) => Some(a),
            _ => None,
        }
    }

    /// If the `CanonicalJsonValue` is an `Object`, return a mutable reference to the inner value.
    pub fn as_object_mut(&mut self) -> Option<&mut Object> {
        match self {
            Self::Object(o) => Some(o),
            _ => None,
        }
    }

    /// Returns `true` if the `CanonicalJsonValue` is a `Bool`.
    pub fn is_bool(&self) -> bool {
        matches!(self, Self::Bool(_))
    }

    /// Returns `true` if the `CanonicalJsonValue` is an `Integer`.
    pub fn is_integer(&self) -> bool {
        matches!(self, Self::Integer(_))
    }

    /// Returns `true` if the `CanonicalJsonValue` is a `String`.
    pub fn is_string(&self) -> bool {
        matches!(self, Self::String(_))
    }

    /// Returns `true` if the `CanonicalJsonValue` is an `Array`.
    pub fn is_array(&self) -> bool {
        matches!(self, Self::Array(_))
    }

    /// Returns `true` if the `CanonicalJsonValue` is an `Object`.
    pub fn is_object(&self) -> bool {
        matches!(self, Self::Object(_))
    }
}

impl Default for CanonicalJsonValue {
    fn default() -> Self {
        Self::Null
    }
}

impl fmt::Debug for CanonicalJsonValue {
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        match *self {
            Self::Null => formatter.debug_tuple("Null").finish(),
            Self::Bool(v) => formatter.debug_tuple("Bool").field(&v).finish(),
            Self::Integer(ref v) => fmt::Debug::fmt(v, formatter),
            Self::String(ref v) => formatter.debug_tuple("String").field(v).finish(),
            Self::Array(ref v) => {
                formatter.write_str("Array(")?;
                fmt::Debug::fmt(v, formatter)?;
                formatter.write_str(")")
            }
            Self::Object(ref v) => {
                formatter.write_str("Object(")?;
                fmt::Debug::fmt(v, formatter)?;
                formatter.write_str(")")
            }
        }
    }
}

impl fmt::Display for CanonicalJsonValue {
    /// Display this value as a string.
    ///
    /// This `Display` implementation is intentionally unaffected by any formatting parameters,
    /// because adding extra whitespace or otherwise pretty-printing it would make it not the
    /// canonical form anymore.
    ///
    /// If you want to pretty-print a `CanonicalJsonValue` for debugging purposes, use
    /// one of `serde_json::{to_string_pretty, to_vec_pretty, to_writer_pretty}`.
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", to_json_string(&self).map_err(|_| fmt::Error)?)
    }
}

impl TryFrom<JsonValue> for CanonicalJsonValue {
    type Error = Error;

    fn try_from(val: JsonValue) -> Result<Self, Self::Error> {
        Ok(match val {
            JsonValue::Bool(b) => Self::Bool(b),
            JsonValue::Number(num) => Self::Integer(
                Int::try_from(num.as_i64().ok_or(Error::IntConvert)?)
                    .map_err(|_| Error::IntConvert)?,
            ),
            JsonValue::Array(vec) => {
                Self::Array(vec.into_iter().map(TryInto::try_into).collect::<Result<Vec<_>, _>>()?)
            }
            JsonValue::String(string) => Self::String(string),
            JsonValue::Object(obj) => Self::Object(
                obj.into_iter()
                    .map(|(k, v)| Ok((k, v.try_into()?)))
                    .collect::<Result<Object, _>>()?,
            ),
            JsonValue::Null => Self::Null,
        })
    }
}

impl From<CanonicalJsonValue> for JsonValue {
    fn from(val: CanonicalJsonValue) -> Self {
        match val {
            CanonicalJsonValue::Bool(b) => Self::Bool(b),
            CanonicalJsonValue::Integer(int) => Self::Number(i64::from(int).into()),
            CanonicalJsonValue::String(string) => Self::String(string),
            CanonicalJsonValue::Array(vec) => {
                Self::Array(vec.into_iter().map(Into::into).collect())
            }
            CanonicalJsonValue::Object(obj) => {
                Self::Object(obj.into_iter().map(|(k, v)| (k, v.into())).collect())
            }
            CanonicalJsonValue::Null => Self::Null,
        }
    }
}

macro_rules! impl_partial_eq {
    ($variant:ident($ty:ty)) => {
        impl PartialEq<$ty> for CanonicalJsonValue {
            fn eq(&self, other: &$ty) -> bool {
                match self {
                    Self::$variant(val) => val == other,
                    _ => false,
                }
            }
        }

        impl PartialEq<CanonicalJsonValue> for $ty {
            fn eq(&self, other: &CanonicalJsonValue) -> bool {
                match other {
                    CanonicalJsonValue::$variant(val) => self == val,
                    _ => false,
                }
            }
        }
    };
}

impl_partial_eq!(Bool(bool));
impl_partial_eq!(Integer(Int));
impl_partial_eq!(String(String));
impl_partial_eq!(Array(Vec<CanonicalJsonValue>));
impl_partial_eq!(Object(Object));

impl Serialize for CanonicalJsonValue {
    #[inline]
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        match self {
            Self::Null => serializer.serialize_unit(),
            Self::Bool(b) => serializer.serialize_bool(*b),
            Self::Integer(n) => n.serialize(serializer),
            Self::String(s) => serializer.serialize_str(s),
            Self::Array(v) => v.serialize(serializer),
            Self::Object(m) => {
                use serde::ser::SerializeMap;
                let mut map = serializer.serialize_map(Some(m.len()))?;
                for (k, v) in m {
                    map.serialize_entry(k, v)?;
                }
                map.end()
            }
        }
    }
}

impl<'de> Deserialize<'de> for CanonicalJsonValue {
    #[inline]
    fn deserialize<D>(deserializer: D) -> Result<CanonicalJsonValue, D::Error>
    where
        D: Deserializer<'de>,
    {
        let val = JsonValue::deserialize(deserializer)?;
        val.try_into().map_err(serde::de::Error::custom)
    }
}

#[cfg(test)]
mod tests {
    use std::convert::TryInto;

    use serde_json::json;

    use super::CanonicalJsonValue;

    #[test]
    fn to_string() {
        const CANONICAL_STR: &str = r#"{"city":"London","street":"10 Downing Street"}"#;

        let json: CanonicalJsonValue =
            json!({ "city": "London", "street": "10 Downing Street" }).try_into().unwrap();

        assert_eq!(format!("{}", json), CANONICAL_STR);
        assert_eq!(format!("{:#}", json), CANONICAL_STR);
    }
}
<|endoftext|>


<commit_before>import std::io;
import std::str;
import std::option;
import std::fs;
import std::os;
import std::vec;
import std::result;

import common::mode_run_pass;
import common::mode_run_fail;
import common::mode_compile_fail;
import common::mode_pretty;
import common::cx;
import common::config;
import header::load_props;
import header::test_props;
import util::logv;

export run;

fn run(cx: cx, -_testfile: [u8]) {
    let testfile = str::unsafe_from_bytes(_testfile);
    if cx.config.verbose {
        // We're going to be dumping a lot of info. Start on a new line.
        io::stdout().write_str("\n\n");
    }
    log #fmt["running %s", testfile];
    let props = load_props(testfile);
    alt cx.config.mode {
      mode_compile_fail. { run_cfail_test(cx, props, testfile); }
      mode_run_fail. { run_rfail_test(cx, props, testfile); }
      mode_run_pass. { run_rpass_test(cx, props, testfile); }
      mode_pretty. { run_pretty_test(cx, props, testfile); }
    }
}

fn run_cfail_test(cx: cx, props: test_props, testfile: str) {
    let procres = compile_test(cx, props, testfile);

    if procres.status == 0 {
        fatal_procres("compile-fail test compiled successfully!", procres);
    }

    check_correct_failure_status(procres);
    check_error_patterns(props, testfile, procres);
}

fn run_rfail_test(cx: cx, props: test_props, testfile: str) {
    let procres = compile_test(cx, props, testfile);

    if procres.status != 0 { fatal_procres("compilation failed!", procres); }

    procres = exec_compiled_test(cx, props, testfile);

    // The value our Makefile configures valgrind to return on failure
    const valgrind_err: int = 100;
    if procres.status == valgrind_err {
        fatal_procres("run-fail test isn't valgrind-clean!", procres);
    }

    check_correct_failure_status(procres);
    check_error_patterns(props, testfile, procres);
}

fn check_correct_failure_status(procres: procres) {
    // The value the rust runtime returns on failure
    const rust_err: int = 101;
    if procres.status != rust_err {
        fatal_procres(
            #fmt("failure produced the wrong error code: %d",
                 procres.status),
            procres);
    }
}

fn run_rpass_test(cx: cx, props: test_props, testfile: str) {
    let procres = compile_test(cx, props, testfile);

    if procres.status != 0 { fatal_procres("compilation failed!", procres); }

    procres = exec_compiled_test(cx, props, testfile);

    if procres.status != 0 { fatal_procres("test run failed!", procres); }
}

fn run_pretty_test(cx: cx, props: test_props, testfile: str) {
    if option::is_some(props.pp_exact) {
        logv(cx.config, "testing for exact pretty-printing");
    } else { logv(cx.config, "testing for converging pretty-printing"); }

    let rounds =
        alt props.pp_exact { option::some(_) { 1 } option::none. { 2 } };

    let srcs = [result::get(io::read_whole_file_str(testfile))];

    let round = 0;
    while round < rounds {
        logv(cx.config, #fmt["pretty-printing round %d", round]);
        let procres = print_source(cx, testfile, srcs[round]);

        if procres.status != 0 {
            fatal_procres(#fmt["pretty-printing failed in round %d", round],
                          procres);
        }

        srcs += [procres.stdout];
        round += 1;
    }

    let expected =
        alt props.pp_exact {
          option::some(file) {
            let filepath = fs::connect(fs::dirname(testfile), file);
            result::get(io::read_whole_file_str(filepath))
          }
          option::none. { srcs[vec::len(srcs) - 2u] }
        };
    let actual = srcs[vec::len(srcs) - 1u];

    if option::is_some(props.pp_exact) {
        // Now we have to care about line endings
        let cr = "\r";
        check (str::is_not_empty(cr));
        actual = str::replace(actual, cr, "");
        expected = str::replace(expected, cr, "");
    }

    compare_source(expected, actual);

    // Finally, let's make sure it actually appears to remain valid code
    let procres = typecheck_source(cx, testfile, actual);

    if procres.status != 0 {
        fatal_procres("pretty-printed source does not typecheck", procres);
    }

    ret;

    fn print_source(cx: cx, testfile: str, src: str) -> procres {
        compose_and_run(cx, testfile, make_pp_args,
                        cx.config.compile_lib_path, option::some(src))
    }

    fn make_pp_args(config: config, _testfile: str) -> procargs {
        let prog = config.rustc_path;
        let args = ["-", "--pretty", "normal"];
        ret {prog: prog, args: args};
    }

    fn compare_source(expected: str, actual: str) {
        if expected != actual {
            error("pretty-printed source does match expected source");
            let msg =
                #fmt["\n\
expected:\n\
------------------------------------------\n\
%s\n\
------------------------------------------\n\
actual:\n\
------------------------------------------\n\
%s\n\
------------------------------------------\n\
\n",
                     expected, actual];
            io::stdout().write_str(msg);
            fail;
        }
    }

    fn typecheck_source(cx: cx, testfile: str, src: str) -> procres {
        compose_and_run(cx, testfile, make_typecheck_args,
                        cx.config.compile_lib_path, option::some(src))
    }

    fn make_typecheck_args(config: config, _testfile: str) -> procargs {
        let prog = config.rustc_path;
        let args = ["-", "--no-trans", "--lib"];
        ret {prog: prog, args: args};
    }
}

fn check_error_patterns(props: test_props, testfile: str, procres: procres) {
    if vec::is_empty(props.error_patterns) {
        fatal("no error pattern specified in " + testfile);
    }

    if procres.status == 0 {
        fatal("process did not return an error status");
    }

    let next_err_idx = 0u;
    let next_err_pat = props.error_patterns[next_err_idx];
    for line: str in str::split(procres.stdout, '\n' as u8) {
        if str::find(line, next_err_pat) > 0 {
            log #fmt["found error pattern %s", next_err_pat];
            next_err_idx += 1u;
            if next_err_idx == vec::len(props.error_patterns) {
                log "found all error patterns";
                ret;
            }
            next_err_pat = props.error_patterns[next_err_idx];
        }
    }

    let missing_patterns =
        vec::slice(props.error_patterns, next_err_idx,
                   vec::len(props.error_patterns));
    if vec::len(missing_patterns) == 1u {
        fatal_procres(#fmt["error pattern '%s' not found!",
                           missing_patterns[0]], procres);
    } else {
        for pattern: str in missing_patterns {
            error(#fmt["error pattern '%s' not found!", pattern]);
        }
        fatal_procres("multiple error patterns not found", procres);
    }
}

type procargs = {prog: str, args: [str]};

type procres = {status: int, stdout: str, stderr: str, cmdline: str};

fn compile_test(cx: cx, props: test_props, testfile: str) -> procres {
    compose_and_run(cx, testfile, bind make_compile_args(_, props, _),
                    cx.config.compile_lib_path, option::none)
}

fn exec_compiled_test(cx: cx, props: test_props, testfile: str) -> procres {
    compose_and_run(cx, testfile, bind make_run_args(_, props, _),
                    cx.config.run_lib_path, option::none)
}

fn compose_and_run(cx: cx, testfile: str,
                   make_args: fn@(config, str) -> procargs, lib_path: str,
                   input: option::t<str>) -> procres {
    let procargs = make_args(cx.config, testfile);
    ret program_output(cx, testfile, lib_path, procargs.prog, procargs.args,
                       input);
}

fn make_compile_args(config: config, props: test_props, testfile: str) ->
   procargs {
    let prog = config.rustc_path;
    let args = [testfile, "-o", make_exe_name(config, testfile)];
    let rustcflags =
        alt config.rustcflags {
          option::some(s) { option::some(s) }
          option::none. { option::none }
        };
    args += split_maybe_args(rustcflags);
    args += split_maybe_args(props.compile_flags);
    ret {prog: prog, args: args};
}

fn make_exe_name(config: config, testfile: str) -> str {
    output_base_name(config, testfile) + os::exec_suffix()
}

fn make_run_args(config: config, _props: test_props, testfile: str) ->
   procargs {
    let toolargs = {
            // If we've got another tool to run under (valgrind),
            // then split apart its command
            let runtool =
                alt config.runtool {
                  option::some(s) { option::some(s) }
                  option::none. { option::none }
                };
            split_maybe_args(runtool)
        };

    let args = toolargs + [make_exe_name(config, testfile)];
    ret {prog: args[0], args: vec::slice(args, 1u, vec::len(args))};
}

fn split_maybe_args(argstr: option::t<str>) -> [str] {
    fn rm_whitespace(v: [str]) -> [str] {
        fn flt(&&s: str) -> option::t<str> {
            if !is_whitespace(s) { option::some(s) } else { option::none }
        }

        // FIXME: This should be in std
        fn is_whitespace(s: str) -> bool {
            for c: u8 in s { if c != ' ' as u8 { ret false; } }
            ret true;
        }
        vec::filter_map(flt, v)
    }

    alt argstr {
      option::some(s) { rm_whitespace(str::split(s, ' ' as u8)) }
      option::none. { [] }
    }
}

fn program_output(cx: cx, testfile: str, lib_path: str, prog: str,
                  args: [str], input: option::t<str>) -> procres {
    let cmdline =
        {
            let cmdline = make_cmdline(lib_path, prog, args);
            logv(cx.config, #fmt["executing %s", cmdline]);
            cmdline
        };
    let res = procsrv::run(cx.procsrv, lib_path, prog, args, input);
    dump_output(cx.config, testfile, res.out, res.err);
    ret {status: res.status,
         stdout: res.out,
         stderr: res.err,
         cmdline: cmdline};
}

// Linux and mac don't require adjusting the library search path
#[cfg(target_os = "linux")]
#[cfg(target_os = "macos")]
fn make_cmdline(_libpath: str, prog: str, args: [str]) -> str {
    #fmt["%s %s", prog, str::connect(args, " ")]
}

#[cfg(target_os = "win32")]
fn make_cmdline(libpath: str, prog: str, args: [str]) -> str {
    #fmt["%s %s %s", lib_path_cmd_prefix(libpath), prog,
         str::connect(args, " ")]
}

// Build the LD_LIBRARY_PATH variable as it would be seen on the command line
// for diagnostic purposes
fn lib_path_cmd_prefix(path: str) -> str {
    #fmt["%s=\"%s\"", util::lib_path_env_var(), util::make_new_path(path)]
}

fn dump_output(config: config, testfile: str, out: str, err: str) {
    dump_output_file(config, testfile, out, "out");
    dump_output_file(config, testfile, err, "err");
    maybe_dump_to_stdout(config, out, err);
}

fn dump_output_file(config: config, testfile: str, out: str, extension: str) {
    let outfile = make_out_name(config, testfile, extension);
    let writer = result::get(
        io::file_writer(outfile, [io::create, io::truncate]));
    writer.write_str(out);
}

fn make_out_name(config: config, testfile: str, extension: str) -> str {
    output_base_name(config, testfile) + "." + extension
}

fn output_base_name(config: config, testfile: str) -> str {
    let base = config.build_base;
    let filename =
        {
            let parts = str::split(fs::basename(testfile), '.' as u8);
            parts = vec::slice(parts, 0u, vec::len(parts) - 1u);
            str::connect(parts, ".")
        };
    #fmt["%s%s.%s", base, filename, config.stage_id]
}

fn maybe_dump_to_stdout(config: config, out: str, err: str) {
    if config.verbose {
        let sep1 = #fmt["------%s------------------------------", "stdout"];
        let sep2 = #fmt["------%s------------------------------", "stderr"];
        let sep3 = "------------------------------------------";
        io::stdout().write_line(sep1);
        io::stdout().write_line(out);
        io::stdout().write_line(sep2);
        io::stdout().write_line(err);
        io::stdout().write_line(sep3);
    }
}

fn error(err: str) { io::stdout().write_line(#fmt["\nerror: %s", err]); }

fn fatal(err: str) -> ! { error(err); fail; }

fn fatal_procres(err: str, procres: procres) -> ! {
    let msg =
        #fmt["\n\
error: %s\n\
command: %s\n\
stdout:\n\
------------------------------------------\n\
%s\n\
------------------------------------------\n\
stderr:\n\
------------------------------------------\n\
%s\n\
------------------------------------------\n\
\n",
             err, procres.cmdline, procres.stdout, procres.stderr];
    io::stdout().write_str(msg);
    fail;
}
<commit_msg>runtest.rs: Fix typo in error message<commit_after>import std::io;
import std::str;
import std::option;
import std::fs;
import std::os;
import std::vec;
import std::result;

import common::mode_run_pass;
import common::mode_run_fail;
import common::mode_compile_fail;
import common::mode_pretty;
import common::cx;
import common::config;
import header::load_props;
import header::test_props;
import util::logv;

export run;

fn run(cx: cx, -_testfile: [u8]) {
    let testfile = str::unsafe_from_bytes(_testfile);
    if cx.config.verbose {
        // We're going to be dumping a lot of info. Start on a new line.
        io::stdout().write_str("\n\n");
    }
    log #fmt["running %s", testfile];
    let props = load_props(testfile);
    alt cx.config.mode {
      mode_compile_fail. { run_cfail_test(cx, props, testfile); }
      mode_run_fail. { run_rfail_test(cx, props, testfile); }
      mode_run_pass. { run_rpass_test(cx, props, testfile); }
      mode_pretty. { run_pretty_test(cx, props, testfile); }
    }
}

fn run_cfail_test(cx: cx, props: test_props, testfile: str) {
    let procres = compile_test(cx, props, testfile);

    if procres.status == 0 {
        fatal_procres("compile-fail test compiled successfully!", procres);
    }

    check_correct_failure_status(procres);
    check_error_patterns(props, testfile, procres);
}

fn run_rfail_test(cx: cx, props: test_props, testfile: str) {
    let procres = compile_test(cx, props, testfile);

    if procres.status != 0 { fatal_procres("compilation failed!", procres); }

    procres = exec_compiled_test(cx, props, testfile);

    // The value our Makefile configures valgrind to return on failure
    const valgrind_err: int = 100;
    if procres.status == valgrind_err {
        fatal_procres("run-fail test isn't valgrind-clean!", procres);
    }

    check_correct_failure_status(procres);
    check_error_patterns(props, testfile, procres);
}

fn check_correct_failure_status(procres: procres) {
    // The value the rust runtime returns on failure
    const rust_err: int = 101;
    if procres.status != rust_err {
        fatal_procres(
            #fmt("failure produced the wrong error code: %d",
                 procres.status),
            procres);
    }
}

fn run_rpass_test(cx: cx, props: test_props, testfile: str) {
    let procres = compile_test(cx, props, testfile);

    if procres.status != 0 { fatal_procres("compilation failed!", procres); }

    procres = exec_compiled_test(cx, props, testfile);

    if procres.status != 0 { fatal_procres("test run failed!", procres); }
}

fn run_pretty_test(cx: cx, props: test_props, testfile: str) {
    if option::is_some(props.pp_exact) {
        logv(cx.config, "testing for exact pretty-printing");
    } else { logv(cx.config, "testing for converging pretty-printing"); }

    let rounds =
        alt props.pp_exact { option::some(_) { 1 } option::none. { 2 } };

    let srcs = [result::get(io::read_whole_file_str(testfile))];

    let round = 0;
    while round < rounds {
        logv(cx.config, #fmt["pretty-printing round %d", round]);
        let procres = print_source(cx, testfile, srcs[round]);

        if procres.status != 0 {
            fatal_procres(#fmt["pretty-printing failed in round %d", round],
                          procres);
        }

        srcs += [procres.stdout];
        round += 1;
    }

    let expected =
        alt props.pp_exact {
          option::some(file) {
            let filepath = fs::connect(fs::dirname(testfile), file);
            result::get(io::read_whole_file_str(filepath))
          }
          option::none. { srcs[vec::len(srcs) - 2u] }
        };
    let actual = srcs[vec::len(srcs) - 1u];

    if option::is_some(props.pp_exact) {
        // Now we have to care about line endings
        let cr = "\r";
        check (str::is_not_empty(cr));
        actual = str::replace(actual, cr, "");
        expected = str::replace(expected, cr, "");
    }

    compare_source(expected, actual);

    // Finally, let's make sure it actually appears to remain valid code
    let procres = typecheck_source(cx, testfile, actual);

    if procres.status != 0 {
        fatal_procres("pretty-printed source does not typecheck", procres);
    }

    ret;

    fn print_source(cx: cx, testfile: str, src: str) -> procres {
        compose_and_run(cx, testfile, make_pp_args,
                        cx.config.compile_lib_path, option::some(src))
    }

    fn make_pp_args(config: config, _testfile: str) -> procargs {
        let prog = config.rustc_path;
        let args = ["-", "--pretty", "normal"];
        ret {prog: prog, args: args};
    }

    fn compare_source(expected: str, actual: str) {
        if expected != actual {
            error("pretty-printed source does not match expected source");
            let msg =
                #fmt["\n\
expected:\n\
------------------------------------------\n\
%s\n\
------------------------------------------\n\
actual:\n\
------------------------------------------\n\
%s\n\
------------------------------------------\n\
\n",
                     expected, actual];
            io::stdout().write_str(msg);
            fail;
        }
    }

    fn typecheck_source(cx: cx, testfile: str, src: str) -> procres {
        compose_and_run(cx, testfile, make_typecheck_args,
                        cx.config.compile_lib_path, option::some(src))
    }

    fn make_typecheck_args(config: config, _testfile: str) -> procargs {
        let prog = config.rustc_path;
        let args = ["-", "--no-trans", "--lib"];
        ret {prog: prog, args: args};
    }
}

fn check_error_patterns(props: test_props, testfile: str, procres: procres) {
    if vec::is_empty(props.error_patterns) {
        fatal("no error pattern specified in " + testfile);
    }

    if procres.status == 0 {
        fatal("process did not return an error status");
    }

    let next_err_idx = 0u;
    let next_err_pat = props.error_patterns[next_err_idx];
    for line: str in str::split(procres.stdout, '\n' as u8) {
        if str::find(line, next_err_pat) > 0 {
            log #fmt["found error pattern %s", next_err_pat];
            next_err_idx += 1u;
            if next_err_idx == vec::len(props.error_patterns) {
                log "found all error patterns";
                ret;
            }
            next_err_pat = props.error_patterns[next_err_idx];
        }
    }

    let missing_patterns =
        vec::slice(props.error_patterns, next_err_idx,
                   vec::len(props.error_patterns));
    if vec::len(missing_patterns) == 1u {
        fatal_procres(#fmt["error pattern '%s' not found!",
                           missing_patterns[0]], procres);
    } else {
        for pattern: str in missing_patterns {
            error(#fmt["error pattern '%s' not found!", pattern]);
        }
        fatal_procres("multiple error patterns not found", procres);
    }
}

type procargs = {prog: str, args: [str]};

type procres = {status: int, stdout: str, stderr: str, cmdline: str};

fn compile_test(cx: cx, props: test_props, testfile: str) -> procres {
    compose_and_run(cx, testfile, bind make_compile_args(_, props, _),
                    cx.config.compile_lib_path, option::none)
}

fn exec_compiled_test(cx: cx, props: test_props, testfile: str) -> procres {
    compose_and_run(cx, testfile, bind make_run_args(_, props, _),
                    cx.config.run_lib_path, option::none)
}

fn compose_and_run(cx: cx, testfile: str,
                   make_args: fn@(config, str) -> procargs, lib_path: str,
                   input: option::t<str>) -> procres {
    let procargs = make_args(cx.config, testfile);
    ret program_output(cx, testfile, lib_path, procargs.prog, procargs.args,
                       input);
}

fn make_compile_args(config: config, props: test_props, testfile: str) ->
   procargs {
    let prog = config.rustc_path;
    let args = [testfile, "-o", make_exe_name(config, testfile)];
    let rustcflags =
        alt config.rustcflags {
          option::some(s) { option::some(s) }
          option::none. { option::none }
        };
    args += split_maybe_args(rustcflags);
    args += split_maybe_args(props.compile_flags);
    ret {prog: prog, args: args};
}

fn make_exe_name(config: config, testfile: str) -> str {
    output_base_name(config, testfile) + os::exec_suffix()
}

fn make_run_args(config: config, _props: test_props, testfile: str) ->
   procargs {
    let toolargs = {
            // If we've got another tool to run under (valgrind),
            // then split apart its command
            let runtool =
                alt config.runtool {
                  option::some(s) { option::some(s) }
                  option::none. { option::none }
                };
            split_maybe_args(runtool)
        };

    let args = toolargs + [make_exe_name(config, testfile)];
    ret {prog: args[0], args: vec::slice(args, 1u, vec::len(args))};
}

fn split_maybe_args(argstr: option::t<str>) -> [str] {
    fn rm_whitespace(v: [str]) -> [str] {
        fn flt(&&s: str) -> option::t<str> {
            if !is_whitespace(s) { option::some(s) } else { option::none }
        }

        // FIXME: This should be in std
        fn is_whitespace(s: str) -> bool {
            for c: u8 in s { if c != ' ' as u8 { ret false; } }
            ret true;
        }
        vec::filter_map(flt, v)
    }

    alt argstr {
      option::some(s) { rm_whitespace(str::split(s, ' ' as u8)) }
      option::none. { [] }
    }
}

fn program_output(cx: cx, testfile: str, lib_path: str, prog: str,
                  args: [str], input: option::t<str>) -> procres {
    let cmdline =
        {
            let cmdline = make_cmdline(lib_path, prog, args);
            logv(cx.config, #fmt["executing %s", cmdline]);
            cmdline
        };
    let res = procsrv::run(cx.procsrv, lib_path, prog, args, input);
    dump_output(cx.config, testfile, res.out, res.err);
    ret {status: res.status,
         stdout: res.out,
         stderr: res.err,
         cmdline: cmdline};
}

// Linux and mac don't require adjusting the library search path
#[cfg(target_os = "linux")]
#[cfg(target_os = "macos")]
fn make_cmdline(_libpath: str, prog: str, args: [str]) -> str {
    #fmt["%s %s", prog, str::connect(args, " ")]
}

#[cfg(target_os = "win32")]
fn make_cmdline(libpath: str, prog: str, args: [str]) -> str {
    #fmt["%s %s %s", lib_path_cmd_prefix(libpath), prog,
         str::connect(args, " ")]
}

// Build the LD_LIBRARY_PATH variable as it would be seen on the command line
// for diagnostic purposes
fn lib_path_cmd_prefix(path: str) -> str {
    #fmt["%s=\"%s\"", util::lib_path_env_var(), util::make_new_path(path)]
}

fn dump_output(config: config, testfile: str, out: str, err: str) {
    dump_output_file(config, testfile, out, "out");
    dump_output_file(config, testfile, err, "err");
    maybe_dump_to_stdout(config, out, err);
}

fn dump_output_file(config: config, testfile: str, out: str, extension: str) {
    let outfile = make_out_name(config, testfile, extension);
    let writer = result::get(
        io::file_writer(outfile, [io::create, io::truncate]));
    writer.write_str(out);
}

fn make_out_name(config: config, testfile: str, extension: str) -> str {
    output_base_name(config, testfile) + "." + extension
}

fn output_base_name(config: config, testfile: str) -> str {
    let base = config.build_base;
    let filename =
        {
            let parts = str::split(fs::basename(testfile), '.' as u8);
            parts = vec::slice(parts, 0u, vec::len(parts) - 1u);
            str::connect(parts, ".")
        };
    #fmt["%s%s.%s", base, filename, config.stage_id]
}

fn maybe_dump_to_stdout(config: config, out: str, err: str) {
    if config.verbose {
        let sep1 = #fmt["------%s------------------------------", "stdout"];
        let sep2 = #fmt["------%s------------------------------", "stderr"];
        let sep3 = "------------------------------------------";
        io::stdout().write_line(sep1);
        io::stdout().write_line(out);
        io::stdout().write_line(sep2);
        io::stdout().write_line(err);
        io::stdout().write_line(sep3);
    }
}

fn error(err: str) { io::stdout().write_line(#fmt["\nerror: %s", err]); }

fn fatal(err: str) -> ! { error(err); fail; }

fn fatal_procres(err: str, procres: procres) -> ! {
    let msg =
        #fmt["\n\
error: %s\n\
command: %s\n\
stdout:\n\
------------------------------------------\n\
%s\n\
------------------------------------------\n\
stderr:\n\
------------------------------------------\n\
%s\n\
------------------------------------------\n\
\n",
             err, procres.cmdline, procres.stdout, procres.stderr];
    io::stdout().write_str(msg);
    fail;
}
<|endoftext|>


<commit_before>//! Physics related constants

// taken from: https://en.wikipedia.org/wiki/Physical_constant

pub const planck_length: f64 = 1.61619997e-35; // [m]
pub const planck_mass: f64 = 2.1765113e-8; // [kg]
pub const planck_time: f64 = 5.3910632e-44; // [s]
pub const planck_charge: f64 = 1.87554595641e-18; // [C]
pub const planck_temperature: f64 = 1.41683385e32; // [K]

pub const speed_of_light_vac: f64 = 299792458.0; // [m/s]

// Electromagnetic constants
pub const magnetic_const_vac: f64 = 1.256637061e-6; // [N*A^(-2)]
pub const electric_const_vac: f64 = 8.854187817e-12; // [F*m^(-1)]
pub const coulombs_const_vac: f64 = 8.9875517873681764e9; // [N*m²*C^(-2)]
pub const elementary_charge: f64 = 1.602 176 620898e-19; // [C]
pub const bohr_magneton: f64 = 9.27400999457e-24; // [J*T^(-1)]
pub const conductance_quantum: f64 = 7.748091731018e-5; // [S]
pub const inverse_conductance_quantum: f64 = 12906.403727829; // [Ohm]
pub const josephson_const: f64 = 4.83597852530e14; // [Hz * V^(-1)]
pub const magnetic_flux_quantum: f64 = 2.06783383113e-15; // [Wb]
pub const nuclear_magneton: f64 = 5.050 783 69931e-27; // [J*T^(-1)]
pub const von_klitzing_const: f64 = 25812.807455559; // [Ohm]

// Atomic and nuclear constants
pub const bohr_radius: f64 = 5.291772106712e-11; // [m]
pub const classical_electron_radius: f64 = 2.817940322719e-15; // [m]
pub const electron_mass: f64 = 9.1093835611e-31; // [kg]
pub const fermi_coupling_const: f64 = 1.16637876e-5; // [GeV^(−2)]
pub const fine_structure_const: f64 = 7.297352566417e-3	; // []
pub const hartree_energy: f64 = 4.35974465054e-18; // [J]
pub const proton_mass: f64 = 1.67262189821e-27; // [Kg]
pub const quantum_of_circulation: f64 = 3.636947548617e-4; // [m² * s^(-1)]
pub const rydberg_const: f64 = 10973731.56850865; // [m^(-1)]
pub const thompson_cross_section: f64 = 6.652458715891e-29; // [m²]
pub const weak_mixing_angle: f64 = 0.222321; // []
pub const efimov_factor: f64 = 22.7; // []
<commit_msg>Fix constant<commit_after>//! Physics related constants

// taken from: https://en.wikipedia.org/wiki/Physical_constant

pub const planck_length: f64 = 1.61619997e-35; // [m]
pub const planck_mass: f64 = 2.1765113e-8; // [kg]
pub const planck_time: f64 = 5.3910632e-44; // [s]
pub const planck_charge: f64 = 1.87554595641e-18; // [C]
pub const planck_temperature: f64 = 1.41683385e32; // [K]

pub const speed_of_light_vac: f64 = 299792458.0; // [m/s]

// Electromagnetic constants
pub const magnetic_const_vac: f64 = 1.256637061e-6; // [N*A^(-2)]
pub const electric_const_vac: f64 = 8.854187817e-12; // [F*m^(-1)]
pub const coulombs_const_vac: f64 = 8.9875517873681764e9; // [N*m²*C^(-2)]
pub const elementary_charge: f64 = 1.602176620898e-19; // [C]
pub const bohr_magneton: f64 = 9.27400999457e-24; // [J*T^(-1)]
pub const conductance_quantum: f64 = 7.748091731018e-5; // [S]
pub const inverse_conductance_quantum: f64 = 12906.403727829; // [Ohm]
pub const josephson_const: f64 = 4.83597852530e14; // [Hz * V^(-1)]
pub const magnetic_flux_quantum: f64 = 2.06783383113e-15; // [Wb]
pub const nuclear_magneton: f64 = 5.050 783 69931e-27; // [J*T^(-1)]
pub const von_klitzing_const: f64 = 25812.807455559; // [Ohm]

// Atomic and nuclear constants
pub const bohr_radius: f64 = 5.291772106712e-11; // [m]
pub const classical_electron_radius: f64 = 2.817940322719e-15; // [m]
pub const electron_mass: f64 = 9.1093835611e-31; // [kg]
pub const fermi_coupling_const: f64 = 1.16637876e-5; // [GeV^(−2)]
pub const fine_structure_const: f64 = 7.297352566417e-3	; // []
pub const hartree_energy: f64 = 4.35974465054e-18; // [J]
pub const proton_mass: f64 = 1.67262189821e-27; // [Kg]
pub const quantum_of_circulation: f64 = 3.636947548617e-4; // [m² * s^(-1)]
pub const rydberg_const: f64 = 10973731.56850865; // [m^(-1)]
pub const thompson_cross_section: f64 = 6.652458715891e-29; // [m²]
pub const weak_mixing_angle: f64 = 0.222321; // []
pub const efimov_factor: f64 = 22.7; // []
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::fs::File;
use std::path::{Path, PathBuf};

use crate::prelude::*;

use rustc_codegen_ssa::{METADATA_FILENAME, RLIB_BYTECODE_EXTENSION};
use rustc_codegen_ssa::back::archive::{ArchiveBuilder, find_library};

struct ArchiveConfig<'a> {
    sess: &'a Session,
    dst: PathBuf,
    src: Option<PathBuf>,
    lib_search_paths: Vec<PathBuf>,
    is_like_osx: bool,
}

#[derive(Debug)]
enum ArchiveEntry {
    FromArchive { archive_index: usize, entry_index: usize },
    File(File),
}

pub struct ArArchiveBuilder<'a> {
    config: ArchiveConfig<'a>,
    src_archives: Vec<ar::Archive<File>>,
    entries: HashMap<String, ArchiveEntry>,
    update_symbols: bool,
}

impl<'a> ArchiveBuilder<'a> for ArArchiveBuilder<'a> {
    fn new(sess: &'a Session, output: &Path, input: Option<&Path>) -> Self {
        use rustc_codegen_ssa::back::link::archive_search_paths;
        let config = ArchiveConfig {
            sess,
            dst: output.to_path_buf(),
            src: input.map(|p| p.to_path_buf()),
            lib_search_paths: archive_search_paths(sess),
            is_like_osx: sess.target.target.options.is_like_osx,
        };

        let (src_archives, entries) = if let Some(src) = &config.src {
            let mut archive = ar::Archive::new(File::open(src).unwrap());
            let mut entries = HashMap::new();

            let mut i = 0;
            while let Some(entry) = archive.next_entry() {
                let entry = entry.unwrap();
                entries.insert(
                    String::from_utf8(entry.header().identifier().to_vec()).unwrap(),
                    ArchiveEntry::FromArchive { archive_index: 0, entry_index: i },
                );
                i += 1;
            }

            (vec![archive], entries)
        } else {
            (vec![], HashMap::new())
        };

        ArArchiveBuilder {
            config,
            src_archives,
            entries,
            update_symbols: false,
        }
    }

    fn src_files(&mut self) -> Vec<String> {
        self.entries.keys().cloned().collect()
    }

    fn remove_file(&mut self, name: &str) {
        let file = self.entries.remove(name);
        assert!(
            file.is_some(),
            "Tried to remove file not existing in src archive",
        );
    }

    fn add_file(&mut self, file: &Path) {
        self.entries.insert(
            file.file_name().unwrap().to_str().unwrap().to_string(),
            ArchiveEntry::File(File::open(file).unwrap()),
        );
    }

    fn add_native_library(&mut self, name: &str) {
        let location = find_library(name, &self.config.lib_search_paths, self.config.sess);
        self.add_archive(&location, |_| false).unwrap_or_else(|e| {
            panic!("failed to add native library {}: {}", location.to_string_lossy(), e);
        });
    }

    fn add_rlib(&mut self, rlib: &Path, name: &str, lto: bool, skip_objects: bool) -> std::io::Result<()> {
        let obj_start = name.to_owned();

        self.add_archive(rlib, move |fname: &str| {
            // Ignore bytecode/metadata files, no matter the name.
            if fname.ends_with(RLIB_BYTECODE_EXTENSION) || fname == METADATA_FILENAME {
                return true;
            }

            // Don't include Rust objects if LTO is enabled
            if lto && fname.starts_with(&obj_start) && fname.ends_with(".o") {
                return true;
            }

            // Otherwise if this is *not* a rust object and we're skipping
            // objects then skip this file
            if skip_objects && (!fname.starts_with(&obj_start) || !fname.ends_with(".o")) {
                return true;
            }

            // ok, don't skip this
            return false;
        })
    }

    fn update_symbols(&mut self) {
        self.update_symbols = true;
    }

    fn build(mut self) {
        println!("{:?}", self.src_archives.len());
        println!("{:?}", self.entries);

        enum BuilderKind {
            Bsd(ar::Builder<File>),
            Gnu(ar::GnuBuilder<File>),
        }

        let archive_file = File::create(&self.config.dst).unwrap();
        let mut builder = if self.config.is_like_osx {
            BuilderKind::Bsd(ar::Builder::new(archive_file))
        } else {
            BuilderKind::Gnu(ar::GnuBuilder::new(
                archive_file,
                self.entries.keys().map(|key| key.as_bytes().to_vec()).collect(),
            ))
        };

        // Add all files
        for (entry_name, entry) in self.entries.into_iter() {
            match entry {
                ArchiveEntry::FromArchive { archive_index, entry_index } => {
                    let entry = self.src_archives[archive_index].jump_to_entry(entry_index).unwrap();
                    let orig_header = entry.header();

                    let mut header =
                        ar::Header::new(orig_header.identifier().to_vec(), orig_header.size());
                    header.set_mtime(orig_header.mtime());
                    header.set_uid(orig_header.uid());
                    header.set_gid(orig_header.gid());
                    header.set_mode(orig_header.mode());
                    match builder {
                        BuilderKind::Bsd(ref mut builder) => builder.append(&header, entry).unwrap(),
                        BuilderKind::Gnu(ref mut builder) => builder.append(&header, entry).unwrap(),
                    }
                }
                ArchiveEntry::File(mut file) => {
                    match builder {
                        BuilderKind::Bsd(ref mut builder) => builder.append_file(entry_name.as_bytes(), &mut file).unwrap(),
                        BuilderKind::Gnu(ref mut builder) => builder.append_file(entry_name.as_bytes(), &mut file).unwrap(),
                    }
                }
            }
        }

        // Finalize archive
        std::mem::drop(builder);

        // Run ranlib to be able to link the archive
        let status = std::process::Command::new("ranlib")
            .arg(self.config.dst)
            .status()
            .expect("Couldn't run ranlib");
        assert!(
            status.success(),
            "Ranlib exited with code {:?}",
            status.code()
        );
    }
}

impl<'a> ArArchiveBuilder<'a> {
    fn add_archive<F>(&mut self, archive: &Path, mut skip: F) -> std::io::Result<()>
        where F: FnMut(&str) -> bool + 'static
    {
        let mut archive = ar::Archive::new(std::fs::File::open(archive)?);
        let archive_index = self.src_archives.len();

        let mut i = 0;
        while let Some(entry) = archive.next_entry() {
            let entry = entry.unwrap();
            let file_name = String::from_utf8(entry.header().identifier().to_vec()).unwrap();
            if !skip(&file_name) {
                self.entries.insert(
                    file_name,
                    ArchiveEntry::FromArchive { archive_index, entry_index: i },
                );
            }
            i += 1;
        }

        self.src_archives.push(archive);
        Ok(())
    }
}
<commit_msg>Remove some debug println's<commit_after>use std::collections::HashMap;
use std::fs::File;
use std::path::{Path, PathBuf};

use crate::prelude::*;

use rustc_codegen_ssa::{METADATA_FILENAME, RLIB_BYTECODE_EXTENSION};
use rustc_codegen_ssa::back::archive::{ArchiveBuilder, find_library};

struct ArchiveConfig<'a> {
    sess: &'a Session,
    dst: PathBuf,
    src: Option<PathBuf>,
    lib_search_paths: Vec<PathBuf>,
    is_like_osx: bool,
}

#[derive(Debug)]
enum ArchiveEntry {
    FromArchive { archive_index: usize, entry_index: usize },
    File(File),
}

pub struct ArArchiveBuilder<'a> {
    config: ArchiveConfig<'a>,
    src_archives: Vec<ar::Archive<File>>,
    entries: HashMap<String, ArchiveEntry>,
    update_symbols: bool,
}

impl<'a> ArchiveBuilder<'a> for ArArchiveBuilder<'a> {
    fn new(sess: &'a Session, output: &Path, input: Option<&Path>) -> Self {
        use rustc_codegen_ssa::back::link::archive_search_paths;
        let config = ArchiveConfig {
            sess,
            dst: output.to_path_buf(),
            src: input.map(|p| p.to_path_buf()),
            lib_search_paths: archive_search_paths(sess),
            is_like_osx: sess.target.target.options.is_like_osx, // FIXME should probably check for linux
        };

        let (src_archives, entries) = if let Some(src) = &config.src {
            let mut archive = ar::Archive::new(File::open(src).unwrap());
            let mut entries = HashMap::new();

            let mut i = 0;
            while let Some(entry) = archive.next_entry() {
                let entry = entry.unwrap();
                entries.insert(
                    String::from_utf8(entry.header().identifier().to_vec()).unwrap(),
                    ArchiveEntry::FromArchive { archive_index: 0, entry_index: i },
                );
                i += 1;
            }

            (vec![archive], entries)
        } else {
            (vec![], HashMap::new())
        };

        ArArchiveBuilder {
            config,
            src_archives,
            entries,
            update_symbols: false,
        }
    }

    fn src_files(&mut self) -> Vec<String> {
        self.entries.keys().cloned().collect()
    }

    fn remove_file(&mut self, name: &str) {
        let file = self.entries.remove(name);
        assert!(
            file.is_some(),
            "Tried to remove file not existing in src archive",
        );
    }

    fn add_file(&mut self, file: &Path) {
        self.entries.insert(
            file.file_name().unwrap().to_str().unwrap().to_string(),
            ArchiveEntry::File(File::open(file).unwrap()),
        );
    }

    fn add_native_library(&mut self, name: &str) {
        let location = find_library(name, &self.config.lib_search_paths, self.config.sess);
        self.add_archive(&location, |_| false).unwrap_or_else(|e| {
            panic!("failed to add native library {}: {}", location.to_string_lossy(), e);
        });
    }

    fn add_rlib(&mut self, rlib: &Path, name: &str, lto: bool, skip_objects: bool) -> std::io::Result<()> {
        let obj_start = name.to_owned();

        self.add_archive(rlib, move |fname: &str| {
            // Ignore bytecode/metadata files, no matter the name.
            if fname.ends_with(RLIB_BYTECODE_EXTENSION) || fname == METADATA_FILENAME {
                return true;
            }

            // Don't include Rust objects if LTO is enabled
            if lto && fname.starts_with(&obj_start) && fname.ends_with(".o") {
                return true;
            }

            // Otherwise if this is *not* a rust object and we're skipping
            // objects then skip this file
            if skip_objects && (!fname.starts_with(&obj_start) || !fname.ends_with(".o")) {
                return true;
            }

            // ok, don't skip this
            return false;
        })
    }

    fn update_symbols(&mut self) {
        self.update_symbols = true;
    }

    fn build(mut self) {
        enum BuilderKind {
            Bsd(ar::Builder<File>),
            Gnu(ar::GnuBuilder<File>),
        }

        let archive_file = File::create(&self.config.dst).unwrap();
        let mut builder = if self.config.is_like_osx {
            BuilderKind::Bsd(ar::Builder::new(archive_file))
        } else {
            BuilderKind::Gnu(ar::GnuBuilder::new(
                archive_file,
                self.entries.keys().map(|key| key.as_bytes().to_vec()).collect(),
            ))
        };

        // Add all files
        for (entry_name, entry) in self.entries.into_iter() {
            match entry {
                ArchiveEntry::FromArchive { archive_index, entry_index } => {
                    let entry = self.src_archives[archive_index].jump_to_entry(entry_index).unwrap();
                    let orig_header = entry.header();

                    let mut header =
                        ar::Header::new(orig_header.identifier().to_vec(), orig_header.size());
                    header.set_mtime(orig_header.mtime());
                    header.set_uid(orig_header.uid());
                    header.set_gid(orig_header.gid());
                    header.set_mode(orig_header.mode());
                    match builder {
                        BuilderKind::Bsd(ref mut builder) => builder.append(&header, entry).unwrap(),
                        BuilderKind::Gnu(ref mut builder) => builder.append(&header, entry).unwrap(),
                    }
                }
                ArchiveEntry::File(mut file) => {
                    match builder {
                        BuilderKind::Bsd(ref mut builder) => builder.append_file(entry_name.as_bytes(), &mut file).unwrap(),
                        BuilderKind::Gnu(ref mut builder) => builder.append_file(entry_name.as_bytes(), &mut file).unwrap(),
                    }
                }
            }
        }

        // Finalize archive
        std::mem::drop(builder);

        // Run ranlib to be able to link the archive
        let status = std::process::Command::new("ranlib")
            .arg(self.config.dst)
            .status()
            .expect("Couldn't run ranlib");
        assert!(
            status.success(),
            "Ranlib exited with code {:?}",
            status.code()
        );
    }
}

impl<'a> ArArchiveBuilder<'a> {
    fn add_archive<F>(&mut self, archive: &Path, mut skip: F) -> std::io::Result<()>
        where F: FnMut(&str) -> bool + 'static
    {
        let mut archive = ar::Archive::new(std::fs::File::open(archive)?);
        let archive_index = self.src_archives.len();

        let mut i = 0;
        while let Some(entry) = archive.next_entry() {
            let entry = entry.unwrap();
            let file_name = String::from_utf8(entry.header().identifier().to_vec()).unwrap();
            if !skip(&file_name) {
                self.entries.insert(
                    file_name,
                    ArchiveEntry::FromArchive { archive_index, entry_index: i },
                );
            }
            i += 1;
        }

        self.src_archives.push(archive);
        Ok(())
    }
}
<|endoftext|>